1
0
Fork 0

day(23): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-01-07 12:32:06 +01:00
parent 4fe0d4d891
commit 0cd41d53df
Signed by: mfocko
GPG key ID: 7C47D46246790496
2 changed files with 237 additions and 0 deletions

7
samples/day23.txt Normal file
View file

@ -0,0 +1,7 @@
....#..
..###.#
#...#.#
.#...##
#.###..
##.#.##
.#..#..

230
src/bin/day23.rs Normal file
View 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);