Compare commits

...

1 Commits

Author SHA1 Message Date
Tristan Daniël Maat 279e9d8cc7
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.
2020-12-12 23:49:27 +00:00
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()
}