1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
// Copyright (C) 2021 Leandro Lisboa Penz <lpenz@lpenz.org>
// This file is subject to the terms and conditions defined in
// file 'LICENSE', which is part of this source code package.
#![warn(rust_2018_idioms)]
#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
//! *sqrid* provides square grid coordinates and related operations,
//! in a crate with zero dependencies.
//!
//! It's easier to explain the features of this crate in terms of the
//! types it provides:
//! - [`Pos`]: position, as absolute coordinates in a grid of fixed
//! size. The dimensions of the grid are const generics type
//! parameters; invalid coordinates can't be created.
//! - [`Dir`]: "movement", relative coordinates. These are the
//! cardinal (and intercardinal) directions.
//! Addition is implemented in the form of `Pos + Dir = Option<Pos>`,
//! which can be `None` if the result is outside the grid.
//! - [`Grid`]: a `Pos`-indexed array.
//! - [`Gridbool`]: a bitmap-backed `Pos`-indexed grid of booleans.
//! - [`Sqrid`]: "factory" type that acts as an entry point to the
//! fundamental types below and to algorithms.
//!
//! We also have traits that generalize `Grid` and `Gridbool`:
//! - [`MapPos`]: trait that maps `Pos` to parameterized items;
//! it's implemented by `Grid`, and some `HashMap`/`BTreeMap` based types.
//! - [`SetPos`]: trait that maps each `Pos` to a bool; it's implemented
//! by `Gridbool`, `HashSet<Pos>` and `BTreeSet<Pos>`.
//!
//! We then use these generalization to implement some grid algorithms:
//! - [`bf`]: breadth-first iteration and search.
//! - [`astar`]: A* search that takes a destination `Pos`.
//! - [`ucs`]: uniform-cost search.
//!
//! All basic types have the standard `iter`, `iter_mut`, `extend`,
//! `as_ref`, and conversion operations that should be expected.
//!
//! # Fundamental types
//!
//! ## `Pos`: absolute coordinates, position
//!
//! The [`Pos`] type represents an absolute position in a square
//! grid. The type itself receives the height and width of the grid as
//! const generic parameter.
//!
//! We should usually create a type alias for the grid size we are using:
//!
//! ```rust
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use sqrid;
//!
//! type Pos = sqrid::Pos<6, 7>;
//!
//! let pos = Pos::new(3, 3)?;
//! # Ok(()) }
//! ```
//!
//! We can only generate [`Pos`] instances that are inside the passed
//! dimensions.
//!
//! ## `Dir`: relative coordinates, direction, movement
//!
//! The [`Dir`] type represents a relative movement of one square. It
//! can only be one of the 8 cardinal and intercardinal directions:
//! [`Dir::N`], [`Dir::NE`], [`Dir::E`], [`Dir::SE`], [`Dir::S`],
//! [`Dir::SW`], [`Dir::W`], [`Dir::NW`].
//!
//! It's a building block for paths, iterating on a [`Pos`] neighbors,
//! etc. It effectively represents the edges in a graph where the
//! [`Pos`] type represents nodes.
//!
//! All functions that iterate on `Dir` values accept a boolean const
//! argument that specifies whether the intercardinal directions
//! (`NE`, `SE`, `SW`, `NW`) should be considered.
//!
//! ## `Grid`: a `Pos`-indexed array
//!
//! A [`Grid`] is a generic array that can be indexed by a [`Pos`].
//!
//! We can create the type from a suitable [`Sqrid`] type by using the
//! [`grid_create`] macro. We can then interact with specific lines
//! with [`Grid::line`] and [`Grid::line_mut`], or with the whole
//! underlying array with `as_ref` (see [`std::convert::AsRef`]) and
//! `as_mut` (see [`std::convert::AsMut`]).
//!
//! Usage example:
//!
//! ```rust
//! 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`: a bitmap-backed `Pos`-indexed grid of booleans
//!
//! The [`Gridbool`] is a compact abstraction of a grid of booleans.
//!
//! The type itself can be created with [`gridbool_create`] macro.
//! It's optimized for getting and setting values at specific
//! coordinates, but we can also get all `true`/`false` coordinates
//! with suboptimal performance - in this case, the time is
//! proportional to the size of the grid and not to the quantity of
//! `true`/`false` values.
//!
//! Usage example:
//!
//! ```rust
//! 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`: entry point for algorithms
//!
//! The [`Pos`] type and some methods on the [`Dir`] type require const
//! generic arguments that usually don't change inside an application.
//! Both [`Grid`] and [`Gridbool`] also require further arguments that
//! can actually be derived from the width and height of the grid, but
//! that have to be explicitly specified due to some Rust limitations.
//!
//! To make the creation of these types easier, we provide the
//! [`Sqrid`] type, which acumulates all const generic parameters and
//! can be used to create the other types via macros.
//!
//! Example usage:
//!
//! ```rust
//! 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);
//! ```
//!
//! # Algorithms
//!
//! ## Breadth-first traversal
//!
//! The [`Sqrid::bf_iter`] function instantiates an iterator struct
//! ([`bf::BfIterator`]) that can be used to iterate coordinates in
//! breadth-first order, from a given origin, using a provided
//! function to evaluate a given [`Pos`] position + [`Dir`] direction
//! into the next `Pos` position.
//!
//! Example usage:
//!
//! ```rust
//! 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);
//! }
//! ```
//!
//! ## Breadth-first search
//!
//! [`Sqrid::bfs_path`] takes an origin, a movement function and a
//! goal function, and figures out the shortest path to a goal by
//! using a breadth-first iteration.
//!
//! The function returns the [`Pos`] that fulfills the goal and a
//! path in the form of a `Vec<Dir>`.
//!
//! Example usage:
//!
//! ```rust
//! 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* search
//!
//! [`Sqrid::astar_path`] takes a movement function, an origin and a
//! destination, and figures out the shortest path by using A*.
//!
//! The function returns path in the form of a `Vec<Dir>`.
//!
//! Example usage:
//!
//! ```rust
//! 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);
//! }
//! ```
//!
//! ## Uniform-cost search
//!
//! [`Sqrid::ucs_path`] takes a movement-cost function, an origin and a
//! destination, and figures out the path with the lowest cost by using
//! uniform-cost search, which is essentially a variation of
//! [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm).
//!
//! The function returns path in the form of a `Vec<Dir>`.
//!
//! Example usage:
//!
//! ```rust
//! 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);
//! }
//! ```
mod sqrid;
pub use self::sqrid::*;