day21
This commit is contained in:
parent
a19a3258c2
commit
9db0e3f054
@ -1,5 +1,4 @@
|
|||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
|
||||||
|
186
src/day21.rs
Normal file
186
src/day21.rs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
const STEPS: i32 = 64;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
struct Position(i32, i32);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct Garden {
|
||||||
|
rocks: HashSet<Position>,
|
||||||
|
start: Position,
|
||||||
|
extents: (Position, Position),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day21, part1)]
|
||||||
|
fn parse(input: &str) -> Garden {
|
||||||
|
let mut rocks: HashSet<Position> = HashSet::new();
|
||||||
|
let mut start: Position = Position(0, 0);
|
||||||
|
for (row, line) in input.lines().enumerate() {
|
||||||
|
for (col, ch) in line.chars().enumerate() {
|
||||||
|
match ch {
|
||||||
|
'#' => {
|
||||||
|
rocks.insert(Position(row as i32, col as i32));
|
||||||
|
}
|
||||||
|
'S' => {
|
||||||
|
start = Position(row as i32, col as i32);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut trans_rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for rock in rocks.iter() {
|
||||||
|
let trans_rock = Position(rock.0 - start.0, start.1 - rock.1);
|
||||||
|
trans_rocks.insert(trans_rock);
|
||||||
|
}
|
||||||
|
rocks = trans_rocks;
|
||||||
|
let num_rows = input.lines().count() as i32;
|
||||||
|
let num_cols = input.lines().next().unwrap().chars().count() as i32;
|
||||||
|
let extents = (
|
||||||
|
Position(-num_rows / 2, -num_cols / 2),
|
||||||
|
Position(num_rows / 2, num_cols / 2),
|
||||||
|
);
|
||||||
|
Garden {
|
||||||
|
rocks,
|
||||||
|
start: Position(0, 0),
|
||||||
|
extents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day21, part1)]
|
||||||
|
fn part1(map: &Garden) -> u32 {
|
||||||
|
let mut visited: HashSet<Position> = HashSet::new();
|
||||||
|
visited.insert(map.start);
|
||||||
|
for _ in 0..STEPS {
|
||||||
|
let mut new_visited = HashSet::new();
|
||||||
|
for pos in visited.iter().clone() {
|
||||||
|
let neighbors = [
|
||||||
|
Position(pos.0 - 1, pos.1),
|
||||||
|
Position(pos.0 + 1, pos.1),
|
||||||
|
Position(pos.0, pos.1 - 1),
|
||||||
|
Position(pos.0, pos.1 + 1),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| in_bounds(*p, map.extents) && !map.rocks.contains(p));
|
||||||
|
for neighbor in neighbors {
|
||||||
|
new_visited.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited = new_visited;
|
||||||
|
}
|
||||||
|
visited.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_bounds(pos: Position, extents: (Position, Position)) -> bool {
|
||||||
|
pos.0 >= extents.0 .0 && pos.0 <= extents.1 .0 && pos.1 >= extents.0 .1 && pos.1 < extents.1 .1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_bounds_part2(pos: Position, extents: Position) -> bool {
|
||||||
|
pos.0 >= 0 && pos.0 <= extents.0 && pos.1 >= 0 && pos.1 < extents.1
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GardenPart2 {
|
||||||
|
rocks: HashSet<Position>,
|
||||||
|
start: Position,
|
||||||
|
extents: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day21, part2)]
|
||||||
|
fn parse_part2(input: &str) -> GardenPart2 {
|
||||||
|
let mut rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for (row, line) in input.lines().enumerate() {
|
||||||
|
for (col, ch) in line.chars().enumerate() {
|
||||||
|
match ch {
|
||||||
|
'#' => {
|
||||||
|
rocks.insert(Position(row as i32, col as i32));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num_rows = input.lines().count() as i32;
|
||||||
|
let num_cols = input.lines().next().unwrap().chars().count() as i32;
|
||||||
|
let mut expanded_rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for rock in rocks.iter() {
|
||||||
|
for row_mul in 0..5 {
|
||||||
|
for col_mul in 0..5 {
|
||||||
|
let expanded_rock = Position(
|
||||||
|
rock.0 + (num_rows * row_mul as i32),
|
||||||
|
rock.1 + (num_cols * col_mul as i32),
|
||||||
|
);
|
||||||
|
expanded_rocks.insert(expanded_rock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GardenPart2 {
|
||||||
|
rocks: expanded_rocks,
|
||||||
|
start: Position(num_rows * 5 / 2, num_cols * 5 / 2),
|
||||||
|
extents: Position(num_rows * 5, num_cols * 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day21, part2)]
|
||||||
|
fn part2(map: &GardenPart2) -> i64 {
|
||||||
|
let b0: i64 = walk(&map, 65) as i64;
|
||||||
|
let b1: i64 = walk(&map, 65 + 131) as i64;
|
||||||
|
let b2: i64 = walk(&map, 65 + 2 * 131) as i64;
|
||||||
|
let n: i64 = 202300;
|
||||||
|
// below uses Cramer's Rule to solve for x0, x1, x2
|
||||||
|
let det_a: f64 = -2.0;
|
||||||
|
let det_a0: f64 = -b0 as f64 + 2.0 * b1 as f64 - b2 as f64;
|
||||||
|
let det_a1: f64 = 3.0 * b0 as f64 - 4.0 * b1 as f64 + b2 as f64;
|
||||||
|
let det_a2: f64 = -2.0 * b0 as f64;
|
||||||
|
let x0: i64 = (det_a0 / det_a) as i64;
|
||||||
|
let x1: i64 = (det_a1 / det_a) as i64;
|
||||||
|
let x2: i64 = (det_a2 / det_a) as i64;
|
||||||
|
|
||||||
|
x0 * n * n + x1 * n + x2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk(garden: &GardenPart2, steps: u32) -> u32 {
|
||||||
|
let mut visited: HashSet<Position> = HashSet::new();
|
||||||
|
visited.insert(garden.start);
|
||||||
|
for _ in 0..steps {
|
||||||
|
let mut new_visited = HashSet::new();
|
||||||
|
for pos in visited.iter().clone() {
|
||||||
|
let neighbors = [
|
||||||
|
Position(pos.0 - 1, pos.1),
|
||||||
|
Position(pos.0 + 1, pos.1),
|
||||||
|
Position(pos.0, pos.1 - 1),
|
||||||
|
Position(pos.0, pos.1 + 1),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| in_bounds_part2(*p, garden.extents) && !garden.rocks.contains(p));
|
||||||
|
for neighbor in neighbors {
|
||||||
|
new_visited.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited = new_visited;
|
||||||
|
}
|
||||||
|
visited.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"...........
|
||||||
|
.....###.#.
|
||||||
|
.###.##..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.##..S####.
|
||||||
|
.##..#...#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##..##.##.
|
||||||
|
...........";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 16);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
mod day21;
|
||||||
mod day20;
|
mod day20;
|
||||||
mod day19;
|
mod day19;
|
||||||
mod day18;
|
mod day18;
|
||||||
|
Loading…
Reference in New Issue
Block a user