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 6dd5d1a04c
commit 279e9d8cc7
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()
}