use std::collections::HashMap; use std::collections::HashSet; use std::io::Read; use std::mem::swap; use crate::common::Lines; use crate::Solution; type Pos = (i32, i32); const DIRECTIONS: [Pos; 6] = [(-2, 0), (2, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]; fn convert_pos(s: &str) -> Pos { let mut s = s.chars(); let mut x = 0; let mut y = 0; while let Some(c) = s.next() { match c { 'e' => x += 2, 'w' => x -= 2, 'n' | 's' => { if c == 'n' { y += 1; } else { y -= 1; } match s.next() { Some('e') => x += 1, Some('w') => x -= 1, _ => panic!(), } } _ => panic!(), } } (x, y) } fn step(current: &HashSet, target: &mut HashSet) { let mut black_count: HashMap = current.iter().map(|&pos| (pos, 0)).collect(); for &(x, y) in current { for &(dx, dy) in &DIRECTIONS { let pos = (x + dx, y + dy); *black_count.entry(pos).or_default() += 1; } } target.clear(); for (pos, neighbours) in black_count { let is_black = current.contains(&pos); let going_black = match neighbours { 1 | 2 if is_black => true, 2 if !is_black => true, _ => false, }; if going_black { target.insert(pos); } } } fn get_black_tiles(input: &mut dyn Read) -> HashSet { let mut black = HashSet::new(); for line in Lines::new(input) { let pos = convert_pos(&line); if !black.insert(pos) { black.remove(&pos); } } black } #[derive(Default)] pub struct Day24; impl Solution for Day24 { fn part1(&mut self, input: &mut dyn Read) -> String { get_black_tiles(input).len().to_string() } fn part2(&mut self, input: &mut dyn Read) -> String { let mut state = get_black_tiles(input); let mut scratch_pad = state.clone(); for _ in 0..100 { step(&state, &mut scratch_pad); swap(&mut state, &mut scratch_pad); } state.len().to_string() } } #[cfg(test)] mod tests { use crate::test_implementation; use super::*; const SAMPLE: &[u8] = include_bytes!("../samples/24.txt"); #[test] fn test_convert_pos() { assert_eq!((1, -1), convert_pos("esew")); assert_eq!((0, 0), convert_pos("nwwswee")); } #[test] fn test_step() { let state = get_black_tiles(&mut SAMPLE.clone()); let mut target = state.clone(); step(&state, &mut target); assert_eq!(15, target.len()); } #[test] fn sample_part1() { test_implementation(Day24, 1, SAMPLE, 10); } #[test] fn sample_part2() { test_implementation(Day24, 2, SAMPLE, 2208); } }