day 8 part 2 dp

This commit is contained in:
Dominic 2023-12-08 22:15:07 +01:00
parent 40b252e393
commit 094d48df84
Signed by: msrd0
GPG key ID: DCC8C247452E98F9

View file

@ -1,18 +1,9 @@
#![forbid(elided_lifetimes_in_paths)]
#![forbid(elided_lifetimes_in_paths, unsafe_code)]
use aoc23::read;
use chumsky::prelude::*;
use std::{
collections::{HashMap, HashSet},
sync::{
atomic::{AtomicBool, Ordering},
Mutex
},
thread,
time::Duration
};
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Clone, Copy, Debug)]
enum Instruction {
Left,
Right
@ -27,7 +18,7 @@ impl Instruction {
}
}
#[derive(Clone, Debug)]
#[derive(Debug)]
struct Node {
left: String,
right: String
@ -51,7 +42,7 @@ impl Node {
}
}
#[derive(Clone, Debug)]
#[derive(Debug)]
struct Network {
nodes: HashMap<String, Node>
}
@ -92,15 +83,93 @@ fn parser() -> impl Parser<char, (Vec<Instruction>, Network), Error = Simple<cha
.then_ignore(end())
}
static STOP: AtomicBool = AtomicBool::new(false);
static mut RESULTS: Vec<Mutex<Vec<usize>>> = Vec::new();
#[derive(Copy, Clone, Debug)]
struct Ghost<'a> {
node: &'a str,
i: usize,
steps: u128
}
impl<'a> Ghost<'a> {
fn new(node: &'a str) -> Self {
Self {
node,
i: 0,
steps: 0
}
}
fn advance(&mut self, ghost: Ghost<'a>) {
self.node = ghost.node;
self.i = ghost.i;
self.steps += ghost.steps;
}
}
fn main() -> anyhow::Result<()> {
let (instructions, network) = read("inputs/day8.txt", parser())?;
// build a lookup map of left/right predecessors
let mut left_pred: HashMap<&str, Vec<&str>> = HashMap::new();
let mut right_pred: HashMap<&str, Vec<&str>> = HashMap::new();
for (node, next) in &network.nodes {
left_pred.entry(&next.left).or_default().push(node);
right_pred.entry(&next.right).or_default().push(node);
}
eprintln!("Built pred lookup map");
// build a lookup map from instruction position and node with the length to the next
// finish node
let mut dp: HashMap<(&str, usize), Ghost<'_>> = HashMap::new();
let mut q: VecDeque<(&str, usize)> = VecDeque::new();
for node in network
.nodes
.keys()
.map(|node| node.as_str())
.filter(|node| node.ends_with('Z'))
{
for i in 0 .. instructions.len() {
dp.insert((node, i), Ghost { node, i, steps: 0 });
q.push_back((node, i));
}
}
while let Some((node, i)) = q.pop_front() {
let ghost = dp[&(node, i)];
let i_pred = match i {
0 => instructions.len() - 1,
i => i - 1
};
let pred = match instructions[i_pred] {
Instruction::Left => left_pred.get(node),
Instruction::Right => right_pred.get(node)
};
for node_pred in pred.into_iter().flatten() {
let new = (*node_pred, i_pred);
#[allow(clippy::map_entry)] // lint is opinionated garbage
if !dp.contains_key(&new) {
dp.insert(new, Ghost {
node: ghost.node,
i: ghost.i,
steps: ghost.steps + 1
});
q.push_back(new);
if dp.len() % 1_000_000 == 0 {
dbg!(dp.len());
}
}
}
if ghost.steps == 0 {
dp.remove(&(node, i));
}
}
eprintln!("Built ghost lookup map");
// for (key, value) in &dp {
// eprintln!("\t{key:?}\t = \t{value:?}");
// }
// let mut curr_node = "AAA";
// let mut i = 0;
// let mut steps = 0;
// let mut steps = 0_u128;
// while curr_node != "ZZZ" {
// curr_node = match instructions[i] {
// Instruction::Left => network.left(curr_node),
@ -111,84 +180,39 @@ fn main() -> anyhow::Result<()> {
// }
// println!("{steps}");
let curr_nodes: HashSet<&str> = network
let mut ghosts: Vec<Ghost<'_>> = network
.nodes
.keys()
.map(|node| node.as_str())
.filter(|node| node.ends_with('A'))
.map(|node| Ghost::new(node))
.collect();
dbg!(curr_nodes.len());
for (thread_index, node) in curr_nodes
.iter()
.map(|node| String::from(*node))
.enumerate()
{
unsafe {
RESULTS.push(Mutex::new(Vec::new()));
let mut progress = 0;
loop {
if ghosts[0].steps > 0
&& ghosts
.iter()
.skip(1)
.all(|ghost| ghost.steps == ghosts[0].steps)
{
println!("{}", ghosts[0].steps);
break;
}
let network = network.clone();
let instructions = instructions.clone();
thread::spawn(move || {
let mut curr_node = node.as_str();
let mut i = 0;
let mut steps = 0;
while !STOP.load(Ordering::Relaxed) {
curr_node = match instructions[i] {
Instruction::Left => network.left(curr_node),
Instruction::Right => network.right(curr_node)
};
i = (i + 1) % instructions.len();
steps += 1;
if steps % 1_000_000 == 0 {
dbg!(steps);
}
if curr_node.ends_with('Z') {
unsafe {
RESULTS[thread_index].lock().unwrap().push(steps);
}
}
}
});
}
'outer: loop {
thread::sleep(Duration::from_secs(1));
unsafe {
let res0 = RESULTS[0].lock().unwrap();
'i: for i in 0 .. res0.len() {
't: for t in 1 .. RESULTS.len() {
let rest = RESULTS[t].lock().unwrap();
for j in 0 .. rest.len() {
if res0[i] == rest[j] {
continue 't;
}
}
continue 'i;
}
println!("{}", res0[i]);
STOP.store(true, Ordering::Relaxed);
break 'outer;
}
}
}
// let mut i = 0;
// let mut steps = 0;
// while curr_nodes.iter().any(|node| !node.ends_with('Z')) {
// curr_nodes = curr_nodes
// .into_iter()
// .map(|node| match instructions[i] {
// Instruction::Left => network.left(node),
// Instruction::Right => network.right(node)
// })
// .collect();
// i = (i + 1) % instructions.len();
// steps += 1;
// if steps % 10000 == 0 {
// dbg!(steps);
// }
// }
// println!("{steps}");
let (ghost_index, ghost) = ghosts
.iter()
.enumerate()
.min_by_key(|(_, ghost)| ghost.steps)
.unwrap();
if ghost.steps - progress >= 1_000_000_000_000 {
progress = ghost.steps;
eprintln!("progress: {progress}");
}
// eprint!("{ghost:?}\t -> \t");
let ghost = dp[&(ghost.node, ghost.i)];
// eprintln!("{ghost:?}");
ghosts[ghost_index].advance(ghost);
}
Ok(())
}