adventofcode-2020/day-11/src/main.rs

181 lines
4.5 KiB
Rust

use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = fs::read_to_string("input")?;
let layout = parse_layout(&input)?;
// Part 1
let result = count_eventually_occupied_seats(&layout);
println!("{}", result);
Ok(())
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Seating {
Occupied,
Empty,
Floor,
}
impl Seating {
fn new(symbol: char) -> Result<Self, String> {
match symbol {
'#' => Ok(Seating::Occupied),
'L' => Ok(Seating::Empty),
'.' => Ok(Seating::Floor),
rubbish => Err(format!("Found some rubbish on the floor, area must be clean to conserve human behavior: {}", rubbish))
}
}
}
fn parse_layout(input: &str) -> Result<Vec<Vec<Seating>>, String> {
input
.lines()
.map(|line| line.chars().map(Seating::new).collect())
.collect()
}
fn print_layout(layout: &Vec<Vec<Seating>>) {
let output: String = layout
.iter()
.map(|row| {
let mut row: String = row
.iter()
.map(|c| match c {
Seating::Occupied => '#',
Seating::Empty => 'L',
Seating::Floor => '.',
})
.collect();
row.push('\n');
row
})
.collect();
println!("{}", output);
}
fn get_adjacent(layout: &Vec<Vec<Seating>>, i: usize, j: usize) -> Vec<Option<&Seating>> {
let adjacent: [[usize; 2]; 8] = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 2],
[2, 0],
[2, 1],
[2, 2],
];
adjacent
.iter()
.map(|[nudge_row, nudge_col]| {
if let Some(row) = (i + nudge_row).checked_sub(1) {
if let Some(row) = layout.get(row) {
if let Some(col) = (j + nudge_col).checked_sub(1) {
row.get(col)
} else {
None
}
} else {
None
}
} else {
None
}
})
.collect()
}
fn simulate_people(layout: &Vec<Vec<Seating>>) -> (Vec<Vec<Seating>>, usize) {
let mut changes = 0;
let layout = layout
.iter()
.enumerate()
.map(|(i, row)| {
row.iter()
.enumerate()
.map(|(j, seat)| match seat {
Seating::Occupied => {
if get_adjacent(layout, i, j)
.iter()
.filter(|seat| **seat == Some(&Seating::Occupied))
.count()
>= 4
{
changes += 1;
Seating::Empty
} else {
Seating::Occupied
}
}
Seating::Empty => {
if get_adjacent(layout, i, j)
.iter()
.any(|seat| *seat == Some(&Seating::Occupied))
{
Seating::Empty
} else {
changes += 1;
Seating::Occupied
}
}
Seating::Floor => Seating::Floor,
})
.collect()
})
.collect();
(layout, changes)
}
fn count_eventually_occupied_seats(layout: &Vec<Vec<Seating>>) -> usize {
let mut loop_layout = layout.clone();
loop {
let (res, changes) = simulate_people(&loop_layout);
loop_layout = res;
if changes == 0 {
break;
}
}
loop_layout
.iter()
.flatten()
.filter(|seat| **seat == Seating::Occupied)
.count()
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn test_simple() -> Result<(), Box<dyn std::error::Error>> {
let input = indoc!(
"L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL
"
);
let layout = parse_layout(&input)?;
let result = count_eventually_occupied_seats(&layout);
assert_eq!(result, 37);
Ok(())
}
}