1
Fork 0
advent-of-code/source/year_2020/day_11/grid.rs

163 lines
3.7 KiB
Rust
Raw Normal View History

2022-10-03 16:02:40 +00:00
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Cell {
Edge,
Floor,
Empty,
Occupied,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Ruleset {
PartOne,
PartTwo,
}
impl From<char> for Cell {
fn from(input: char) -> Self {
match input {
'@' => Self::Edge,
'.' => Self::Floor,
'L' => Self::Empty,
'#' => Self::Occupied,
_ => unreachable!("{input}"),
}
}
}
#[derive(Debug)]
pub struct Grid {
pub line_length: isize,
pub occupied_cell_count: usize,
pub occupied_cell_tolerance: usize,
pub cells: Vec<Cell>,
pub ruleset: Ruleset,
}
impl Grid {
pub fn new(input: &str, ruleset: Ruleset) -> Self {
let line_length = input.find('\n').unwrap() + 2;
// Deal with edge cases by just adding an extra cell to the outer edges.
let extra_floor = "@".repeat(line_length - 2);
let data = format!("{}\n{}\n{}", extra_floor, input, extra_floor);
let cells = data
.trim()
.replace('\n', "@@")
.chars()
.map(Into::into)
.collect();
let occupied_cell_tolerance = match ruleset {
Ruleset::PartOne => 4,
Ruleset::PartTwo => 5,
};
Self {
line_length: line_length as isize,
occupied_cell_count: 0,
occupied_cell_tolerance,
cells,
ruleset,
}
}
pub fn simulate_step(&self) -> Vec<Cell> {
let mut cells = self.cells.clone();
for (index, cell) in self.cells.iter().enumerate() {
let index = index as isize;
let mut occupied_cells = 0;
let adjacencies = vec![
// Top Left
-self.line_length - 1,
// Above
-self.line_length,
// Top Right
-self.line_length + 1,
// Left
-1,
// Right
1,
// Bottom Left
self.line_length - 1,
// Below
self.line_length,
// Bottom Right
self.line_length + 1,
];
if self.ruleset == Ruleset::PartOne {
for mut adjacency in adjacencies {
adjacency += index;
if adjacency < 0 {
continue;
}
if self.cells.get(adjacency as usize) == Some(&Cell::Occupied) {
occupied_cells += 1;
}
}
} else if self.ruleset == Ruleset::PartTwo {
for adjacency in adjacencies {
let mut target_index = index;
'inner: loop {
target_index += adjacency;
if target_index < 0 || target_index as usize > self.cells.len() {
break 'inner;
}
match self.cells.get(target_index as usize) {
Some(&Cell::Empty) | Some(&Cell::Edge) => break 'inner,
Some(&Cell::Occupied) => {
occupied_cells += 1;
break 'inner;
}
_ => (),
};
}
}
}
if cell == &Cell::Edge || cell == &Cell::Floor {
continue;
}
if cell == &Cell::Empty && occupied_cells == 0 {
cells[index as usize] = Cell::Occupied;
}
if cell == &Cell::Occupied
&& occupied_cells >= self.occupied_cell_tolerance
{
cells[index as usize] = Cell::Empty;
}
}
cells
}
pub fn count_cells(&self, target: Cell) -> usize {
self.cells.iter().filter(|cell| cell == &&target).count()
}
// Useful for debugging.
pub fn _draw(&self) -> String {
let mut result = String::new();
for (index, cell) in self.cells.iter().enumerate() {
if index % self.line_length as usize == 0 {
result += "\n";
}
result += match cell {
Cell::Edge => "@",
Cell::Floor => ".",
Cell::Empty => "L",
Cell::Occupied => "#",
};
}
result
}
}