Solve day23

This commit is contained in:
Malte Tammena 2022-12-23 21:56:53 +01:00
parent 9af717658a
commit 7a214d2591
3 changed files with 338 additions and 2 deletions

75
day23/input Normal file
View file

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

7
day23/input.test Normal file
View file

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

View file

@ -1,3 +1,257 @@
fn main() {
println!("Hello, world!");
use std::{
collections::{HashMap, HashSet},
fs::File,
io::{BufRead, BufReader},
ops::Index,
};
type Elf = (isize, isize);
const ACTIONS: [Dir; 4] = [Dir::North, Dir::South, Dir::West, Dir::East];
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
#[default]
Second,
}
struct Playground {
elves: HashSet<Elf>,
first_action_index: usize,
}
impl Playground {
fn parse_input<R: BufRead>(reader: R) -> Self {
let elves = reader
.lines()
.map(Result::unwrap)
.enumerate()
.flat_map(|(y, line)| {
line.into_bytes()
.into_iter()
.enumerate()
.filter_map(move |(x, byte)| {
if byte == b'#' {
Some((x as isize, y as isize))
} else {
None
}
})
})
.collect();
Self {
elves,
first_action_index: 0,
}
}
fn simulate_round(&mut self) -> HasMoved {
let mut any_moved = false;
let proposed_moves: HashMap<Elf, Elf> = self
.elves
.iter()
.copied()
.filter_map(|elf| {
let surr = Surrounding::for_elf(&self.elves, &elf);
// Do nothing if there's noone around
if surr.is_empty() {
None
} else {
let mut proposed_move = None;
for action_idx in self.first_action_index..self.first_action_index + 4 {
let action = ACTIONS[action_idx % ACTIONS.len()];
if surr.is_dir_empty(action) {
proposed_move = Some(action);
break;
}
}
proposed_move.map(|dir| (elf, dir.apply_to(elf)))
}
})
.collect();
let mut illegal_moves: HashSet<Elf> = HashSet::new();
proposed_moves
.iter()
.fold(HashSet::new(), |mut set, (_from, to)| {
if set.contains(&to) {
illegal_moves.insert(*to);
} else {
set.insert(to);
}
set
});
proposed_moves
.into_iter()
.filter(|(_from, to)| !illegal_moves.contains(to))
.for_each(|(from, to)| {
self.elves.remove(&from);
self.elves.insert(to);
any_moved = true;
});
self.first_action_index = (self.first_action_index + 1) % ACTIONS.len();
match any_moved {
true => HasMoved::Yes,
false => HasMoved::No,
}
}
fn count_empty_tiles(self) -> usize {
// Calculate empty tiles
let (min_x, max_x, min_y, max_y, total_elf_count) = self.elves.into_iter().fold(
(isize::MAX, isize::MIN, isize::MAX, isize::MIN, 0),
|(min_x, max_x, min_y, max_y, count), (x, y)| {
(
min_x.min(x),
max_x.max(x),
min_y.min(y),
max_y.max(y),
count + 1,
)
},
);
((max_x - min_x + 1) * (max_y - min_y + 1) - total_elf_count) as usize
}
}
#[derive(Debug, Clone, Copy)]
enum Dir {
North,
East,
South,
West,
}
impl Dir {
fn apply_to(&self, (x, y): Elf) -> Elf {
match self {
Dir::North => (x, y - 1),
Dir::East => (x + 1, y),
Dir::South => (x, y + 1),
Dir::West => (x - 1, y),
}
}
}
#[derive(Debug, Clone, Copy)]
enum DirDiag {
North = 0,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
}
#[derive(Debug)]
struct Surrounding {
raw: [bool; 8],
}
impl Surrounding {
fn for_elf(elves: &HashSet<Elf>, &(x, y): &Elf) -> Self {
let raw = [
(x, y - 1),
(x + 1, y - 1),
(x + 1, y),
(x + 1, y + 1),
(x, y + 1),
(x - 1, y + 1),
(x - 1, y),
(x - 1, y - 1),
]
.into_iter()
.map(|(x, y)| elves.get(&(x, y)).is_some())
.collect::<Vec<_>>()
.try_into()
.unwrap();
Self { raw }
}
fn is_empty(&self) -> bool {
!self.raw.iter().any(|&b| b)
}
fn is_dir_empty(&self, action: Dir) -> bool {
use DirDiag::*;
let any = match action {
Dir::North => self[North] || self[NorthWest] || self[NorthEast],
Dir::East => self[East] || self[NorthEast] || self[SouthEast],
Dir::South => self[South] || self[SouthWest] || self[SouthEast],
Dir::West => self[West] || self[NorthWest] || self[SouthWest],
};
!any
}
}
enum HasMoved {
Yes,
No,
}
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
let mut playground = Playground::parse_input(reader);
for _ in 1..11 {
playground.simulate_round();
}
playground.count_empty_tiles()
}
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
let mut playground = Playground::parse_input(reader);
let mut curr_round = 1;
loop {
let has_moved = playground.simulate_round();
if let HasMoved::No = has_moved {
break curr_round;
}
curr_round += 1;
}
}
fn main() {
let mut args = ::std::env::args().skip(1);
let file = args.next().unwrap_or_else(|| String::from("./input"));
let solve = args
.next()
.map(|arg| match arg.as_str() {
"first" => SolvePuzzle::First,
"second" => SolvePuzzle::Second,
_ => unreachable!(),
})
.unwrap_or_default();
let file = BufReader::new(File::open(file).expect("Opening file"));
match solve {
SolvePuzzle::First => println!("{}", solve_first_puzzle(file)),
SolvePuzzle::Second => println!("{}", solve_second_puzzle(file)),
};
}
impl Index<DirDiag> for Surrounding {
type Output = bool;
fn index(&self, index: DirDiag) -> &Self::Output {
&self.raw[index as usize]
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
fn example_on_first() {
let reader = Cursor::new(include_str!("../input.test"));
assert_eq!(solve_first_puzzle(reader), 110);
}
#[test]
fn example_on_second() {
let reader = Cursor::new(include_str!("../input.test"));
assert_eq!(solve_second_puzzle(reader), 20);
}
}