141 lines
4.1 KiB
Rust
141 lines
4.1 KiB
Rust
use std::collections::HashSet;
|
|
|
|
const INPUT: &str = include_str!("../../data/day24");
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
enum Direction {
|
|
East,
|
|
SouthEast,
|
|
SouthWest,
|
|
West,
|
|
NorthWest,
|
|
NorthEast,
|
|
}
|
|
|
|
impl Direction {
|
|
const ALL: [Direction; 6] = [
|
|
Self::East,
|
|
Self::SouthEast,
|
|
Self::SouthWest,
|
|
Self::West,
|
|
Self::NorthWest,
|
|
Self::NorthEast,
|
|
];
|
|
|
|
fn symbol(self) -> &'static str {
|
|
match self {
|
|
Self::East => "e",
|
|
Self::SouthEast => "se",
|
|
Self::SouthWest => "sw",
|
|
Self::West => "w",
|
|
Self::NorthWest => "nw",
|
|
Self::NorthEast => "ne",
|
|
}
|
|
}
|
|
|
|
fn parse_list(line: &str) -> Vec<Self> {
|
|
let mut line = line.trim();
|
|
let mut result = Vec::new();
|
|
'outer: while !line.is_empty() {
|
|
for &d in Self::ALL.iter() {
|
|
if let Some(rem) = line.strip_prefix(d.symbol()) {
|
|
result.push(d);
|
|
line = rem;
|
|
continue 'outer;
|
|
}
|
|
}
|
|
panic!("Invalid directions: {:?}", line);
|
|
}
|
|
result
|
|
}
|
|
|
|
// -> (row, col)
|
|
fn as_offset(self) -> (i32, i32) {
|
|
match self {
|
|
Self::East => (0, 1),
|
|
Self::SouthEast => (1, 1),
|
|
Self::SouthWest => (1, 0),
|
|
Self::West => (0, -1),
|
|
Self::NorthWest => (-1, -1),
|
|
Self::NorthEast => (-1, 0),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
|
|
struct Position {
|
|
row: i32,
|
|
// the "0"-column goes from south-west to north-east through (0/0)
|
|
col: i32,
|
|
}
|
|
|
|
impl Position {
|
|
fn adjacent(self) -> impl Iterator<Item=Self> {
|
|
Direction::ALL.iter().map(move |&d| self + d)
|
|
}
|
|
}
|
|
|
|
impl std::ops::Add<Direction> for Position {
|
|
type Output = Position;
|
|
|
|
fn add(self, rhs: Direction) -> Self::Output {
|
|
let (row, col) = rhs.as_offset();
|
|
Self {
|
|
row: self.row + row,
|
|
col: self.col + col,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> {
|
|
let mut new_black_tiles: HashSet<Position> = HashSet::new();
|
|
let mut dont_flip_to_black: HashSet<Position> = HashSet::new();
|
|
for &tile in &black_tiles {
|
|
let mut count_adj_black = 0;
|
|
for adj_tile in tile.adjacent() {
|
|
if black_tiles.contains(&adj_tile) {
|
|
count_adj_black += 1;
|
|
} else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) {
|
|
// white adjacent: maybe needs to be flipped to black
|
|
let mut white_count_adj_black = 0;
|
|
for adj_tile_2 in adj_tile.adjacent() {
|
|
if black_tiles.contains(&adj_tile_2) {
|
|
white_count_adj_black += 1;
|
|
if white_count_adj_black > 2 { break; }
|
|
}
|
|
}
|
|
if white_count_adj_black == 2 {
|
|
new_black_tiles.insert(adj_tile);
|
|
} else {
|
|
// don't check again
|
|
dont_flip_to_black.insert(adj_tile);
|
|
}
|
|
}
|
|
}
|
|
if count_adj_black == 1 || count_adj_black == 2 {
|
|
new_black_tiles.insert(tile);
|
|
}
|
|
}
|
|
new_black_tiles
|
|
}
|
|
|
|
fn main() {
|
|
let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>();
|
|
let flip_tiles = directions.iter().map(|tile| {
|
|
tile.iter().fold(Position::default(), |tile, &dir| tile + dir)
|
|
}).collect::<Vec<_>>();
|
|
let mut black_tiles = HashSet::new();
|
|
for &tile in &flip_tiles {
|
|
if black_tiles.contains(&tile) {
|
|
black_tiles.remove(&tile);
|
|
} else {
|
|
black_tiles.insert(tile);
|
|
}
|
|
}
|
|
println!("Black tiles: {}", black_tiles.len());
|
|
for _ in 0..100 {
|
|
black_tiles = daily_flip(black_tiles);
|
|
}
|
|
println!("Black tiles: {}", black_tiles.len());
|
|
}
|