Compare commits

..

No commits in common. "main" and "v0.6.1" have entirely different histories.
main ... v0.6.1

12 changed files with 128 additions and 78 deletions

View File

@ -9,30 +9,76 @@ env:
RUSTFLAGS: --deny warnings RUSTFLAGS: --deny warnings
RUSTDOCFLAGS: --deny warnings RUSTDOCFLAGS: --deny warnings
jobs: jobs:
build-and-test: # Run tests.
test:
name: Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: timeout-minutes: 30
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps: steps:
- name: Checkout code - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Install Rust - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
- name: Populate target directory from cache
uses: Leafwing-Studios/cargo-cache@v2
with:
sweep-cache: true
- name: Run tests
run: |
cargo test --locked --workspace --all-features --all-targets
# Workaround for https://github.com/rust-lang/cargo/issues/6669
cargo test --locked --workspace --all-features --doc
# Run clippy lints.
clippy:
name: Clippy
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: stable components: clippy
components: clippy, rustfmt - name: Install dependencies
- name: Run sccache-cache run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
uses: mozilla-actions/sccache-action@v0.0.9 - name: Populate target directory from cache
- name: Install cargo-nextest uses: Leafwing-Studios/cargo-cache@v2
uses: taiki-e/install-action@cargo-nextest with:
- name: Run Clippy sweep-cache: true
run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings - name: Run clippy lints
- name: Run formatting run: cargo clippy --locked --workspace --all-features -- --deny warnings
run: cargo fmt --all --check # Check formatting.
- name: Run Tests format:
run: | name: Format
cargo nextest run --all-features --all-targets runs-on: ubuntu-latest
cargo test --locked --workspace --all-features --doc timeout-minutes: 30
- name: Check Documentation steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run cargo fmt
run: cargo fmt --all -- --check
# Check documentation.
doc:
name: Docs
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
- name: Populate target directory from cache
uses: Leafwing-Studios/cargo-cache@v2
with:
sweep-cache: true
- name: Check documentation
run: cargo doc --locked --workspace --all-features --document-private-items --no-deps run: cargo doc --locked --workspace --all-features --document-private-items --no-deps

View File

@ -42,7 +42,7 @@ fn main() {
.with_radius(5) .with_radius(5)
.build() .build()
.expect("Failed to create maze"); .expect("Failed to create maze");
println!("Maze size: {}", maze.count()); println!("Maze size: {}", maze.len());
} }
``` ```

View File

@ -20,7 +20,7 @@ use hexx::Hex;
/// ///
/// // A radius of 5 creates 61 hexagonal tiles /// // A radius of 5 creates 61 hexagonal tiles
/// assert!(!maze.is_empty()); /// assert!(!maze.is_empty());
/// assert_eq!(maze.count(), 91); /// assert_eq!(maze.len(), 91);
/// ``` /// ```
/// ///
/// Using a seed for reproducible results: /// Using a seed for reproducible results:
@ -40,7 +40,7 @@ use hexx::Hex;
/// .expect("Failed to create maze"); /// .expect("Failed to create maze");
/// ///
/// // Same seed should produce identical mazes /// // Same seed should produce identical mazes
/// assert_eq!(maze1.count(), maze2.count()); /// assert_eq!(maze1.len(), maze2.len());
/// assert_eq!(maze1, maze2); /// assert_eq!(maze1, maze2);
/// ``` /// ```
/// ///
@ -190,7 +190,7 @@ pub fn create_hex_maze(radius: u16) -> Maze {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use claims::{assert_gt, assert_some}; use claims::assert_gt;
use rstest::rstest; use rstest::rstest;
#[test] #[test]
@ -249,8 +249,8 @@ mod test {
Hex::new(0, -2), Hex::new(0, -2),
Hex::new(1, -2), Hex::new(1, -2),
]; ];
for pos in &expected_positions { for pos in expected_positions.iter() {
assert_some!(maze.get(pos), "Expected tile at {pos:?}"); assert!(maze.get(pos).is_some(), "Expected tile at {:?}", pos);
} }
} }
} }

View File

@ -44,7 +44,6 @@ fn recursive_backtrack<R: Rng>(
mod test { mod test {
use super::*; use super::*;
use crate::builder::create_hex_maze; use crate::builder::create_hex_maze;
use claims::assert_some;
use rstest::rstest; use rstest::rstest;
#[rstest] #[rstest]
@ -73,7 +72,7 @@ mod test {
recursive_backtrack(&mut maze, start, &mut visited, &mut rng); recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
for &pos in maze.keys() { for &pos in maze.keys() {
let walls = assert_some!(maze.get_walls(&pos)); let walls = maze.get_walls(&pos).unwrap();
assert!( assert!(
walls.count() < 6, walls.count() < 6,
"At least one wall should be removed for each tile" "At least one wall should be removed for each tile"

View File

@ -15,7 +15,6 @@ pub enum GeneratorType {
#[default] #[default]
RecursiveBacktracking, RecursiveBacktracking,
} }
impl GeneratorType { impl GeneratorType {
pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) { pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
match self { match self {

View File

@ -20,7 +20,7 @@
//! .build() //! .build()
//! .expect("Failed to create maze"); //! .expect("Failed to create maze");
//! //!
//! assert_eq!(maze.count(), 37); // A radius of 3 should create 37 tiles //! assert_eq!(maze.len(), 37); // A radius of 3 should create 37 tiles
//!``` //!```
//! //!
//! Customizing maze generation: //! Customizing maze generation:

View File

@ -34,7 +34,7 @@ impl Maze {
/// let maze = Maze::new(); /// let maze = Maze::new();
/// ///
/// assert!(maze.is_empty()); /// assert!(maze.is_empty());
/// assert_eq!(maze.count(), 0); /// assert_eq!(maze.len(), 0);
/// ``` /// ```
#[inline] #[inline]
#[must_use] #[must_use]

View File

@ -236,7 +236,7 @@ mod test {
let layout = HexLayout::default(); let layout = HexLayout::default();
let tile = Tile::new(Hex::new(1, 0)); let tile = Tile::new(Hex::new(1, 0));
let vec2 = tile.to_vec2(&layout); let vec2 = tile.to_vec2(&layout);
assert_eq!(vec2, Vec2::new(1.5, -0.866_025_4)); assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
} }
#[test] #[test]
@ -244,7 +244,7 @@ mod test {
let layout = HexLayout::default(); let layout = HexLayout::default();
let tile = Tile::new(Hex::new(0, 1)); let tile = Tile::new(Hex::new(0, 1));
let vec3 = tile.to_vec3(&layout); let vec3 = tile.to_vec3(&layout);
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.732_050_8)); assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
} }
} }
} }

View File

@ -375,7 +375,7 @@ mod test {
let walls = Walls::all_directions(); let walls = Walls::all_directions();
assert!(walls.is_enclosed()); assert!(walls.is_enclosed());
assert!(!walls.is_empty()); assert!(!walls.is_empty());
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
// as_bits // as_bits
@ -389,7 +389,7 @@ mod test {
fn as_bits_single_wall() { fn as_bits_single_wall() {
let mut walls = Walls::empty(); let mut walls = Walls::empty();
walls.insert(EdgeDirection::FLAT_NORTH); walls.insert(EdgeDirection::FLAT_NORTH);
assert_eq!(walls.as_bits(), 0b01_0000); assert_eq!(walls.as_bits(), 0b010000);
} }
#[test] #[test]
@ -397,13 +397,13 @@ mod test {
let mut walls = Walls::empty(); let mut walls = Walls::empty();
walls.insert(EdgeDirection::FLAT_NORTH); walls.insert(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_SOUTH); walls.insert(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
} }
#[test] #[test]
fn as_bits_all_walls() { fn as_bits_all_walls() {
let walls = Walls::new(); let walls = Walls::new();
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
// new // new
@ -411,7 +411,7 @@ mod test {
fn new_created_closed_walls() { fn new_created_closed_walls() {
let walls = Walls::new(); let walls = Walls::new();
assert!(walls.is_enclosed()); assert!(walls.is_enclosed());
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
// empty // empty
@ -520,7 +520,7 @@ mod test {
fn default_creates_closed_walls() { fn default_creates_closed_walls() {
let walls = Walls::default(); let walls = Walls::default();
assert!(walls.is_enclosed()); assert!(walls.is_enclosed());
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
#[test] #[test]
@ -542,53 +542,53 @@ mod test {
// Test single bit operations // Test single bit operations
walls.insert(EdgeDirection::FLAT_NORTH); walls.insert(EdgeDirection::FLAT_NORTH);
assert_eq!(walls.as_bits(), 0b01_0000); assert_eq!(walls.as_bits(), 0b010000);
walls.insert(EdgeDirection::FLAT_SOUTH); walls.insert(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
// Test removing middle bit // Test removing middle bit
walls.insert(EdgeDirection::FLAT_SOUTH_EAST); walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
assert_eq!(walls.as_bits(), 0b01_0011); assert_eq!(walls.as_bits(), 0b010011);
walls.remove(EdgeDirection::FLAT_SOUTH); walls.remove(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b01_0001); assert_eq!(walls.as_bits(), 0b010001);
} }
// From<EdgeDirection> tests // From<EdgeDirection> tests
#[test] #[test]
fn from_edge_direction_flat_south_east() { fn from_edge_direction_flat_south_east() {
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST); let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST);
assert_eq!(walls.as_bits(), 0b00_0001); assert_eq!(walls.as_bits(), 0b000001);
} }
#[test] #[test]
fn from_edge_direction_flat_south() { fn from_edge_direction_flat_south() {
let walls = Walls::from(EdgeDirection::FLAT_SOUTH); let walls = Walls::from(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b00_0010); assert_eq!(walls.as_bits(), 0b000010);
} }
#[test] #[test]
fn from_edge_direction_flat_south_west() { fn from_edge_direction_flat_south_west() {
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST); let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST);
assert_eq!(walls.as_bits(), 0b00_0100); assert_eq!(walls.as_bits(), 0b000100);
} }
#[test] #[test]
fn from_edge_direction_flat_north_west() { fn from_edge_direction_flat_north_west() {
let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST); let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST);
assert_eq!(walls.as_bits(), 0b00_1000); assert_eq!(walls.as_bits(), 0b001000);
} }
#[test] #[test]
fn from_edge_direction_flat_north() { fn from_edge_direction_flat_north() {
let walls = Walls::from(EdgeDirection::FLAT_NORTH); let walls = Walls::from(EdgeDirection::FLAT_NORTH);
assert_eq!(walls.as_bits(), 0b01_0000); assert_eq!(walls.as_bits(), 0b010000);
} }
#[test] #[test]
fn from_edge_direction_flat_east() { fn from_edge_direction_flat_east() {
let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST); let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST);
assert_eq!(walls.as_bits(), 0b10_0000); assert_eq!(walls.as_bits(), 0b100000);
} }
// FromIterator tests // FromIterator tests
@ -603,7 +603,7 @@ mod test {
let walls = vec![EdgeDirection::FLAT_SOUTH] let walls = vec![EdgeDirection::FLAT_SOUTH]
.into_iter() .into_iter()
.collect::<Walls>(); .collect::<Walls>();
assert_eq!(walls.as_bits(), 0b00_0010); assert_eq!(walls.as_bits(), 0b000010);
} }
#[test] #[test]
@ -611,7 +611,7 @@ mod test {
let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH] let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]
.into_iter() .into_iter()
.collect::<Walls>(); .collect::<Walls>();
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
} }
#[test] #[test]
@ -623,13 +623,13 @@ mod test {
] ]
.into_iter() .into_iter()
.collect::<Walls>(); .collect::<Walls>();
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
} }
#[test] #[test]
fn from_iterator_all_directions() { fn from_iterator_all_directions() {
let walls = EdgeDirection::iter().collect::<Walls>(); let walls = EdgeDirection::iter().collect::<Walls>();
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
// From<[EdgeDirection; N]> tests // From<[EdgeDirection; N]> tests
@ -642,13 +642,13 @@ mod test {
#[test] #[test]
fn from_array_single() { fn from_array_single() {
let walls = Walls::from([EdgeDirection::FLAT_NORTH]); let walls = Walls::from([EdgeDirection::FLAT_NORTH]);
assert_eq!(walls.as_bits(), 0b01_0000); assert_eq!(walls.as_bits(), 0b010000);
} }
#[test] #[test]
fn from_array_multiple() { fn from_array_multiple() {
let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]); let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
} }
#[test] #[test]
@ -658,7 +658,7 @@ mod test {
EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_NORTH,
EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH,
]); ]);
assert_eq!(walls.as_bits(), 0b01_0010); assert_eq!(walls.as_bits(), 0b010010);
} }
#[test] #[test]
@ -671,6 +671,6 @@ mod test {
EdgeDirection::FLAT_SOUTH_WEST, EdgeDirection::FLAT_SOUTH_WEST,
EdgeDirection::FLAT_NORTH_WEST, EdgeDirection::FLAT_NORTH_WEST,
]); ]);
assert_eq!(walls.as_bits(), 0b11_1111); assert_eq!(walls.as_bits(), 0b111111);
} }
} }

View File

@ -67,24 +67,28 @@ fn maze_connectivity() {
let maze = assert_ok!(MazeBuilder::new().with_radius(3).build()); let maze = assert_ok!(MazeBuilder::new().with_radius(3).build());
// Helper function to count accessible neighbors // Helper function to count accessible neighbors
let count_accessible_neighbors = |pos: Hex| -> usize { fn count_accessible_neighbors(maze: &Maze, pos: Hex) -> usize {
hexx::EdgeDirection::ALL_DIRECTIONS hexx::EdgeDirection::ALL_DIRECTIONS
.iter() .iter()
.filter(|&&dir| { .filter(|&&dir| {
let neighbor = pos + dir; let neighbor = pos + dir;
maze.get_walls(&pos) if let Some(walls) = maze.get_walls(&pos) {
.is_some_and(|walls| !walls.contains(dir) && maze.get(&neighbor).is_some()) !walls.contains(dir) && maze.get(&neighbor).is_some()
} else {
false
}
}) })
.count() .count()
}; }
// Check that each tile has at least one connection // Check that each tile has at least one connection
for &pos in maze.keys() { for &pos in maze.keys() {
let accessible_neighbors = count_accessible_neighbors(pos); let accessible_neighbors = count_accessible_neighbors(&maze, pos);
assert_gt!( claims::assert_gt!(
accessible_neighbors, accessible_neighbors,
0, 0,
"Tile at {pos:?} has no accessible neighbors", "Tile at {:?} has no accessible neighbors",
pos
); );
} }
} }
@ -92,9 +96,10 @@ fn maze_connectivity() {
#[test] #[test]
fn maze_boundaries() { fn maze_boundaries() {
let radius = 3; let radius = 3;
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build()); let maze = MazeBuilder::new()
.with_radius(radius as u16)
let radius = i32::from(radius); .build()
.unwrap();
// Test that tiles exist within the radius // Test that tiles exist within the radius
for q in -radius..=radius { for q in -radius..=radius {
@ -103,7 +108,8 @@ fn maze_boundaries() {
if q.abs() + r.abs() <= radius { if q.abs() + r.abs() <= radius {
assert!( assert!(
maze.get(&pos).is_some(), maze.get(&pos).is_some(),
"Expected tile at {pos:?} to exist", "Expected tile at {:?} to exist",
pos
); );
} }
} }

View File

@ -1,4 +1,3 @@
use claims::assert_some;
use hexlab::prelude::*; use hexlab::prelude::*;
use rstest::rstest; use rstest::rstest;
@ -47,10 +46,11 @@ fn generator_type(
// Check that each tile has at least one open wall // Check that each tile has at least one open wall
for &pos in maze.keys() { for &pos in maze.keys() {
let walls = assert_some!(maze.get_walls(&pos)); let walls = maze.get_walls(&pos).unwrap();
assert!( assert!(
walls.count() < 6, walls.count() < 6,
"Tile at {pos:?} should have at least one open wall", "Tile at {:?} should have at least one open wall",
pos
); );
} }
} }

View File

@ -1,4 +1,3 @@
use claims::assert_some;
use hexlab::prelude::*; use hexlab::prelude::*;
#[test] #[test]
@ -11,8 +10,9 @@ fn hex_maze_creation_and_basic_operations() {
assert_eq!(maze.count(), 1); assert_eq!(maze.count(), 1);
assert!(!maze.is_empty()); assert!(!maze.is_empty());
let tile = assert_some!(maze.get(&center)); let tile = maze.get(&center);
assert_eq!(tile.pos(), center); assert!(tile.is_some());
assert_eq!(tile.unwrap().pos(), center);
} }
#[test] #[test]
@ -26,7 +26,7 @@ fn hex_maze_wall_operations() {
let _ = maze.add_tile_wall(&center, direction); let _ = maze.add_tile_wall(&center, direction);
} }
let walls = assert_some!(maze.get_walls(&center)); let walls = maze.get_walls(&center).unwrap();
assert_eq!(walls.count(), 6); assert_eq!(walls.count(), 6);
// Remove walls // Remove walls
@ -34,7 +34,7 @@ fn hex_maze_wall_operations() {
let _ = maze.remove_tile_wall(&center, direction); let _ = maze.remove_tile_wall(&center, direction);
} }
let walls = assert_some!(maze.get_walls(&center)); let walls = maze.get_walls(&center).unwrap();
assert_eq!(walls.count(), 0); assert_eq!(walls.count(), 0);
} }