Compare commits
2 commits
b0fce4d80a
...
4909b8b21d
Author | SHA1 | Date | |
---|---|---|---|
4909b8b21d | |||
c2d7a4beec |
4 changed files with 258 additions and 0 deletions
|
@ -10,3 +10,7 @@ path = "day11/part2.rs"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "d12p1"
|
name = "d12p1"
|
||||||
path = "day12/part1.rs"
|
path = "day12/part1.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "d12p2"
|
||||||
|
path = "day12/part2.rs"
|
||||||
|
|
140
day12/part2.rs
Normal file
140
day12/part2.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#![feature(let_chains)]
|
||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
|
const GRID_SIZE: usize = include_bytes!("input.txt").len().isqrt();
|
||||||
|
|
||||||
|
fn dfs(grid: &[[char; GRID_SIZE]; GRID_SIZE], start: (usize, usize)) -> HashSet<(usize, usize)> {
|
||||||
|
let mut queue = VecDeque::from([start]);
|
||||||
|
let mut set = HashSet::from([start]);
|
||||||
|
let plant_type = grid[start.0][start.1];
|
||||||
|
|
||||||
|
while let Some(next) = queue.pop_front() {
|
||||||
|
let to_check = [
|
||||||
|
(next.0 + 1, next.1),
|
||||||
|
(next.0.wrapping_sub(1), next.1),
|
||||||
|
(next.0, next.1 + 1),
|
||||||
|
(next.0, next.1.wrapping_sub(1)),
|
||||||
|
];
|
||||||
|
for pos in to_check {
|
||||||
|
if set.contains(&pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(plant) = grid.get(pos.0).and_then(|r| r.get(pos.1))
|
||||||
|
&& *plant == plant_type
|
||||||
|
{
|
||||||
|
queue.push_back(pos);
|
||||||
|
set.insert(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn dfs_perimeter(
|
||||||
|
// perimeter_set: &HashSet<(isize, isize)>,
|
||||||
|
// start: (isize, isize),
|
||||||
|
// ) -> HashSet<(isize, isize)> {
|
||||||
|
// let mut queue = VecDeque::from([start]);
|
||||||
|
// let mut set = HashSet::from([start]);
|
||||||
|
|
||||||
|
// while let Some(next) = queue.pop_front() {
|
||||||
|
// let to_check = [
|
||||||
|
// (next.0 + 1, next.1),
|
||||||
|
// (next.0 - 1, next.1),
|
||||||
|
// (next.0, next.1 + 1),
|
||||||
|
// (next.0, next.1 - 1),
|
||||||
|
// ];
|
||||||
|
// for pos in to_check {
|
||||||
|
// if set.contains(&pos) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if perimeter_set.contains(&pos) {
|
||||||
|
// queue.push_back(pos);
|
||||||
|
// set.insert(pos);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// set
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut input_iter = include_str!("input.txt").lines().map(|l| {
|
||||||
|
let mut iter = l.chars();
|
||||||
|
std::array::from_fn::<_, GRID_SIZE, _>(|_| iter.next().unwrap())
|
||||||
|
});
|
||||||
|
let grid: [_; GRID_SIZE] = std::array::from_fn(|_| input_iter.next().unwrap());
|
||||||
|
|
||||||
|
// Parse all independent regions
|
||||||
|
let mut regions = Vec::<(char, HashSet<(usize, usize)>)>::new();
|
||||||
|
for row in 0..GRID_SIZE {
|
||||||
|
for col in 0..GRID_SIZE {
|
||||||
|
if regions.iter().all(|region| !region.1.contains(&(row, col))) {
|
||||||
|
regions.push((grid[row][col], dfs(&grid, (row, col))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check -> are there any duplicated nodes between the regions?
|
||||||
|
assert_eq!(
|
||||||
|
regions.iter().map(|r| r.1.len()).sum::<usize>(),
|
||||||
|
regions
|
||||||
|
.iter()
|
||||||
|
.map(|r| &r.1)
|
||||||
|
.flatten()
|
||||||
|
.collect::<HashSet<&(usize, usize)>>()
|
||||||
|
.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Expand grid by two because im going insane
|
||||||
|
let mut expanded_grid = [[' '; GRID_SIZE * 2 + 1]; GRID_SIZE * 2 + 1];
|
||||||
|
for row in 0..GRID_SIZE {
|
||||||
|
for col in 0..GRID_SIZE {
|
||||||
|
expanded_grid[row * 2 + 1][col * 2 + 1] = grid[row][col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in 0..(GRID_SIZE*2+1) {
|
||||||
|
print!("|");
|
||||||
|
for col in 0..(GRID_SIZE*2+1) {
|
||||||
|
print!("{}", expanded_grid[row][col]);
|
||||||
|
}
|
||||||
|
println!("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate area and sides of each
|
||||||
|
let mut sum = 0;
|
||||||
|
for (plant_type, region) in regions.into_iter() {
|
||||||
|
let area = region.len();
|
||||||
|
let sides = 0;
|
||||||
|
|
||||||
|
// Clone the expanded grid
|
||||||
|
let expanded_grid_clone = expanded_grid.clone();
|
||||||
|
for pos in region.iter() {
|
||||||
|
// None in a position is a special case meaning "overfilled in the negative direction"
|
||||||
|
// Since we don't actually access the point there, it just gets treated like -1 would
|
||||||
|
let to_check = [
|
||||||
|
(pos.0.checked_add(1), Some(pos.1)),
|
||||||
|
(pos.0.checked_sub(1), Some(pos.1)),
|
||||||
|
(Some(pos.0), pos.1.checked_add(1)),
|
||||||
|
(Some(pos.0), pos.1.checked_sub(1)),
|
||||||
|
];
|
||||||
|
for outer_pos in to_check {
|
||||||
|
if (outer_pos.0.is_none() || outer_pos.1.is_none())
|
||||||
|
|| !region.contains(&(outer_pos.0.unwrap(), outer_pos.1.unwrap()))
|
||||||
|
{
|
||||||
|
expanded_grid_clone[outer_pos.0.map(|p| p * 2 + 1)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += area * sides;
|
||||||
|
println!("{plant_type}: {area}A {sides}S");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Result: {sum}");
|
||||||
|
}
|
56
day13/part1.kts
Executable file
56
day13/part1.kts
Executable file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
// I LOVE MATH
|
||||||
|
typealias Matrix2 = Pair<Pair<Int, Int>, Pair<Int, Int>>
|
||||||
|
fun Matrix2.determinant(): Int = (this.first.first * this.second.second) - (this.first.second * this.second.first)
|
||||||
|
|
||||||
|
typealias Point = Pair<Int, Int>
|
||||||
|
|
||||||
|
data class Game(
|
||||||
|
val buttonA: Point,
|
||||||
|
val buttonB: Point,
|
||||||
|
val goal: Point
|
||||||
|
)
|
||||||
|
|
||||||
|
val games = File("input.txt").readLines().chunked(4).map { lines ->
|
||||||
|
Game(
|
||||||
|
buttonA = lines[0].substring(12).split(", Y+").let {
|
||||||
|
it[0].toInt() to it[1].toInt()
|
||||||
|
},
|
||||||
|
buttonB = lines[1].substring(12).split(", Y+").let {
|
||||||
|
it[0].toInt() to it[1].toInt()
|
||||||
|
},
|
||||||
|
goal = lines[2].substring(9).split(", Y=").let {
|
||||||
|
it[0].toInt() to it[1].toInt()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens = 0
|
||||||
|
|
||||||
|
games.forEach { game ->
|
||||||
|
val aPresses = Matrix2(
|
||||||
|
game.goal.first to game.buttonB.first,
|
||||||
|
game.goal.second to game.buttonB.second
|
||||||
|
).determinant().toDouble() / Matrix2(
|
||||||
|
game.buttonA.first to game.buttonB.first,
|
||||||
|
game.buttonA.second to game.buttonB.second
|
||||||
|
).determinant()
|
||||||
|
|
||||||
|
val bPresses = Matrix2(
|
||||||
|
game.buttonA.first to game.goal.first,
|
||||||
|
game.buttonA.second to game.goal.second
|
||||||
|
).determinant().toDouble() / Matrix2(
|
||||||
|
game.buttonA.first to game.buttonB.first,
|
||||||
|
game.buttonA.second to game.buttonB.second
|
||||||
|
).determinant()
|
||||||
|
|
||||||
|
if (aPresses != floor(aPresses) || bPresses != floor(bPresses)) return@forEach
|
||||||
|
|
||||||
|
tokens += aPresses.toInt() * 3 + bPresses.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Result: $tokens")
|
58
day13/part2.kts
Executable file
58
day13/part2.kts
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
// I LOVE MATH
|
||||||
|
typealias Matrix2 = Pair<Pair<Long, Long>, Pair<Long, Long>>
|
||||||
|
fun Matrix2.determinant(): Long = (this.first.first * this.second.second) - (this.first.second * this.second.first)
|
||||||
|
|
||||||
|
typealias Point = Pair<Long, Long>
|
||||||
|
|
||||||
|
data class Game(
|
||||||
|
val buttonA: Point,
|
||||||
|
val buttonB: Point,
|
||||||
|
val goal: Point
|
||||||
|
)
|
||||||
|
|
||||||
|
val games = File("input.txt").readLines().chunked(4).map { lines ->
|
||||||
|
Game(
|
||||||
|
buttonA = lines[0].substring(12).split(", Y+").let {
|
||||||
|
it[0].toLong() to it[1].toLong()
|
||||||
|
},
|
||||||
|
buttonB = lines[1].substring(12).split(", Y+").let {
|
||||||
|
it[0].toLong() to it[1].toLong()
|
||||||
|
},
|
||||||
|
goal = lines[2].substring(9).split(", Y=").let {
|
||||||
|
it[0].toLong() + 10000000000000 to it[1].toLong() + 10000000000000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens = 0L
|
||||||
|
|
||||||
|
games.forEach { game ->
|
||||||
|
val aPressesNumer = Matrix2(
|
||||||
|
game.goal.first to game.buttonB.first,
|
||||||
|
game.goal.second to game.buttonB.second
|
||||||
|
).determinant()
|
||||||
|
val aPressesDenom = Matrix2(
|
||||||
|
game.buttonA.first to game.buttonB.first,
|
||||||
|
game.buttonA.second to game.buttonB.second
|
||||||
|
).determinant()
|
||||||
|
|
||||||
|
val bPressesNumer = Matrix2(
|
||||||
|
game.buttonA.first to game.goal.first,
|
||||||
|
game.buttonA.second to game.goal.second
|
||||||
|
).determinant()
|
||||||
|
val bPressesDenom = Matrix2(
|
||||||
|
game.buttonA.first to game.buttonB.first,
|
||||||
|
game.buttonA.second to game.buttonB.second
|
||||||
|
).determinant()
|
||||||
|
|
||||||
|
if (aPressesNumer % aPressesDenom == 0L && bPressesNumer % bPressesDenom == 0L) {
|
||||||
|
tokens += (aPressesNumer / aPressesDenom) * 3 + (bPressesNumer / bPressesDenom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Result: $tokens")
|
Loading…
Reference in a new issue