Re-implement day 11.1 with graphs

Tristan Daniël Maat 2020-12-12 21:42:16 +00:00
parent 6dd5d1a04c
commit d8a562145a
Signed by: tlater
GPG Key ID: 49670FD774E43268
3 changed files with 129 additions and 104 deletions

39
day-11/Cargo.lock generated
View File

@ -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"

View File

@ -8,3 +8,4 @@ edition = "2018"
[dependencies]
indoc = "0.3"
petgraph = "0.5"

View File

@ -1,5 +1,8 @@
use std::fs;
use petgraph::graph::{DiGraph, Graph};
use petgraph::visit::IntoNodeIdentifiers;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = fs::read_to_string("input")?;
let layout = parse_layout(&input)?;
@ -11,6 +14,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
type Layout = DiGraph<Seating, Direction>;
#[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<Vec<Vec<Seating>>, String> {
input
fn parse_layout(input: &str) -> Result<Layout, String> {
let mut output: Layout = Graph::new();
let grid: Vec<Vec<_>> = 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,
})
.map(|line| {
line.chars()
.map(|c| Ok(output.add_node(Seating::new(c)?)))
.collect()
})
.collect();
.collect::<Result<_, String>>()?;
(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<Vec<Seating>>) -> 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()
}