aoc-2021-rust

git clone https://git.sinitax.com/sinitax/aoc-2021-rust
Log | Files | Refs | README | sfeed.txt

commit af7aee76aea2b6a7bfd2f2d49d9c1f2487672c92
parent 67a315a49eb5f6a607c23a7c2af5116914ad1bc1
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed, 19 Apr 2023 16:46:29 +0200

[Day 23] Enable optimizations and use binaryheap pq

Diffstat:
Msrc/23/src/main.rs | 186++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/Makefile | 16++++++++++++++--
2 files changed, 142 insertions(+), 60 deletions(-)

diff --git a/src/23/src/main.rs b/src/23/src/main.rs @@ -1,5 +1,5 @@ -use priority_queue::PriorityQueue; -use std::{collections::HashMap, hash::Hash}; +use std::cmp::Reverse; +use std::{collections::HashMap, collections::BinaryHeap, hash::Hash}; macro_rules! idx2slot { ( $cfg:expr, $t:expr ) => { @@ -13,9 +13,11 @@ struct Pos { y: isize } -#[derive(PartialEq,Eq,Hash,Clone)] +#[derive(Clone)] struct DJKState { - agents: [[Pos; 4]; 4] + agents: [[Pos; 4]; 4], + cost: usize, + total: usize } #[derive(Clone)] @@ -36,13 +38,66 @@ struct MapConfig { fixed: [[bool; 4]; 4] } +// struct FastHasher(u64); + static AGENT_SYMBS: [char; 4] = ['A', 'B', 'C', 'D']; -static MOVE_COST: [isize; 4] = [1, 10, 100, 1000]; +static MOVE_COST: [usize; 4] = [1, 10, 100, 1000]; + +// impl Default for FastHasher { +// fn default() -> Self { +// return FastHasher(0xcbf29ce484222325); +// } +// } +// +// impl Hasher for FastHasher { +// #[inline] +// fn finish(&self) -> u64 { +// self.0 +// } +// +// #[inline] +// fn write(&mut self, bytes: &[u8]) { +// let FastHasher(mut hash) = *self; +// +// for byte in bytes.iter() { +// hash = hash ^ (*byte as u64); +// hash = hash.wrapping_mul(0x100000001b3); +// } +// +// *self = FastHasher(hash); +// } +// } impl Pos { - fn dist_to(&self, other: &Self) -> isize { - return isize::abs(self.x - other.x) - + isize::abs(self.y - other.y); + fn dist_to(&self, other: &Self) -> usize { + return (isize::abs(self.x - other.x) + + isize::abs(self.y - other.y)) as usize; + } +} + +impl Eq for DJKState { } + +impl PartialEq for DJKState { + fn eq(&self, other: &Self) -> bool { + return self.total == other.total; + } +} + +impl Ord for DJKState { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + Reverse(self.total).cmp(&Reverse(other.total)) + } +} + +impl PartialOrd for DJKState { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(Reverse(self.total).cmp(&Reverse(other.total))) + } +} + +impl Hash for DJKState { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.agents.hash(state) } } @@ -105,15 +160,15 @@ fn parse_input(input: &str) -> (HashMap<Pos,char>, MapConfig) { slot_y: Range { min: 1, max: y - 1 }, agent_cnt: (y - 3) as usize, type_cnt: 4, - initial: DJKState { agents: [ [Pos { x: 0, y: 0 }; 4]; 4 ] }, + initial: DJKState { agents: [ [Pos { x: 0, y: 0 }; 4]; 4 ], cost: 0, total: 0 }, fixed: [ [false; 4]; 4 ], }; return (map, cfg); } -fn heuristic(cfg: &MapConfig, state: &DJKState) -> isize { - let mut cost: isize = 0; +fn heuristic(cfg: &MapConfig, state: &DJKState) -> usize { + let mut cost: usize = 0; for t in 0..cfg.type_cnt { for i in 0..cfg.agent_cnt { let agent = state.agents[t][i]; @@ -127,9 +182,9 @@ fn heuristic(cfg: &MapConfig, state: &DJKState) -> isize { } fn try_move_out(map: &HashMap<Pos,char>, cfg: &MapConfig, - costmap: &mut HashMap<DJKState,isize>, - active: &mut PriorityQueue<DJKState,isize>, - state: &DJKState, cost: isize, at: usize, ai: usize) { + costmap: &mut HashMap<[[Pos; 4]; 4],usize>, + active: &mut BinaryHeap<DJKState>, + state: &DJKState, at: usize, ai: usize) { let cur = &state.agents[at][ai]; let mut minx: isize = cfg.hallway.min; @@ -159,28 +214,34 @@ fn try_move_out(map: &HashMap<Pos,char>, cfg: &MapConfig, let mut nstate = state.clone(); nstate.agents[at][ai].x = x; nstate.agents[at][ai].y = cfg.hallway_y; - let ncost_g = cost + MOVE_COST[at] + nstate.cost = state.cost + MOVE_COST[at] * state.agents[at][ai].dist_to(&nstate.agents[at][ai]); - let ncost_f = ncost_g + heuristic(cfg, &nstate); - let res = costmap.get_mut(&nstate); - if res.is_none() || *res.unwrap() > ncost_g { + let res = costmap.get_mut(&nstate.agents); + if res.is_none() || **res.as_ref().unwrap() > nstate.cost { + nstate.total = nstate.cost + heuristic(cfg, &nstate); + if aoc::get_debug() > 1 { - aoc::debugln!("MOVE OUT {} {}", ncost_g, ncost_f); + aoc::debugln!("MOVE OUT {} {}", nstate.cost, nstate.total); nstate.print(cfg, map); aoc::debugln!(""); } - costmap.insert(nstate.clone(), ncost_g); - active.push_increase(nstate, -ncost_f); + if res.is_none() { + costmap.insert(nstate.agents.clone(), nstate.cost); + } else { + *res.unwrap() = nstate.cost; + } + + active.push(nstate); } } } fn try_move_in(map: &HashMap<Pos,char>, cfg: &MapConfig, - costmap: &mut HashMap<DJKState,isize>, - active: &mut PriorityQueue<DJKState,isize>, - state: &DJKState, cost: isize, at: usize, ai: usize) { + costmap: &mut HashMap<[[Pos; 4]; 4],usize>, + active: &mut BinaryHeap<DJKState>, + state: &DJKState, at: usize, ai: usize) { let mut slot_free_y = cfg.slot_y.max-1; for t in 0..cfg.type_cnt { @@ -205,47 +266,54 @@ fn try_move_in(map: &HashMap<Pos,char>, cfg: &MapConfig, let mut nstate = state.clone(); nstate.agents[at][ai].x = idx2slot!(cfg, at); nstate.agents[at][ai].y = slot_free_y; - let ncost_g = cost + MOVE_COST[at] + nstate.cost = state.cost + MOVE_COST[at] * state.agents[at][ai].dist_to(&nstate.agents[at][ai]); - let ncost_f = ncost_g + heuristic(cfg, &nstate); - let res = costmap.get_mut(&nstate); - if res.is_none() || *res.unwrap() > ncost_g { + let res = costmap.get_mut(&nstate.agents); + if res.is_none() || **res.as_ref().unwrap() > nstate.cost { + nstate.total = nstate.cost + heuristic(cfg, &nstate); + if aoc::get_debug() > 1 { - aoc::debugln!("MOVE IN {} {}", ncost_g, ncost_f); + aoc::debugln!("MOVE IN {} {}", nstate.cost, nstate.total); nstate.print(cfg, map); aoc::debugln!(""); } - costmap.insert(nstate.clone(), ncost_g); - active.push_increase(nstate, -ncost_f); + if res.is_none() { + costmap.insert(nstate.agents.clone(), nstate.cost); + } else { + *res.unwrap() = nstate.cost; + } + + active.push(nstate); } } fn try_move(map: &HashMap<Pos,char>, cfg: &MapConfig, - costmap: &mut HashMap<DJKState,isize>, - active: &mut PriorityQueue<DJKState,isize>, - state: &DJKState, cost: isize, at: usize, ai: usize) -> bool { + costmap: &mut HashMap<[[Pos; 4]; 4],usize>, + active: &mut BinaryHeap<DJKState>, + state: &DJKState, at: usize, ai: usize) { if !state.moved_in(at, ai, cfg) { if !state.moved_out(at, ai, cfg) { - try_move_out(map, cfg, costmap, active, state, cost, at, ai); + try_move_out(map, cfg, costmap, active, state, at, ai); } else { - try_move_in(map, cfg, costmap, active, state, cost, at, ai); + try_move_in(map, cfg, costmap, active, state, at, ai); } - return false; - } else { - for t in 0..cfg.type_cnt { - for i in 0..cfg.agent_cnt { - if !state.moved_in(t, i, cfg) { return false; } - } + } +} + +fn all_moved_in(state: &DJKState, cfg: &MapConfig) -> bool { + for t in 0..cfg.type_cnt { + for i in 0..cfg.agent_cnt { + if !state.moved_in(t, i, cfg) { return false; } } - return true; } + return true; } fn organize(map: &HashMap<Pos,char>, cfg: &mut MapConfig) -> Option<usize> { - let mut active: PriorityQueue<DJKState,isize> = PriorityQueue::with_capacity(100000); - let mut costmap: HashMap<DJKState,isize> = HashMap::with_capacity(100000); + let mut active = BinaryHeap::with_capacity(10000000); + let mut costmap: HashMap<[[Pos; 4]; 4],usize> = HashMap::with_capacity(10000000); let mut counts: [usize; 4] = [0; 4]; for (p,c) in map.iter() { @@ -262,13 +330,14 @@ fn organize(map: &HashMap<Pos,char>, cfg: &mut MapConfig) -> Option<usize> { counts[t] += 1; } } - active.push(cfg.initial.clone(), -heuristic(&cfg, &cfg.initial)); - costmap.insert(cfg.initial.clone(), 0); + cfg.initial.cost = 0; + cfg.initial.total = heuristic(&cfg, &cfg.initial); + active.push(cfg.initial.clone()); + costmap.insert(cfg.initial.agents.clone(), 0); let mut advanced: usize = 0; while !active.is_empty() { - let (state, _) = active.pop().unwrap(); - let cost_g = *costmap.get(&state).unwrap(); + let state = active.pop().unwrap(); advanced += 1; @@ -278,16 +347,16 @@ fn organize(map: &HashMap<Pos,char>, cfg: &mut MapConfig) -> Option<usize> { state.print(&cfg, &map); aoc::debugln!(""); } - aoc::debugln!("{}", cost_g); + aoc::debugln!("{}", state.cost); + + if all_moved_in(&state, cfg) { + return Some(state.cost as usize); + } for t in 0..cfg.type_cnt { for i in 0..cfg.agent_cnt { - let done = try_move(&map, &cfg, &mut costmap, - &mut active, &state, cost_g, t, i); - if done { - aoc::debugln!("ANSWER?"); - return Some(cost_g as usize); - } + try_move(&map, &cfg, &mut costmap, + &mut active, &state, t, i); } } } @@ -304,13 +373,13 @@ fn extend_map(s_map: &HashMap<Pos,char>, s_cfg: &MapConfig) let mut map: HashMap<Pos,char> = HashMap::new(); for (y,l) in insert.iter().enumerate() { for (x,c) in l.chars().enumerate() { - let p = Pos { x: x as isize, y: s_cfg.hallway_y + 1 + (y as isize) }; + let p = Pos { x: x as isize, y: s_cfg.hallway_y + 2 + (y as isize) }; map.insert(p, c); } } for (p,&c) in s_map.iter() { if p.x >= s_cfg.slot_x.min - 1 && p.x < s_cfg.slot_x.max + 1 - && p.y >= s_cfg.hallway_y + 1 { + && p.y > s_cfg.hallway_y + 1 { map.insert(Pos { x: p.x, y: p.y + 2 }, c); } else { map.insert(p.clone(), c); @@ -319,6 +388,7 @@ fn extend_map(s_map: &HashMap<Pos,char>, s_cfg: &MapConfig) let mut cfg = s_cfg.clone(); cfg.slot_y.max += 2; + cfg.agent_cnt += 2; return (map, cfg); } diff --git a/src/Makefile b/src/Makefile @@ -12,8 +12,20 @@ clean:: $1/clean .PHONY: $1/run $1/clean -$1/main: $1/src/main.rs - cd $1 && cargo build && cp ./target/debug/day$1 main +$1/target/debug/day$1: $1/src/main.rs + @cd $1 && cargo build + +$1/target/release/day$1: $1/src/main.rs + @cd $1 && cargo build -r + +$1/main: $1/target/release/day$1 + @cp $1/target/release/day$1 $1/main + +$1/debug1: $1/target/debug/day$1 + @cd $1 && time ./target/debug/day$1 1 + +$1/debug2: $1/target/debug/day$1 + @cd $1 && time ./target/debug/day$1 2 $1/run1: $1/main @cd $1 && time ./main 1