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 { 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 { Direction::ALL.iter().map(move |&d| self + d) } } impl std::ops::Add 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) -> HashSet { let mut new_black_tiles: HashSet = HashSet::new(); let mut dont_flip_to_black: HashSet = 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::>(); let flip_tiles = directions.iter().map(|tile| { tile.iter().fold(Position::default(), |tile, &dir| tile + dir) }).collect::>(); 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()); }