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
74819475ca
commit
5a7cd20ca6
3 changed files with 129 additions and 104 deletions
day-11
39
day-11/Cargo.lock
generated
39
day-11/Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -8,3 +8,4 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
indoc = "0.3"
|
||||
petgraph = "0.5"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue