diff --git a/day-11/Cargo.lock b/day-11/Cargo.lock index 5e329eb..097af7b 100644 --- a/day-11/Cargo.lock +++ b/day-11/Cargo.lock @@ -1,10 +1,39 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "day-11" version = "0.1.0" dependencies = [ "indoc", + "petgraph", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown", ] [[package]] @@ -30,6 +59,16 @@ dependencies = [ "unindent", ] +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" diff --git a/day-11/Cargo.toml b/day-11/Cargo.toml index 5cf3b01..115407c 100644 --- a/day-11/Cargo.toml +++ b/day-11/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" [dependencies] indoc = "0.3" +petgraph = "0.5" diff --git a/day-11/src/main.rs b/day-11/src/main.rs index 886261d..813bb24 100644 --- a/day-11/src/main.rs +++ b/day-11/src/main.rs @@ -1,5 +1,8 @@ use std::fs; +use petgraph::graph::{DiGraph, Graph}; +use petgraph::visit::IntoNodeIdentifiers; + fn main() -> Result<(), Box> { let input = fs::read_to_string("input")?; let layout = parse_layout(&input)?; @@ -11,6 +14,20 @@ fn main() -> Result<(), Box> { Ok(()) } +type Layout = DiGraph; + +#[derive(Copy, Clone, Debug)] +enum Direction { + NorthWest, + North, + NorthEast, + West, + East, + SouthWest, + South, + SouthEast, +} + #[derive(Copy, Clone, Debug, PartialEq)] enum Seating { Occupied, @@ -29,124 +46,92 @@ impl Seating { } } -fn parse_layout(input: &str) -> Result>, String> { - input +fn parse_layout(input: &str) -> Result { + let mut output: Layout = Graph::new(); + + let grid: Vec> = input .lines() - .map(|line| line.chars().map(Seating::new).collect()) - .collect() -} - -fn print_layout(layout: &Vec>) { - 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>, i: usize, j: usize) -> Vec> { - 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>, 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, - }) + .map(|line| { + line.chars() + .map(|c| Ok(output.add_node(Seating::new(c)?))) .collect() }) - .collect(); + .collect::>()?; - (layout, changes) + for i in 0..grid.len() { + for j in 0..grid[0].len() { + if let Some(node) = grid.get(i).and_then(|row| row.get(j)) { + for ((c, r), direction) in [ + ((0, 0), Direction::NorthWest), + ((0, 1), Direction::North), + ((0, 2), Direction::NorthEast), + ((1, 0), Direction::West), + ((1, 2), Direction::East), + ((2, 0), Direction::SouthWest), + ((2, 1), Direction::South), + ((2, 2), Direction::SouthEast), + ] + .iter() + { + if let (Some(c), Some(r)) = ((i + c).checked_sub(1), (j + r).checked_sub(1)) { + if let Some(target) = grid.get(c).and_then(|row| row.get(r)) { + output.add_edge(*node, *target, *direction); + } + } + } + } + } + } + + Ok(output) } -fn count_eventually_occupied_seats(layout: &Vec>) -> usize { - let mut loop_layout = layout.clone(); +fn count_eventually_occupied_seats(layout: &Layout) -> usize { + let mut layout = layout.clone(); loop { - let (res, changes) = simulate_people(&loop_layout); - loop_layout = res; + let mut changes = 0; + + layout = layout.map( + |node, seat| match seat { + Seating::Occupied => { + if layout + .neighbors(node) + .filter(|seat| layout[*seat] == Seating::Occupied) + .count() + >= 4 + { + changes += 1; + Seating::Empty + } else { + *seat + } + } + Seating::Empty => { + if !layout + .neighbors(node) + .any(|seat| layout[seat] == Seating::Occupied) + { + changes += 1; + Seating::Occupied + } else { + *seat + } + } + Seating::Floor => Seating::Floor, + }, + |_, direction| *direction, + ); if changes == 0 { break; } } - loop_layout - .iter() - .flatten() - .filter(|seat| **seat == Seating::Occupied) + layout + .node_identifiers() + .map(|id| layout[id]) + .filter(|seat| *seat == Seating::Occupied) .count() }