Add bucket queue and PriorityQueueByKey
This commit is contained in:
parent
ffe51bede9
commit
b8f83ec4eb
7 changed files with 245 additions and 21 deletions
|
|
@ -4,8 +4,9 @@ use factorio_blueprint_generator::factory::{FactoryGraph, generate_factory};
|
||||||
use factorio_core::{prelude::*, visualize::Visualize};
|
use factorio_core::{prelude::*, visualize::Visualize};
|
||||||
use factorio_graph::{
|
use factorio_graph::{
|
||||||
priority_queue::{
|
priority_queue::{
|
||||||
PriorityQueue,
|
ByKey, PriorityQueue,
|
||||||
binary_heap::{BinaryHeap, FastBinaryHeap},
|
binary_heap::{BinaryHeap, FastBinaryHeap},
|
||||||
|
bucket_queue::BucketQueue,
|
||||||
},
|
},
|
||||||
wheighted_graph::shortest_path::QueueObject,
|
wheighted_graph::shortest_path::QueueObject,
|
||||||
};
|
};
|
||||||
|
|
@ -19,7 +20,7 @@ use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
#[arg(default_value_t = 0)]
|
#[arg(short, long, default_value_t = 0)]
|
||||||
seed: u64,
|
seed: u64,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
|
|
@ -44,6 +45,7 @@ enum Tracing {
|
||||||
enum PathfinderArg {
|
enum PathfinderArg {
|
||||||
CaFbh,
|
CaFbh,
|
||||||
CaBh,
|
CaBh,
|
||||||
|
CaBq,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -92,7 +94,7 @@ where
|
||||||
let p = ConflictAvoidance {
|
let p = ConflictAvoidance {
|
||||||
timeout: Some(std::time::Duration::from_millis(100)),
|
timeout: Some(std::time::Duration::from_millis(100)),
|
||||||
priority_queue: std::marker::PhantomData::<
|
priority_queue: std::marker::PhantomData::<
|
||||||
FastBinaryHeap<QueueObject<(Position, Direction)>>,
|
FastBinaryHeap<ByKey<QueueObject<(Position, Direction)>>>,
|
||||||
>,
|
>,
|
||||||
};
|
};
|
||||||
execute::<_, _>(args, l, p);
|
execute::<_, _>(args, l, p);
|
||||||
|
|
@ -101,7 +103,16 @@ where
|
||||||
let p = ConflictAvoidance {
|
let p = ConflictAvoidance {
|
||||||
timeout: Some(std::time::Duration::from_millis(100)),
|
timeout: Some(std::time::Duration::from_millis(100)),
|
||||||
priority_queue: std::marker::PhantomData::<
|
priority_queue: std::marker::PhantomData::<
|
||||||
BinaryHeap<QueueObject<(Position, Direction)>>,
|
BinaryHeap<ByKey<QueueObject<(Position, Direction)>>>,
|
||||||
|
>,
|
||||||
|
};
|
||||||
|
execute::<_, _>(args, l, p);
|
||||||
|
}
|
||||||
|
PathfinderArg::CaBq => {
|
||||||
|
let p = ConflictAvoidance {
|
||||||
|
timeout: Some(std::time::Duration::from_millis(100)),
|
||||||
|
priority_queue: std::marker::PhantomData::<
|
||||||
|
BucketQueue<QueueObject<(Position, Direction)>>,
|
||||||
>,
|
>,
|
||||||
};
|
};
|
||||||
execute::<_, _>(args, l, p);
|
execute::<_, _>(args, l, p);
|
||||||
|
|
|
||||||
147
factorio-graph/src/priority_queue/bucket_queue.rs
Normal file
147
factorio-graph/src/priority_queue/bucket_queue.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
use super::{PriorityQueue, PriorityQueueByKey};
|
||||||
|
|
||||||
|
pub struct BucketQueue<Item> {
|
||||||
|
min: i64,
|
||||||
|
data: VecDeque<Vec<(usize, Item)>>,
|
||||||
|
map: HashMap<usize, (i64, usize)>,
|
||||||
|
nextfree: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> std::fmt::Debug for BucketQueue<Item> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
struct DebugWrapper2<'a, I>(&'a Vec<(usize, I)>);
|
||||||
|
impl<'a, I> std::fmt::Debug for DebugWrapper2<'a, I> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entries(self.0.iter().map(|v| v.0)).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct DebugWrapper<'a, I>(&'a VecDeque<Vec<(usize, I)>>);
|
||||||
|
impl<'a, I> std::fmt::Debug for DebugWrapper<'a, I> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(self.0.iter().map(|v| DebugWrapper2(v)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.debug_struct("BucketQueue")
|
||||||
|
.field("min", &self.min)
|
||||||
|
.field("data", &DebugWrapper::<Item>(&self.data))
|
||||||
|
.field("map", &self.map)
|
||||||
|
.field("nextfree", &self.nextfree)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BucketQueueHandle(usize);
|
||||||
|
|
||||||
|
impl<Item> PriorityQueueByKey<Item> for BucketQueue<Item>
|
||||||
|
where
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
type Handle = BucketQueueHandle;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
min: 0,
|
||||||
|
data: VecDeque::new(),
|
||||||
|
map: HashMap::new(),
|
||||||
|
nextfree: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, item: Item) -> Self::Handle {
|
||||||
|
self.nextfree += 1;
|
||||||
|
let handle = BucketQueueHandle(self.nextfree - 1);
|
||||||
|
|
||||||
|
self.insert_with_handle(item, &handle);
|
||||||
|
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_min(&mut self) -> Option<Item> {
|
||||||
|
if let Some(bucket) = self.data.get_mut(0) {
|
||||||
|
if let Some((i, item)) = bucket.pop() {
|
||||||
|
self.map.remove(&i);
|
||||||
|
Some(item)
|
||||||
|
} else {
|
||||||
|
self.data.pop_front();
|
||||||
|
self.min += 1;
|
||||||
|
self.pop_min()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item)) {
|
||||||
|
self.update_key(handle, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item)) {
|
||||||
|
self.update_key(handle, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> BucketQueue<Item>
|
||||||
|
where
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
fn get_index(&self, i: i64) -> i64 {
|
||||||
|
i - self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_with_handle(&mut self, item: Item, handle: &BucketQueueHandle) {
|
||||||
|
let value = (&item).into();
|
||||||
|
if self.data.is_empty() {
|
||||||
|
self.min = value;
|
||||||
|
self.map.insert(handle.0, (value, 0));
|
||||||
|
self.data.push_back(vec![(handle.0, item)]);
|
||||||
|
} else {
|
||||||
|
let index = self.get_index(value);
|
||||||
|
|
||||||
|
if index < 0 {
|
||||||
|
self.min = value;
|
||||||
|
for _ in 0..index.abs() {
|
||||||
|
self.data.push_front(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.map.insert(handle.0, (value, 0));
|
||||||
|
self.data.get_mut(0).unwrap().push((handle.0, item));
|
||||||
|
} else {
|
||||||
|
while index as usize >= self.data.len() {
|
||||||
|
self.data.push_back(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.map.insert(
|
||||||
|
handle.0,
|
||||||
|
(value, self.data.get(index as usize).unwrap().len()),
|
||||||
|
);
|
||||||
|
self.data
|
||||||
|
.get_mut(index as usize)
|
||||||
|
.unwrap()
|
||||||
|
.push((handle.0, item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, handle: &BucketQueueHandle) -> Item {
|
||||||
|
let (i, o) = self.map.remove(&handle.0).unwrap();
|
||||||
|
let index = self.get_index(i);
|
||||||
|
|
||||||
|
let bucket = self.data.get_mut(index as usize).unwrap();
|
||||||
|
let item = bucket.swap_remove(o).1;
|
||||||
|
|
||||||
|
if let Some((swapped, _)) = bucket.get(o) {
|
||||||
|
self.map.insert(*swapped, (i, o));
|
||||||
|
}
|
||||||
|
|
||||||
|
item
|
||||||
|
}
|
||||||
|
fn update_key(&mut self, handle: &BucketQueueHandle, f: impl Fn(&mut Item)) {
|
||||||
|
let mut item = self.remove(handle);
|
||||||
|
f(&mut item);
|
||||||
|
self.insert_with_handle(item, handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod binary_heap;
|
pub mod binary_heap;
|
||||||
|
pub mod bucket_queue;
|
||||||
pub mod fibonacci_heap;
|
pub mod fibonacci_heap;
|
||||||
|
|
||||||
pub trait PriorityQueue<Item>
|
pub trait PriorityQueue<Item>
|
||||||
|
|
@ -17,6 +18,68 @@ where
|
||||||
fn increase_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item));
|
fn increase_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PriorityQueueByKey<Item>
|
||||||
|
where
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
type Handle;
|
||||||
|
fn new() -> Self;
|
||||||
|
|
||||||
|
fn insert(&mut self, item: Item) -> Self::Handle;
|
||||||
|
fn pop_min(&mut self) -> Option<Item>;
|
||||||
|
fn decrease_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item));
|
||||||
|
fn increase_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ByKey<Item>(Item);
|
||||||
|
|
||||||
|
impl<Item> PartialEq for ByKey<Item>
|
||||||
|
where
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
(&self.0).into().eq(&(&other.0).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> PartialOrd for ByKey<Item>
|
||||||
|
where
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
(&self.0).into().partial_cmp(&(&other.0).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, Item> PriorityQueueByKey<Item> for P
|
||||||
|
where
|
||||||
|
P: PriorityQueue<ByKey<Item>>,
|
||||||
|
for<'a> &'a Item: Into<i64>,
|
||||||
|
{
|
||||||
|
type Handle = P::Handle;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
P::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, item: Item) -> Self::Handle {
|
||||||
|
self.insert(ByKey(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_min(&mut self) -> Option<Item> {
|
||||||
|
self.pop_min().map(|r| r.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item)) {
|
||||||
|
self.decrease_key(handle, |i| f(&mut i.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase_key(&mut self, handle: &Self::Handle, f: impl Fn(&mut Item)) {
|
||||||
|
self.increase_key(handle, |i| f(&mut i.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::PriorityQueue;
|
use super::PriorityQueue;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Debug, hash::Hash, hash::Hasher};
|
||||||
|
|
||||||
use tracing::{field::Empty, trace_span};
|
use tracing::{field::Empty, trace_span};
|
||||||
|
|
||||||
use crate::priority_queue::PriorityQueue;
|
use crate::priority_queue::{PriorityQueue, PriorityQueueByKey};
|
||||||
|
|
||||||
use super::WheightedGraph;
|
use super::WheightedGraph;
|
||||||
|
|
||||||
|
|
@ -18,6 +18,12 @@ impl<N> QueueObject<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N> Into<i64> for &QueueObject<N> {
|
||||||
|
fn into(self) -> i64 {
|
||||||
|
self.score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N> PartialOrd for QueueObject<N> {
|
impl<N> PartialOrd for QueueObject<N> {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
self.score.partial_cmp(&other.score)
|
self.score.partial_cmp(&other.score)
|
||||||
|
|
@ -58,9 +64,8 @@ impl<N, H> MapObject<N, H> {
|
||||||
|
|
||||||
pub fn dijkstra<G, P, E>(graph: &G, start: G::Node, end: E) -> Option<Vec<G::Node>>
|
pub fn dijkstra<G, P, E>(graph: &G, start: G::Node, end: E) -> Option<Vec<G::Node>>
|
||||||
where
|
where
|
||||||
P: PriorityQueue<QueueObject<G::Node>> + Debug,
|
P: PriorityQueueByKey<QueueObject<G::Node>>,
|
||||||
P::Handle: Debug,
|
G::Node: Eq + Hash + Clone,
|
||||||
G::Node: Eq + Hash + Clone + Debug,
|
|
||||||
G: WheightedGraph,
|
G: WheightedGraph,
|
||||||
E: Fn(&'_ G::Node) -> bool,
|
E: Fn(&'_ G::Node) -> bool,
|
||||||
{
|
{
|
||||||
|
|
@ -159,9 +164,8 @@ impl<G: WheightedGraph, F: Fn(&G::Node) -> i64> WheightedGraph for GraphWrapper<
|
||||||
|
|
||||||
pub fn a_star<G, P, E, F>(graph: &G, start: G::Node, end: E, dist: F) -> Option<Vec<G::Node>>
|
pub fn a_star<G, P, E, F>(graph: &G, start: G::Node, end: E, dist: F) -> Option<Vec<G::Node>>
|
||||||
where
|
where
|
||||||
P: PriorityQueue<QueueObject<G::Node>> + Debug,
|
P: PriorityQueueByKey<QueueObject<G::Node>>,
|
||||||
P::Handle: Debug,
|
G::Node: Eq + Hash + Clone,
|
||||||
G::Node: Eq + Hash + Clone + Debug,
|
|
||||||
G: WheightedGraph,
|
G: WheightedGraph,
|
||||||
E: Fn(&'_ G::Node) -> bool,
|
E: Fn(&'_ G::Node) -> bool,
|
||||||
F: Fn(&G::Node) -> i64,
|
F: Fn(&G::Node) -> i64,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use tracing::trace_span;
|
use tracing::trace_span;
|
||||||
|
|
||||||
use crate::priority_queue::PriorityQueue;
|
use crate::priority_queue::{PriorityQueue, PriorityQueueByKey};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::{collections::HashSet, fmt::Debug};
|
use std::{collections::HashSet, fmt::Debug};
|
||||||
|
|
||||||
|
|
@ -32,9 +32,8 @@ where
|
||||||
|
|
||||||
pub fn takaheshi_matsuyama<G, P>(graph: &G, nodes: &[G::Node]) -> Option<Vec<Vec<G::Node>>>
|
pub fn takaheshi_matsuyama<G, P>(graph: &G, nodes: &[G::Node]) -> Option<Vec<Vec<G::Node>>>
|
||||||
where
|
where
|
||||||
P: PriorityQueue<QueueObject<Option<G::Node>>> + Debug,
|
P: PriorityQueueByKey<QueueObject<Option<G::Node>>>,
|
||||||
P::Handle: Debug,
|
G::Node: Eq + Hash + Clone,
|
||||||
G::Node: Eq + Hash + Clone + Debug,
|
|
||||||
G: WheightedGraph,
|
G: WheightedGraph,
|
||||||
{
|
{
|
||||||
let _complete_span = trace_span!("takaheshi_matsuyama").entered();
|
let _complete_span = trace_span!("takaheshi_matsuyama").entered();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use std::path::PathBuf;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use factorio_core::prelude::{Direction, Position};
|
use factorio_core::prelude::{Direction, Position};
|
||||||
use factorio_graph::{
|
use factorio_graph::{
|
||||||
priority_queue::binary_heap::FastBinaryHeap, wheighted_graph::shortest_path::QueueObject,
|
priority_queue::{ByKey, binary_heap::FastBinaryHeap},
|
||||||
|
wheighted_graph::shortest_path::QueueObject,
|
||||||
};
|
};
|
||||||
use factorio_layout::{Layouter, valid_layout::ValidLayout};
|
use factorio_layout::{Layouter, valid_layout::ValidLayout};
|
||||||
use factorio_pathfinding::belt_finding::ConflictAvoidance;
|
use factorio_pathfinding::belt_finding::ConflictAvoidance;
|
||||||
|
|
@ -36,7 +37,7 @@ fn main() {
|
||||||
let p = ConflictAvoidance {
|
let p = ConflictAvoidance {
|
||||||
timeout: Some(std::time::Duration::from_millis(5)),
|
timeout: Some(std::time::Duration::from_millis(5)),
|
||||||
priority_queue: std::marker::PhantomData::<
|
priority_queue: std::marker::PhantomData::<
|
||||||
FastBinaryHeap<QueueObject<(Position, Direction)>>,
|
FastBinaryHeap<ByKey<QueueObject<(Position, Direction)>>>,
|
||||||
>,
|
>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::examples::HashMapMap;
|
||||||
use factorio_core::misc::Map;
|
use factorio_core::misc::Map;
|
||||||
use factorio_core::{prelude::*, visualize::Visualize};
|
use factorio_core::{prelude::*, visualize::Visualize};
|
||||||
use factorio_graph::priority_queue::PriorityQueue;
|
use factorio_graph::priority_queue::PriorityQueue;
|
||||||
|
use factorio_graph::priority_queue::PriorityQueueByKey;
|
||||||
use factorio_graph::wheighted_graph::WheightedGraph;
|
use factorio_graph::wheighted_graph::WheightedGraph;
|
||||||
use factorio_graph::wheighted_graph::shortest_path::QueueObject;
|
use factorio_graph::wheighted_graph::shortest_path::QueueObject;
|
||||||
use factorio_graph::wheighted_graph::shortest_path::a_star;
|
use factorio_graph::wheighted_graph::shortest_path::a_star;
|
||||||
|
|
@ -26,8 +27,7 @@ pub struct ConflictAvoidance<P> {
|
||||||
|
|
||||||
impl<P> SinglePathfinder for ConflictAvoidance<P>
|
impl<P> SinglePathfinder for ConflictAvoidance<P>
|
||||||
where
|
where
|
||||||
P: PriorityQueue<QueueObject<(Position, Direction)>> + std::fmt::Debug,
|
P: PriorityQueueByKey<QueueObject<(Position, Direction)>>,
|
||||||
P::Handle: std::fmt::Debug,
|
|
||||||
{
|
{
|
||||||
fn find_paths<M: crate::Map>(
|
fn find_paths<M: crate::Map>(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -280,8 +280,7 @@ impl WheightedGraph for MapInternal<'_> {
|
||||||
impl Problem {
|
impl Problem {
|
||||||
pub fn find_path<P>(&mut self) -> bool
|
pub fn find_path<P>(&mut self) -> bool
|
||||||
where
|
where
|
||||||
P: PriorityQueue<QueueObject<(Position, Direction)>> + std::fmt::Debug,
|
P: PriorityQueueByKey<QueueObject<(Position, Direction)>>,
|
||||||
P::Handle: std::fmt::Debug,
|
|
||||||
{
|
{
|
||||||
let _span = span!(Level::TRACE, "find_path").entered();
|
let _span = span!(Level::TRACE, "find_path").entered();
|
||||||
for i in 0..self.start.len() {
|
for i in 0..self.start.len() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue