use std::fs; use std::path::Path; use std::collections::VecDeque; use std::fmt::Display; use std::fmt::Formatter; use std::fmt::Result; #[derive(Debug)] struct Map { step: usize, pixels: VecDeque> } impl Map { fn pad(&mut self, pix: u8) { let h = self.pixels.len(); let w = self.pixels[0].len(); let pix = if pix == 0 { 0 } else { u8::MAX }; if self.pixels[h-1].iter().any(|&b| b > 0) || self.pixels[h-2].iter().any(|&b| b > 0) { let empty = VecDeque::from([pix].repeat(w)); self.pixels.push_back(empty.clone()); self.pixels.push_back(empty); } if self.pixels[0].iter().any(|&b| b > 0) || self.pixels[1].iter().any(|&b| b > 0) { let empty = VecDeque::from([pix].repeat(w)); self.pixels.push_front(empty.clone()); self.pixels.push_front(empty); } if self.pixels.iter().any(|v| v[w-1] & 3 != 0) { self.pixels.iter_mut().for_each(|v| v.push_back(pix)); } if self.pixels.iter().any(|v| v[0] >= (1<<6)) { self.pixels.iter_mut().for_each(|v| v.push_front(pix)); } } fn enhance(mut self, algorithm: &[u8]) -> Map { let padpix = if algorithm[0] == 0 { 0 } else { algorithm[[algorithm.len()-1,0][self.step % 2]] }; self.pad(padpix); let h = self.pixels.len(); let w = self.pixels[0].len(); let mut pixels = VecDeque::with_capacity(h); for i in 0..h { let mut row = VecDeque::with_capacity(w); for j in 0..w { let mut byte = 0; for p in 0..8 { let pix = { if i < 1 || i > h-2 || (j < 1 && p < 1 ) || (j > w-2 && p > 6) { if algorithm[0] == 0 { 0 } else { algorithm[[0,algorithm.len()-1][self.step % 2]] } } else { algorithm[self.index(i,8*j+p)] } }; byte = (byte<<1) | pix; } row.push_back(byte); } pixels.push_back(row); } Map { step: self.step+1, pixels } } fn pix(&self, y: usize, x: usize) -> u8 { (self.pixels[y][x/8] >> (7-x%8)) & 1 } fn index(&self, y: usize, x: usize) -> usize { let mut idx = 0; for i in y-1..y+2 { for j in x-1..x+2 { idx = (idx << 1) | self.pix(i,j) as usize } } idx } fn lights(&self) -> u32 { self.pixels.iter().flat_map(|v| v.iter().map(|&b| b.count_ones())).sum() } } impl Display for Map { fn fmt(&self, f: &mut Formatter<'_>) -> Result { self.pixels.iter().for_each(|v| { v.iter().for_each(|b| write!(f, "{}", format!("{:08b}",b).chars().map(|c| if c == '1' { '#' } else { '.' }).collect::()).unwrap()); write!(f, "\n").unwrap() }); Ok(()) } } fn main() { let input = Path::new("resources").join("input.txt"); let content = fs::read_to_string(input).expect("Unable to read input file"); let (mut map, algorithm) = parse_input(&content); map = map.enhance(&algorithm).enhance(&algorithm); println!("Ex1: The result is {}", map.lights()); for _ in 0..50-2 { map = map.enhance(&algorithm); } println!("Ex2: The result is {}", map.lights()); } fn parse_input(input: &str) -> (Map, Vec) { let (algorithm,pixels) = input.split_once("\n\n").expect("Malformed input"); let algorithm: Vec = algorithm.chars().map(|c| (c == '#') as u8).collect(); let pixels: VecDeque> = pixels.lines().map(|l| { let bits: Vec = l.chars().map(|c| (c == '#') as u8).collect(); bits.chunks(8).map(|c| c.iter().fold(0, |a,b| (a<<1)|b) << (8-c.len())).collect() }).collect(); (Map { step: 0, pixels }, algorithm) } #[cfg(test)] mod tests { use super::*; const INPUT: &str = "..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..# #..#. #.... ##..# ..#.. ..###"; #[test] fn input_parse() { let (mut map,algorithm) = parse_input(INPUT); println!("{}", map); map = map.enhance(&algorithm); println!("{}", map); map = map.enhance(&algorithm); println!("{}", map); assert_eq!(35, map.lights()); } }