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 aoc23::read;
use chumsky::prelude::*; use chumsky::prelude::*;
use std::{ use std::collections::{HashMap, HashSet, VecDeque};
collections::{HashMap, HashSet},
sync::{
atomic::{AtomicBool, Ordering},
Mutex
},
thread,
time::Duration
};
#[derive(Clone, Copy, Debug)]
enum Instruction { enum Instruction {
Left, Left,
Right Right
@ -27,7 +18,7 @@ impl Instruction {
} }
} }
#[derive(Clone, Debug)] #[derive(Debug)]
struct Node { struct Node {
left: String, left: String,
right: String right: String
@ -51,7 +42,7 @@ impl Node {
} }
} }
#[derive(Clone, Debug)] #[derive(Debug)]
struct Network { struct Network {
nodes: HashMap<String, Node> nodes: HashMap<String, Node>
} }
@ -92,15 +83,93 @@ fn parser() -> impl Parser<char, (Vec<Instruction>, Network), Error = Simple<cha
.then_ignore(end()) .then_ignore(end())
} }
static STOP: AtomicBool = AtomicBool::new(false); #[derive(Copy, Clone, Debug)]
static mut RESULTS: Vec<Mutex<Vec<usize>>> = Vec::new(); 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<()> { fn main() -> anyhow::Result<()> {
let (instructions, network) = read("inputs/day8.txt", parser())?; 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 curr_node = "AAA";
// let mut i = 0; // let mut i = 0;
// let mut steps = 0; // let mut steps = 0_u128;
// while curr_node != "ZZZ" { // while curr_node != "ZZZ" {
// curr_node = match instructions[i] { // curr_node = match instructions[i] {
// Instruction::Left => network.left(curr_node), // Instruction::Left => network.left(curr_node),
@ -111,84 +180,39 @@ fn main() -> anyhow::Result<()> {
// } // }
// println!("{steps}"); // println!("{steps}");
let curr_nodes: HashSet<&str> = network let mut ghosts: Vec<Ghost<'_>> = network
.nodes .nodes
.keys() .keys()
.map(|node| node.as_str())
.filter(|node| node.ends_with('A')) .filter(|node| node.ends_with('A'))
.map(|node| Ghost::new(node))
.collect(); .collect();
dbg!(curr_nodes.len()); let mut progress = 0;
loop {
for (thread_index, node) in curr_nodes if ghosts[0].steps > 0
&& ghosts
.iter() .iter()
.map(|node| String::from(*node)) .skip(1)
.enumerate() .all(|ghost| ghost.steps == ghosts[0].steps)
{ {
unsafe { println!("{}", ghosts[0].steps);
RESULTS.push(Mutex::new(Vec::new())); 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 (ghost_index, ghost) = ghosts
// let mut steps = 0; .iter()
// while curr_nodes.iter().any(|node| !node.ends_with('Z')) { .enumerate()
// curr_nodes = curr_nodes .min_by_key(|(_, ghost)| ghost.steps)
// .into_iter() .unwrap();
// .map(|node| match instructions[i] { if ghost.steps - progress >= 1_000_000_000_000 {
// Instruction::Left => network.left(node), progress = ghost.steps;
// Instruction::Right => network.right(node) eprintln!("progress: {progress}");
// }) }
// .collect();
// i = (i + 1) % instructions.len(); // eprint!("{ghost:?}\t -> \t");
// steps += 1; let ghost = dp[&(ghost.node, ghost.i)];
// if steps % 10000 == 0 { // eprintln!("{ghost:?}");
// dbg!(steps); ghosts[ghost_index].advance(ghost);
// } }
// }
// println!("{steps}");
Ok(()) Ok(())
} }