Skip to content

Commit

Permalink
Traverser helper type with bf_iter method
Browse files Browse the repository at this point in the history
  • Loading branch information
lpenz committed Sep 21, 2021
1 parent 9f80e9b commit 6957378
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 50 deletions.
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ types it provides:
which can be `None` if the result is outside the grid.
- [`Grid`]: a `Qa`-indexed array.
- [`Gridbool`]: a bitmap-backed `Qa`-indexed grid of booleans.
- [`BfIterator`]: iterate a grid in breadth-first order, which is
useful for path-finding, flood-filling, and several other
things.
- [`Traverser`]: helper structure that concentrates all `const
generics` arguments and acts as a provider of grid-travessing
algorithms. This is the starting point of a big chunk of the
core functionality of this crate.

All basic types have the standard `iter`, `iter_mut`, `extend`,
`as_ref`, and conversion operations that should be expected.
Expand Down Expand Up @@ -116,7 +117,7 @@ for (qa, &i) in gridnums.iter_qa() {
gridnums.as_mut().reverse();
```

# `Gridbool`: a bitmap-backed `Qa`-indexed grid of booleans
## `Gridbool`: a bitmap-backed `Qa`-indexed grid of booleans

`Gridbool` is a compact abstraction of a grid of booleans.

Expand Down Expand Up @@ -155,6 +156,23 @@ for (qa, b) in gb.iter_qa() {
}
```

## `Travesser`: entry point of travessing algorithms

This type provides a convenient entry point for all travessing
algorithms implemented by this crate. To use it, create a type
alias using [`traverser_create`], and use the alias to call the
methods.

Example usage:

```rust
type Qa = sqrid::Qa<4,4>;
type Traverser = sqrid::traverser_create!(Qa, false); // No diagonals

for (qa, qr, dist) in Traverser::bf_iter(&[Qa::CENTER], |qa, qr| qa + qr) {
println!("breadth-first qa {} from {} distance {}", qa, qr, dist);
}
```

[`Qa`]: https://docs.rs/sqrid/0/sqrid/sqrid/struct.Qa.html
[`Qa::FIRST`]: https://docs.rs/sqrid/0/sqrid/sqrid/struct.Qa.html#associatedconstant.FIRST
Expand All @@ -179,4 +197,6 @@ for (qa, b) in gb.iter_qa() {
[`Grid::line_mut`]: https://docs.rs/sqrid/0/sqrid/sqrid/struct.Grid.html#method.line_mut
[`Gridbool`]: https://docs.rs/sqrid/0/sqrid/sqrid/struct.Gridbool.html
[`gridbool_create`]: https://docs.rs/sqrid/0/sqrid/macro.gridbool_create.html
[`Traverser`]: https://docs.rs/sqrid/0/sqrid/sqrid/enum.Traverser.html
[`traverser_create`]: https://docs.rs/sqrid/0/sqrid/macro.traverser_create.html

26 changes: 23 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
//! which can be `None` if the result is outside the grid.
//! - [`Grid`]: a `Qa`-indexed array.
//! - [`Gridbool`]: a bitmap-backed `Qa`-indexed grid of booleans.
//! - [`BfIterator`]: iterate a grid in breadth-first order, which is
//! useful for path-finding, flood-filling, and several other
//! things.
//! - [`Traverser`]: helper structure that concentrates all `const
//! generics` arguments and acts as a provider of grid-travessing
//! algorithms. This is the starting point of a big chunk of the
//! core functionality of this crate.
//!
//! All basic types have the standard `iter`, `iter_mut`, `extend`,
//! `as_ref`, and conversion operations that should be expected.
Expand Down Expand Up @@ -156,6 +157,25 @@
//! }
//! ```
//!
//! # `Travesser`: entry point of travessing algorithms
//!
//! This type provides a convenient entry point for all travessing
//! algorithms implemented by this crate. To use it, create a type
//! alias using [`traverser_create`], and use the alias to call the
//! methods.
//!
//! Example usage:
//!
//! ```rust
//! type Qa = sqrid::Qa<4,4>;
//! type Traverser = sqrid::traverser_create!(Qa, false); // No diagonals
//!
//! for (qa, qr, dist) in Traverser::bf_iter(&[Qa::CENTER], |qa, qr| qa + qr) {
//! println!("breadth-first qa {} from {} distance {}", qa, qr, dist);
//! }
//! ```
//!
//!

mod sqrid;
pub use self::sqrid::*;
121 changes: 84 additions & 37 deletions src/sqrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,75 @@ impl<const W: u16, const H: u16, const WORDS: usize> fmt::Display for Gridbool<W
}
}

/* Traverser helper struct: *****************************************/

/// Helper type that builds traversal-related entities
///
/// This type holds all generic parameters used to create
/// traversal-related types like BfIterator.
///
/// These types usually have to be aware of the dimensions of the
/// grid, whether diagonals should be used, and other generic const
/// parameters that can't be calculated in rust yet. Instead of having
/// to instantiate each traversing type with the parameres, we use
/// this class to concentrate them, and then use its methods.
///
/// The helper macro [`traverser_create`] should be used to create
/// aliases to this type.
///
/// Example usage:
/// ```
/// type Qa = sqrid::Qa<4,4>;
/// type Traverser = sqrid::traverser_create!(Qa, false); // No diagonals
///
/// for (qa, qr, dist) in Traverser::bf_iter(&[Qa::CENTER], |qa, qr| qa + qr) {
/// println!("breadth-first qa {} from {} distance {}", qa, qr, dist);
/// }
/// ```
#[allow(missing_debug_implementations)]
pub enum Traverser<const W: u16, const H: u16, const D: bool, const SIZE: usize, const WORDS: usize>
{}

/// Helper macro that creates a Traverser type from a [`Qa`]
///
/// The [`Traverser`] type needs at least 3 parameters that can be
/// derived from the grid's [`Qa`] coordinate type. This macro takes
/// advantage of that and uses a provided `Qa` type to create the
/// corresponding [`Traverser`].
///
/// A usage example can be seen in [`Traverser`]
#[macro_export]
macro_rules! traverser_create {
($qatype: ty, $diag: expr) => {
$crate::Traverser<{ <$qatype>::WIDTH }, { <$qatype>::HEIGHT }, $diag,
{ (<$qatype>::WIDTH as usize * <$qatype>::HEIGHT as usize) },
{ (<$qatype>::WIDTH as usize * <$qatype>::HEIGHT as usize - 1) / 32 + 1 }
>
};
}

impl<const W: u16, const H: u16, const D: bool, const SIZE: usize, const WORDS: usize>
Traverser<W, H, D, SIZE, WORDS>
{
/// Create a breadth-first iterator
///
/// The function accepts a slice with a set of points to be used
/// as the origins and a function that is responsible for
/// evaluating a given [`Qa`] position plus a [`Qr`] direction
/// into an optional next position, `Option<Qa>`. The
/// [`qaqr_eval`] function can be used to traverse a grid where
/// all the coordinates are available with the trivial topological
/// relations.
///
/// A usage example can be seen in [`Traverser`]
pub fn bf_iter<F>(origins: &[Qa<W, H>], go: F) -> BfIterator<F, W, H, D, WORDS>
where
F: Fn(Qa<W, H>, Qr) -> Option<Qa<W, H>>,
{
BfIterator::<F, W, H, D, WORDS>::new(origins, go)
}
}

/* Breadth-first iterator *******************************************/

/// Breadth-first iterator
Expand All @@ -1474,17 +1543,6 @@ impl<const W: u16, const H: u16, const WORDS: usize> fmt::Display for Gridbool<W
/// a provided set of specific points, using a provided function to
/// evaluate a given [`Qa`] position + [`Qr`] direction into the next
/// `Qa` position.
///
/// The type arguments are:
/// - `F`: type of the evaluation function, doesn't have to be
/// explicitly provided.
/// - `W`, `H`: grid parameters, width and height.
/// - `D`: `true` if the grid can be traversed diagonally; `false` to
/// allow only north, south, east, west traversal.
/// - `WORDS`: [`Gridbool`] parameter, essentially `W * H / 32`
/// rounded up.
///
/// See [`BfIterator::new`] for example usage.
#[derive(Debug, Clone)]
pub struct BfIterator<F, const W: u16, const H: u16, const D: bool, const WORDS: usize> {
visited: Gridbool<W, H, WORDS>,
Expand All @@ -1499,32 +1557,8 @@ impl<F, const W: u16, const H: u16, const D: bool, const WORDS: usize>
{
/// Create new breadth-first iterator
///
/// This function creates a new [`BfIterator`] structure that can
/// be used to iterate a grid in bradth-first order.
///
/// The function accepts a slice with a set of points to be used
/// as the origins and a function that is responsible for
/// evaluating a given [`Qa`] position plus a [`Qr`] direction
/// into an optional next position, `Option<Qa>`. The
/// [`qaqr_eval`] function can be used to traverse a grid where
/// all the coordinates are available with the trivial topological
/// relations.
///
/// Example: traversing a grid starting at the center:
///
/// ```
/// type Qa = sqrid::Qa<11, 11>;
/// let mut iter = sqrid::BfIterator::<
/// _, 11, 11, false, 4
/// >::new(
/// &[Qa::CENTER],
/// sqrid::qaqr_eval
/// );
/// for (qa, qr, dist) in iter {
/// eprintln!("position {} came from direction {}, distance {}",
/// qa, qr, dist);
/// }
/// ```
/// Use [`Traverser::bf_iter`] instead of this to instantiate
/// [`BfIterator`], it's way more convenient.
pub fn new(origins: &[Qa<W, H>], go: F) -> Self
where
F: Fn(Qa<W, H>, Qr) -> Option<Qa<W, H>>,
Expand All @@ -1544,6 +1578,19 @@ impl<F, const W: u16, const H: u16, const D: bool, const WORDS: usize>
/// Get the next coordinate in breadth-first order
///
/// This is the backend of the `Iterator` trait for `BfIterator`.
///
/// Example: traversing a grid starting at the center:
///
/// ```
/// type Qa = sqrid::Qa<11, 11>;
/// type Traverser = sqrid::traverser_create!(Qa, false); // No diagonals
///
/// for (qa, qr, dist) in Traverser::bf_iter(&[Qa::CENTER],
/// sqrid::qaqr_eval) {
/// eprintln!("position {} came from direction {}, distance {}",
/// qa, qr, dist);
/// }
/// ```
pub fn visit_next(&mut self) -> Option<(Qa<W, H>, Qr, usize)>
where
F: Fn(Qa<W, H>, Qr) -> Option<Qa<W, H>>,
Expand Down
11 changes: 5 additions & 6 deletions tests/bf_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use std::convert::TryFrom;

type Qa = sqrid::Qa<3, 3>;
type GridDist = sqrid::Grid<usize, 3, 3, 9>;
type Traverser = sqrid::traverser_create!(Qa, false);

type Qa2 = sqrid::Qa<256, 256>;
type GridDist2 = sqrid::grid_create!(usize, Qa2);
type Traverser2 = sqrid::traverser_create!(Qa2, false);

fn sumfunc(qa: Qa, qr: sqrid::Qr) -> Option<Qa> {
qa + qr
Expand All @@ -21,7 +23,7 @@ fn sumfunc(qa: Qa, qr: sqrid::Qr) -> Option<Qa> {
fn test_basic() -> Result<()> {
let center = Qa::try_from((1, 1))?;
let mut g = GridDist::default();
let bfiterator = sqrid::BfIterator::<_, 3, 3, false, 1>::new(&[center], sumfunc);
let bfiterator = Traverser::bf_iter(&[center], sumfunc);
let bfiterator2 = bfiterator.clone();
g.extend(bfiterator2.map(|(qa, _, d)| (qa, d)));
assert_eq!(
Expand All @@ -36,7 +38,7 @@ fn test_walls() -> Result<()> {
let center = Qa::try_from((1, 1))?;
let mut g = GridDist::default();
g.extend(
sqrid::BfIterator::<_, 3, 3, false, 1>::new(&[center], |qa, qr| {
Traverser::bf_iter(&[center], |qa, qr| {
(qa + qr).and_then(|qa| {
let t = qa.tuple();
if t != (0, 1) && t != (2, 1) {
Expand All @@ -58,10 +60,7 @@ fn test_walls() -> Result<()> {
#[test]
fn test_scale() -> Result<()> {
let mut g = GridDist2::default();
g.extend(
sqrid::BfIterator::<_, 256, 256, false, 2048>::new(&[Qa2::TOP_LEFT], |qa, qr| qa + qr)
.map(|(qa, _, d)| (qa, d)),
);
g.extend(Traverser2::bf_iter(&[Qa2::TOP_LEFT], |qa, qr| qa + qr).map(|(qa, _, d)| (qa, d)));
for qa in Qa2::iter() {
assert_eq!(g[qa], Qa2::manhattan(qa, Qa2::TOP_LEFT));
}
Expand Down

0 comments on commit 6957378

Please sign in to comment.