Re-implement day 11.1 with graphs

This is to have a potentially nicer way to iterate neighbours, as well
as recursive directions.

While the former is neater than it was previously, the latter sadly
wasn't possible after all, and it's very inefficient.
This commit is contained in:
Tristan Daniël Maat 2020-12-12 21:42:16 +00:00
parent 74819475ca
commit 5a7cd20ca6
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())
.map(|line| {
line.chars()
.map(|c| Ok(output.add_node(Seating::new(c)?)))
.collect()
})
.collect::<Result<_, String>>()?;
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 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();
fn count_eventually_occupied_seats(layout: &Layout) -> usize {
let mut layout = layout.clone();
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) {
loop {
let mut changes = 0;
let layout = layout
.iter()
.enumerate()
.map(|(i, row)| {
row.iter()
.enumerate()
.map(|(j, seat)| match seat {
layout = layout.map(
|node, seat| match seat {
Seating::Occupied => {
if get_adjacent(layout, i, j)
.iter()
.filter(|seat| **seat == Some(&Seating::Occupied))
if layout
.neighbors(node)
.filter(|seat| layout[*seat] == Seating::Occupied)
.count()
>= 4
{
changes += 1;
Seating::Empty
} else {
Seating::Occupied
*seat
}
}
Seating::Empty => {
if get_adjacent(layout, i, j)
.iter()
.any(|seat| *seat == Some(&Seating::Occupied))
if !layout
.neighbors(node)
.any(|seat| layout[seat] == Seating::Occupied)
{
Seating::Empty
} else {
changes += 1;
Seating::Occupied
} else {
*seat
}
}
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;
},
|_, 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()
}