day(23): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
4fe0d4d891
commit
0cd41d53df
2 changed files with 237 additions and 0 deletions
7
samples/day23.txt
Normal file
7
samples/day23.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
....#..
|
||||||
|
..###.#
|
||||||
|
#...#.#
|
||||||
|
.#...##
|
||||||
|
#.###..
|
||||||
|
##.#.##
|
||||||
|
.#..#..
|
230
src/bin/day23.rs
Normal file
230
src/bin/day23.rs
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
use std::cmp::{max, min};
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
|
use aoc_2022::*;
|
||||||
|
|
||||||
|
type Position = Vector2D<isize>;
|
||||||
|
|
||||||
|
type Input = BTreeSet<Position>;
|
||||||
|
type Output = isize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Direction {
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
West,
|
||||||
|
East,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn adjacent(self) -> Vec<Position> {
|
||||||
|
match self {
|
||||||
|
Direction::North => vec![
|
||||||
|
Position::new(0, -1),
|
||||||
|
Position::new(1, -1),
|
||||||
|
Position::new(-1, -1),
|
||||||
|
],
|
||||||
|
Direction::South => vec![
|
||||||
|
Position::new(0, 1),
|
||||||
|
Position::new(1, 1),
|
||||||
|
Position::new(-1, 1),
|
||||||
|
],
|
||||||
|
Direction::West => vec![
|
||||||
|
Position::new(-1, 0),
|
||||||
|
Position::new(-1, 1),
|
||||||
|
Position::new(-1, -1),
|
||||||
|
],
|
||||||
|
Direction::East => vec![
|
||||||
|
Position::new(1, 0),
|
||||||
|
Position::new(1, 1),
|
||||||
|
Position::new(1, -1),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DIRECTIONS: Vec<Direction> = vec![
|
||||||
|
Direction::North,
|
||||||
|
Direction::South,
|
||||||
|
Direction::West,
|
||||||
|
Direction::East
|
||||||
|
];
|
||||||
|
static ref NEIGHBOURHOOD: BTreeSet<Position> =
|
||||||
|
DIRECTIONS.iter().flat_map(|d| d.adjacent()).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propose_moves(i: usize, elves: &Input) -> BTreeMap<Position, Position> {
|
||||||
|
let mut already_proposed: BTreeSet<Position> = BTreeSet::new();
|
||||||
|
|
||||||
|
let directions = (0..DIRECTIONS.len())
|
||||||
|
.map(|j| DIRECTIONS[(i + j) % DIRECTIONS.len()].adjacent())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let mut moves = BTreeMap::new();
|
||||||
|
for elf in elves {
|
||||||
|
if NEIGHBOURHOOD
|
||||||
|
.iter()
|
||||||
|
.map(|d| *elf + *d)
|
||||||
|
.all(|pos| !elves.contains(&pos))
|
||||||
|
{
|
||||||
|
// debug!("{:?} not moving.", elf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for adjacent in &directions {
|
||||||
|
if adjacent
|
||||||
|
.iter()
|
||||||
|
.map(|dpos| *dpos + *elf)
|
||||||
|
.all(|pos| !elves.contains(&pos))
|
||||||
|
{
|
||||||
|
let new_position = adjacent[0] + *elf;
|
||||||
|
|
||||||
|
// debug!(
|
||||||
|
// "{:?} proposes moving by {:?} to {:?}",
|
||||||
|
// elf, adjacent[0], new_position
|
||||||
|
// );
|
||||||
|
|
||||||
|
if already_proposed.contains(&new_position) {
|
||||||
|
// cannot move more than one elf to the position
|
||||||
|
moves.remove(&new_position);
|
||||||
|
|
||||||
|
// debug!("{:?} would move to the same location ({:?} by {:?}) as other elf, canceling!", elf, new_position, adjacent[0]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
moves.insert(new_position, *elf);
|
||||||
|
already_proposed.insert(new_position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moves
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_moves(elves: &mut Input, moves: &BTreeMap<Position, Position>) {
|
||||||
|
for previous_position in moves.values() {
|
||||||
|
elves.remove(previous_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
for new_position in moves.keys() {
|
||||||
|
elves.insert(*new_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round(i: usize, elves: &mut Input) -> bool {
|
||||||
|
let moves = propose_moves(i, elves);
|
||||||
|
execute_moves(elves, &moves);
|
||||||
|
|
||||||
|
moves.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _show_ground_with_dimensions(
|
||||||
|
positions: &Input,
|
||||||
|
min_x: isize,
|
||||||
|
max_x: isize,
|
||||||
|
min_y: isize,
|
||||||
|
max_y: isize,
|
||||||
|
) {
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
let mut row = String::new();
|
||||||
|
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
row += if positions.contains(&Position::new(x, y)) {
|
||||||
|
"#"
|
||||||
|
} else {
|
||||||
|
"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!("{}", row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _show_ground(positions: &Input) {
|
||||||
|
let min_pos = positions
|
||||||
|
.iter()
|
||||||
|
.fold(Vector2D::new(isize::MAX, isize::MAX), |acc, elf| {
|
||||||
|
Vector2D::new(min(*acc.x(), *elf.x()), min(*acc.y(), *elf.y()))
|
||||||
|
});
|
||||||
|
let max_pos = positions
|
||||||
|
.iter()
|
||||||
|
.fold(Vector2D::new(isize::MIN, isize::MIN), |acc, elf| {
|
||||||
|
Vector2D::new(max(*acc.x(), *elf.x()), max(*acc.y(), *elf.y()))
|
||||||
|
});
|
||||||
|
|
||||||
|
_show_ground_with_dimensions(
|
||||||
|
positions,
|
||||||
|
*min_pos.x(),
|
||||||
|
*max_pos.x(),
|
||||||
|
*min_pos.y(),
|
||||||
|
*max_pos.y(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day23;
|
||||||
|
impl Solution<Input, Output> for Day23 {
|
||||||
|
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||||
|
let map: Vec<Vec<char>> = file_to_string(pathname)
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.chars().collect_vec())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
map.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, row)| {
|
||||||
|
row.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|&(_, c)| c != &'.')
|
||||||
|
.map(move |(x, _)| Vector2D::new(x as isize, y as isize))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_1(input: &Input) -> Output {
|
||||||
|
let mut positions = input.clone();
|
||||||
|
|
||||||
|
// debug!("== Initial State ==");
|
||||||
|
// show_ground_with_dimensions(&positions, -3, 10, -2, 9);
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
round(i, &mut positions);
|
||||||
|
|
||||||
|
// debug!("== End of Round {} ==", i + 1);
|
||||||
|
// show_ground_with_dimensions(&positions, -3, 10, -2, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_pos = positions
|
||||||
|
.iter()
|
||||||
|
.fold(Vector2D::new(isize::MAX, isize::MAX), |acc, elf| {
|
||||||
|
Vector2D::new(min(*acc.x(), *elf.x()), min(*acc.y(), *elf.y()))
|
||||||
|
});
|
||||||
|
let max_pos = positions
|
||||||
|
.iter()
|
||||||
|
.fold(Vector2D::new(isize::MIN, isize::MIN), |acc, elf| {
|
||||||
|
Vector2D::new(max(*acc.x(), *elf.x()), max(*acc.y(), *elf.y()))
|
||||||
|
});
|
||||||
|
|
||||||
|
(max_pos.x() - min_pos.x() + 1) * (max_pos.y() - min_pos.y() + 1)
|
||||||
|
- (positions.len() as isize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_2(input: &Input) -> Output {
|
||||||
|
let mut positions = input.clone();
|
||||||
|
|
||||||
|
let mut rounds = 0;
|
||||||
|
while !round(rounds, &mut positions) {
|
||||||
|
rounds += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(1 + rounds).try_into().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Day23::run("sample")
|
||||||
|
Day23::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
test_sample!(day_23, Day23, 110, 20);
|
Loading…
Reference in a new issue