From aa1bc0ee493a066a7736a266d31dfddd7358857c Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 11 Dec 2021 10:34:15 -0800 Subject: [PATCH] A bunch of flashers. --- data/day11a.txt | 10 ++++ data/day11t.txt | 10 ++++ src/bin/day11.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++ src/map.rs | 124 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 data/day11a.txt create mode 100644 data/day11t.txt create mode 100644 src/bin/day11.rs diff --git a/data/day11a.txt b/data/day11a.txt new file mode 100644 index 0000000..f3df54f --- /dev/null +++ b/data/day11a.txt @@ -0,0 +1,10 @@ +2264552475 +7681287325 +3878781441 +6868471776 +7175255555 +7517441253 +3513418848 +4628736747 +1133155762 +8816621663 diff --git a/data/day11t.txt b/data/day11t.txt new file mode 100644 index 0000000..03743f6 --- /dev/null +++ b/data/day11t.txt @@ -0,0 +1,10 @@ +5483143223 +2745854711 +5264556173 +6141336146 +6357385478 +4167524645 +2176841721 +6882881134 +4846848554 +5283751526 diff --git a/src/bin/day11.rs b/src/bin/day11.rs new file mode 100644 index 0000000..4dd45e5 --- /dev/null +++ b/src/bin/day11.rs @@ -0,0 +1,135 @@ +use advent2021::map::{Graph, Oopsie}; +use core::fmt; + +#[cfg(test)] +const TEST_DATA: &str = include_str!("../../data/day11t.txt"); +const REAL_DATA: &str = include_str!("../../data/day11a.txt"); + +pub struct Level(u16); + +impl fmt::Debug for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl PartialEq for Level { + fn eq(&self, other: &u16) -> bool { + self.0 == *other + } +} + +impl TryFrom for Level { + type Error = Oopsie; + + fn try_from(value: char) -> Result { + Ok(Level( + value.to_digit(10).ok_or(Oopsie::BadCharacter(value))? as u16, + )) + } +} + +impl Level { + fn increment(&mut self) -> bool { + if self.0 == 9 { + self.0 = 0; + true + } else { + self.0 += 1; + false + } + } +} + +fn step(map: &mut Graph) -> usize { + let mut flash_points: Vec<(usize, usize)> = map + .points_mut() + .map(|p| (p.x, p.y, p.value.increment())) + .filter_map(|(x, y, flashed)| if flashed { Some((x, y)) } else { None }) + .collect(); + let mut queue = flash_points.clone(); + + // loop until we have flashed everyone + while let Some((x, y)) = queue.pop() { + for (nx, ny) in map.neighbor_points(x, y).drain(..) { + if !flash_points.contains(&(nx, ny)) && map.get_mut(nx, ny).unwrap().value.increment() { + flash_points.push((nx, ny)); + queue.push((nx, ny)); + } + } + } + + flash_points.len() +} + +fn run(map: &mut Graph, steps: usize) -> usize { + let mut total_flashes = 0; + + for _ in 0..steps { + total_flashes += step(map); + } + + total_flashes +} + +fn full_flash_step(map: &mut Graph) -> usize { + let mut count = 0; + + loop { + let flash_count = step(map); + + count += 1; + if flash_count == map.size() { + return count; + } + } +} + +#[test] +fn regression() { + let mut test_data_steps: Graph = Graph::from_file_data(TEST_DATA).unwrap(); + assert_eq!(0, step(&mut test_data_steps)); + assert_eq!(35, step(&mut test_data_steps)); + assert_eq!(45, step(&mut test_data_steps)); + assert_eq!(16, step(&mut test_data_steps)); + assert_eq!(8, step(&mut test_data_steps)); + assert_eq!(1, step(&mut test_data_steps)); + assert_eq!(7, step(&mut test_data_steps)); + assert_eq!(24, step(&mut test_data_steps)); + assert_eq!(39, step(&mut test_data_steps)); + assert_eq!(29, step(&mut test_data_steps)); + + let mut test_data10: Graph = Graph::from_file_data(TEST_DATA).unwrap(); + assert_eq!(204, run(&mut test_data10, 10)); + + let mut test_data: Graph = Graph::from_file_data(TEST_DATA).unwrap(); + assert_eq!(1656, run(&mut test_data, 100)); + + let mut test_data_wait: Graph = Graph::from_file_data(TEST_DATA).unwrap(); + assert_eq!(195, full_flash_step(&mut test_data_wait)); +} + +fn day11() -> Result<(), Oopsie> { + let mut real_data: Graph = Graph::from_file_data(REAL_DATA)?; + println!("Flashes after 100 steps: {}", run(&mut real_data, 100)); + + let mut real_data_wait = Graph::from_file_data(REAL_DATA)?; + println!( + "{} steps until they all flash.", + full_flash_step(&mut real_data_wait) + ); + + Ok(()) +} + +fn main() { + if let Err(e) = day11() { + println!("Top level error: {}", e); + } +} diff --git a/src/map.rs b/src/map.rs index 527bbe9..7ded17c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -17,7 +17,7 @@ pub struct Graph { height: usize, } -impl> Graph { +impl> Graph { pub fn from_file_data(file_data: &str) -> Result, Oopsie> { let mut data = Vec::with_capacity(file_data.len()); let mut width = None; @@ -57,6 +57,12 @@ impl> Graph { height, }) } +} + +impl Graph { + pub fn size(&self) -> usize { + self.width * self.height + } pub fn get(&self, x: usize, y: usize) -> Option> { if x >= self.width || y >= self.height { @@ -70,6 +76,26 @@ impl> Graph { }) } + pub fn get_mut(&mut self, x: usize, y: usize) -> Option> { + if x >= self.width || y >= self.height { + return None; + } + + Some(PointMut { + x, + y, + value: &mut self.data[(y * self.width) + x], + }) + } + + pub fn coordinates(&self) -> Coord { + Coord { + graph: self, + curx: 0, + cury: 0, + } + } + pub fn points(&self) -> Points<'_, T> { Points { graph: self, @@ -78,6 +104,14 @@ impl> Graph { } } + pub fn points_mut(&mut self) -> impl Iterator> { + let coords: Vec<(usize, usize)> = self.coordinates().collect(); + self.data + .iter_mut() + .zip(coords) + .map(|(value, (x, y))| PointMut { x, y, value }) + } + pub fn neighbors(&self, x: usize, y: usize) -> Vec> { let mut retval = Vec::new(); @@ -96,6 +130,50 @@ impl> Graph { retval } + + pub fn neighbor_points(&mut self, x: usize, y: usize) -> Vec<(usize, usize)> { + let mut retval = Vec::new(); + + if y > 0 { + if x > 0 { + retval.push((x - 1, y - 1)); + } + retval.push((x, y - 1)); + if x + 1 < self.width { + retval.push((x + 1, y - 1)) + }; + } + + if x > 0 { + retval.push((x - 1, y)) + }; + if x + 1 < self.width { + retval.push((x + 1, y)) + }; + + if y + 1 < self.width { + if x > 0 { + retval.push((x - 1, y + 1)) + }; + retval.push((x, y + 1)); + if x + 1 < self.width { + retval.push((x + 1, y + 1)) + }; + } + + retval + } +} + +impl Graph { + pub fn print(&self) { + for y in 0..self.height { + for x in 0..self.width { + print!("{}", self.get(x, y).unwrap().value); + } + println!(); + } + } } #[derive(Clone)] @@ -117,6 +195,50 @@ impl<'a, T> PartialEq for Point<'a, T> { } } +pub struct PointMut<'a, T> { + pub x: usize, + pub y: usize, + pub value: &'a mut T, +} + +impl<'a, T> fmt::Debug for PointMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({},{})", self.x, self.y) + } +} + +impl<'a, T> PartialEq for PointMut<'a, T> { + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} + +pub struct Coord<'a, T> { + graph: &'a Graph, + curx: usize, + cury: usize, +} + +impl<'a, T> Iterator for Coord<'a, T> { + type Item = (usize, usize); + + fn next(&mut self) -> Option { + if self.cury == self.graph.height { + return None; + } + + let next_value = (self.curx, self.cury); + + self.curx += 1; + if self.curx == self.graph.width { + self.curx = 0; + self.cury += 1; + } + + Some(next_value) + } +} + pub struct Points<'a, T> { graph: &'a Graph, curx: usize,