A bunch of flashers.
This commit is contained in:
10
data/day11a.txt
Normal file
10
data/day11a.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
2264552475
|
||||||
|
7681287325
|
||||||
|
3878781441
|
||||||
|
6868471776
|
||||||
|
7175255555
|
||||||
|
7517441253
|
||||||
|
3513418848
|
||||||
|
4628736747
|
||||||
|
1133155762
|
||||||
|
8816621663
|
||||||
10
data/day11t.txt
Normal file
10
data/day11t.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
5483143223
|
||||||
|
2745854711
|
||||||
|
5264556173
|
||||||
|
6141336146
|
||||||
|
6357385478
|
||||||
|
4167524645
|
||||||
|
2176841721
|
||||||
|
6882881134
|
||||||
|
4846848554
|
||||||
|
5283751526
|
||||||
135
src/bin/day11.rs
Normal file
135
src/bin/day11.rs
Normal file
@@ -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<u16> for Level {
|
||||||
|
fn eq(&self, other: &u16) -> bool {
|
||||||
|
self.0 == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for Level {
|
||||||
|
type Error = Oopsie;
|
||||||
|
|
||||||
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||||
|
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<Level>) -> 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<Level>, 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<Level>) -> 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<Level> = 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<Level> = Graph::from_file_data(TEST_DATA).unwrap();
|
||||||
|
assert_eq!(204, run(&mut test_data10, 10));
|
||||||
|
|
||||||
|
let mut test_data: Graph<Level> = Graph::from_file_data(TEST_DATA).unwrap();
|
||||||
|
assert_eq!(1656, run(&mut test_data, 100));
|
||||||
|
|
||||||
|
let mut test_data_wait: Graph<Level> = 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<Level> = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
124
src/map.rs
124
src/map.rs
@@ -17,7 +17,7 @@ pub struct Graph<T> {
|
|||||||
height: usize,
|
height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + TryFrom<char, Error = Oopsie>> Graph<T> {
|
impl<T: TryFrom<char, Error = Oopsie>> Graph<T> {
|
||||||
pub fn from_file_data(file_data: &str) -> Result<Graph<T>, Oopsie> {
|
pub fn from_file_data(file_data: &str) -> Result<Graph<T>, Oopsie> {
|
||||||
let mut data = Vec::with_capacity(file_data.len());
|
let mut data = Vec::with_capacity(file_data.len());
|
||||||
let mut width = None;
|
let mut width = None;
|
||||||
@@ -57,6 +57,12 @@ impl<T: Clone + TryFrom<char, Error = Oopsie>> Graph<T> {
|
|||||||
height,
|
height,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Graph<T> {
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.width * self.height
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, x: usize, y: usize) -> Option<Point<T>> {
|
pub fn get(&self, x: usize, y: usize) -> Option<Point<T>> {
|
||||||
if x >= self.width || y >= self.height {
|
if x >= self.width || y >= self.height {
|
||||||
@@ -70,6 +76,26 @@ impl<T: Clone + TryFrom<char, Error = Oopsie>> Graph<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, x: usize, y: usize) -> Option<PointMut<T>> {
|
||||||
|
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<T> {
|
||||||
|
Coord {
|
||||||
|
graph: self,
|
||||||
|
curx: 0,
|
||||||
|
cury: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn points(&self) -> Points<'_, T> {
|
pub fn points(&self) -> Points<'_, T> {
|
||||||
Points {
|
Points {
|
||||||
graph: self,
|
graph: self,
|
||||||
@@ -78,6 +104,14 @@ impl<T: Clone + TryFrom<char, Error = Oopsie>> Graph<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn points_mut(&mut self) -> impl Iterator<Item = PointMut<T>> {
|
||||||
|
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<Point<T>> {
|
pub fn neighbors(&self, x: usize, y: usize) -> Vec<Point<T>> {
|
||||||
let mut retval = Vec::new();
|
let mut retval = Vec::new();
|
||||||
|
|
||||||
@@ -96,6 +130,50 @@ impl<T: Clone + TryFrom<char, Error = Oopsie>> Graph<T> {
|
|||||||
|
|
||||||
retval
|
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<T: fmt::Display> Graph<T> {
|
||||||
|
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)]
|
#[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<T>,
|
||||||
|
curx: usize,
|
||||||
|
cury: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for Coord<'a, T> {
|
||||||
|
type Item = (usize, usize);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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> {
|
pub struct Points<'a, T> {
|
||||||
graph: &'a Graph<T>,
|
graph: &'a Graph<T>,
|
||||||
curx: usize,
|
curx: usize,
|
||||||
|
|||||||
Reference in New Issue
Block a user