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:
parent
3ec82e117e
commit
3c3ef92ff8
39
day-11/Cargo.lock
generated
39
day-11/Cargo.lock
generated
|
@ -1,10 +1,39 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# 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]]
|
[[package]]
|
||||||
name = "day-11"
|
name = "day-11"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc",
|
"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]]
|
[[package]]
|
||||||
|
@ -30,6 +59,16 @@ dependencies = [
|
||||||
"unindent",
|
"unindent",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "petgraph"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset",
|
||||||
|
"indexmap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.19"
|
version = "0.5.19"
|
||||||
|
|
|
@ -8,3 +8,4 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
indoc = "0.3"
|
indoc = "0.3"
|
||||||
|
petgraph = "0.5"
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
use petgraph::graph::{DiGraph, Graph};
|
||||||
|
use petgraph::visit::IntoNodeIdentifiers;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = fs::read_to_string("input")?;
|
let input = fs::read_to_string("input")?;
|
||||||
let layout = parse_layout(&input)?;
|
let layout = parse_layout(&input)?;
|
||||||
|
@ -11,6 +14,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Ok(())
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
enum Seating {
|
enum Seating {
|
||||||
Occupied,
|
Occupied,
|
||||||
|
@ -29,124 +46,92 @@ impl Seating {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_layout(input: &str) -> Result<Vec<Vec<Seating>>, String> {
|
fn parse_layout(input: &str) -> Result<Layout, String> {
|
||||||
input
|
let mut output: Layout = Graph::new();
|
||||||
|
|
||||||
|
let grid: Vec<Vec<_>> = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.chars().map(Seating::new).collect())
|
.map(|line| {
|
||||||
.collect()
|
line.chars()
|
||||||
}
|
.map(|c| Ok(output.add_node(Seating::new(c)?)))
|
||||||
|
|
||||||
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()
|
||||||
})
|
})
|
||||||
.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 {
|
fn count_eventually_occupied_seats(layout: &Layout) -> usize {
|
||||||
let mut loop_layout = layout.clone();
|
let mut layout = layout.clone();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (res, changes) = simulate_people(&loop_layout);
|
let mut changes = 0;
|
||||||
loop_layout = res;
|
|
||||||
|
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 {
|
if changes == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_layout
|
layout
|
||||||
.iter()
|
.node_identifiers()
|
||||||
.flatten()
|
.map(|id| layout[id])
|
||||||
.filter(|seat| **seat == Seating::Occupied)
|
.filter(|seat| *seat == Seating::Occupied)
|
||||||
.count()
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue