24 个版本
0.0.24 | 2023 年 12 月 21 日 |
---|---|
0.0.18 | 2023 年 5 月 30 日 |
0.0.17 | 2023 年 3 月 12 日 |
0.0.14 | 2022 年 12 月 22 日 |
0.0.6 | 2021 年 11 月 29 日 |
#148 in 数据结构
498 每月下载量
130KB
2.5K SLoC
sqrid
sqrid 在无依赖项的 crate 中提供平方网格坐标和相关操作。
通过它提供的类型来解释这个 crate 的特性更为容易
Pos
: 位置,作为固定大小网格中的绝对坐标。网格的尺寸是 const 泛型类型参数;无法创建无效坐标。Dir
: "移动",相对坐标。这些是基本方向(和中间方向)。加法以Pos + Dir = Option<Pos>
的形式实现,如果结果在网格之外,则可以为None
。Grid
: 一个Pos
索引的数组。Gridbool
: 一个基于位图的Pos
索引布尔网格。Sqrid
: 作为以下基本类型和算法的入口点的 "工厂" 类型。
我们还有一些泛化 Grid
和 Gridbool
的特质
MapPos
: 将Pos
映射到参数化项的特质;它由Grid
和一些基于HashMap
/BTreeMap
的类型实现。SetPos
: 将每个Pos
映射到 bool 的特质;它由Gridbool
、HashSet<Pos>
和BTreeSet<Pos>
实现。
然后我们使用这些泛化来实现一些网格算法
所有基本类型都具有标准的 iter
、iter_mut
、extend
、as_ref
以及转换操作,这些都是预期的。
基本类型
Pos
:绝对坐标,位置
Pos
类型表示正方形网格中的绝对位置。该类型本身接受网格的高度和宽度作为 const 泛型参数。
我们通常应该为使用的网格大小创建一个类型别名
use sqrid;
type Pos = sqrid::Pos<6, 7>;
let pos = Pos::new(3, 3)?;
我们只能生成在传入的维度内的 Pos
实例。
Dir
:相对坐标,方向,移动
Dir
类型表示一个正方形的一个相对移动。它只能是 8 个主要和次要方向之一:Dir::N
、Dir::NE
、Dir::E
、Dir::SE
、Dir::S
、Dir::SW
、Dir::W
、Dir::NW
。
它是路径的构建块,遍历 Pos
邻居等。它有效地表示图中由 Pos
类型表示的边。
所有遍历 Dir
值的函数都接受一个布尔 const 参数,指定是否应考虑次要方向(NE、
SE、
SW、
NW
)。
Grid
:一个 Pos
索引的数组
我们可以通过使用Sqrid
类型和grid_create
宏来创建所需的类型。然后,我们可以使用Grid::line
和Grid::line_mut
来与特定行交互,或者使用as_ref(见std::convert::AsRef
)和as_mut(见std::convert::AsMut
)与整个底层数组交互。
使用示例
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
type Grid = sqrid::grid_create!(Sqrid, i32);
// The grid create macro above is currently equivalent to:
type Grid2 = sqrid::Grid<i32, { Sqrid::WIDTH }, { Sqrid::HEIGHT },
{ (Sqrid::WIDTH * Sqrid::HEIGHT) as usize }>;
// We can create grids from iterators via `collect`:
let mut gridnums = (0..9).collect::<Grid>();
// Iterate on their members:
for i in &gridnums {
println!("i {}", i);
}
// Change the members in a loop:
for i in &mut gridnums {
*i *= 10;
}
// Iterate on (coordinate, member) tuples:
for (pos, &i) in gridnums.iter_pos() {
println!("[{}] = {}", pos, i);
}
// And we can always use `as_ref` or `as_mut` to interact with the
// inner array directly. To reverse it, for example, with the
// [`std::slice::reverse`] function:
gridnums.as_mut().reverse();
Gridbool
:一个基于位图的Pos
索引布尔网格
Gridbool
是对布尔网格的紧凑抽象。
该类型可以通过gridbool_create
宏创建。它针对获取和设置特定坐标的值进行了优化,但也可以以次优性能获取所有true
/false
坐标 - 在这种情况下,时间是与网格大小成正比,而不是与true
/false
值的数量成正比。
使用示例
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
type Gridbool = sqrid::gridbool_create!(Sqrid);
// We can create a gridbool from a Pos iterator via `collect`:
let mut gb = Pos::iter().filter(|pos| pos.is_corner()).collect::<Gridbool>();
// We can also set values from an iterator:
gb.set_iter_t(Pos::iter().filter(|pos| pos.is_side()));
// Iterate on the true/false values:
for b in gb.iter() {
println!("{}", b);
}
// Iterate on the true coordinates:
for pos in gb.iter_t() {
assert!(pos.is_side());
}
// Iterate on (coordinate, bool):
for (pos, b) in gb.iter_pos() {
println!("[{}] = {}", pos, b);
}
Sqrid
:算法的入口点
Pos
类型和一些Dir
类型的方法需要通常在应用程序内部不改变的const泛型参数。同时,Grid
和Gridbool
也需要从网格宽度和高度推导出来的进一步参数,但由于某些Rust限制,必须显式指定。
为了简化这些类型的创建,我们提供了Sqrid
类型,它累积所有const泛型参数,可以通过宏来创建其他类型。
示例用法
type Sqrid = sqrid::sqrid_create!(4, 4, false);
type Pos = sqrid::pos_create!(Sqrid);
type Grid = sqrid::grid_create!(Sqrid, i32);
type Gridbool = sqrid::gridbool_create!(Sqrid);
算法
广度优先遍历
Sqrid::bf_iter
函数实例化一个迭代器结构体(bf::BfIterator
),它可以用于迭代从给定原点开始,使用提供的函数评估给定的Pos
位置 + Dir
方向到下一个Pos位置。
示例用法
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
for (pos, dir) in Sqrid::bf_iter(sqrid::mov_eval, &Pos::CENTER)
.flatten() {
println!("breadth-first pos {} from {}", pos, dir);
}
广度优先搜索
Sqrid::bfs_path
接受一个起点、一个移动函数和一个目标函数,通过广度优先迭代找出到达目标的最短路径。
该函数返回满足目标的 Pos
以及形式为 Vec<Dir>
的路径。
示例用法
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
// Generate the grid of "came from" directions from bottom-right to
// top-left:
if let Ok((goal, path)) = Sqrid::bfs_path(
sqrid::mov_eval, &Pos::TOP_LEFT,
|pos| pos == Pos::BOTTOM_RIGHT) {
println!("goal: {}, path: {:?}", goal, path);
}
A* 搜索
Sqrid::astar_path
接收一个移动函数、起点和终点,并通过使用 A* 算法计算出最短路径。
该函数返回形式为 Vec<Dir>
的路径。
示例用法
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
// Generate the grid of "came from" directions from bottom-right to
// top-left:
if let Ok(path) = Sqrid::astar_path(sqrid::mov_eval, &Pos::TOP_LEFT,
&Pos::BOTTOM_RIGHT) {
println!("path: {:?}", path);
}
一致代价搜索
Sqrid::ucs_path
接收一个移动代价函数、起点和终点,并通过使用一致代价搜索(本质上是一种 Dijkstra 算法的变体)找到代价最低的路径。
该函数返回形式为 Vec<Dir>
的路径。
示例用法
type Sqrid = sqrid::sqrid_create!(3, 3, false);
type Pos = sqrid::pos_create!(Sqrid);
fn traverse(position: Pos, direction: sqrid::Dir) -> Option<(Pos, usize)> {
let next_position = (position + direction).ok()?;
let cost = 1;
Some((next_position, cost))
}
// Generate the grid of "came from" directions from bottom-right to
// top-left:
if let Ok(path) = Sqrid::ucs_path(traverse, &Pos::TOP_LEFT,
&Pos::BOTTOM_RIGHT) {
println!("path: {:?}", path);
}