Merge branch 'feature/add-traits'

This commit is contained in:
Kristofers Solo 2024-12-26 18:15:59 +02:00
commit dcbe06fb8c
14 changed files with 466 additions and 305 deletions

2
Cargo.lock generated
View File

@ -2375,7 +2375,7 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "hexlab"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"bevy",
"bevy_reflect",

View File

@ -1,7 +1,7 @@
[package]
name = "hexlab"
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
version = "0.4.0"
version = "0.5.0"
edition = "2021"
description = "A hexagonal maze generation and manipulation library"
repository = "https://github.com/kristoferssolo/hexlab"

View File

@ -1,25 +1,5 @@
use crate::{GeneratorType, HexMaze};
use crate::{errors::MazeBuilderError, GeneratorType, Maze};
use hexx::Hex;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MazeBuilderError {
/// Occurs when attempting to build a maze without specifying a radius.
#[error("Radius must be specified to build a maze")]
NoRadius,
/// Occurs when the specified radius is too large.
#[error("Radius {0} is too large. Maximum allowed radius is {1}")]
RadiusTooLarge(u32, u32),
/// Occurs when the specified start position is outside the maze bounds.
#[error("Start position {0:?} is outside maze bounds")]
InvalidStartPosition(Hex),
/// Occurs when maze generation fails.
#[error("Failed to generate maze: {0}")]
GenerationError(String),
}
/// A builder pattern for creating hexagonal mazes.
///
@ -85,7 +65,7 @@ pub struct MazeBuilder {
impl MazeBuilder {
/// Creates a new [`MazeBuilder`] instance with default settings.
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
@ -101,7 +81,7 @@ impl MazeBuilder {
/// # Arguments
///
/// - `radius` - The number of tiles from the center to the edge of the hexagon.
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn with_radius(mut self, radius: u16) -> Self {
self.radius = Some(radius);
@ -115,7 +95,7 @@ impl MazeBuilder {
/// # Arguments
///
/// - `seed` - The random seed value.
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn with_seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
@ -129,7 +109,7 @@ impl MazeBuilder {
/// # Arguments
///
/// - `generator_type` - The maze generation algorithm to use.
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn with_generator(mut self, generator_type: GeneratorType) -> Self {
self.generator_type = generator_type;
@ -140,7 +120,7 @@ impl MazeBuilder {
/// # Arguments
///
/// - `pos` - The hexagonal coordinates for the starting position.
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn with_start_position(mut self, pos: Hex) -> Self {
self.start_position = Some(pos);
@ -172,12 +152,12 @@ impl MazeBuilder {
/// let maze = result.unwrap();
/// assert!(!maze.is_empty());
/// ```
pub fn build(self) -> Result<HexMaze, MazeBuilderError> {
pub fn build(self) -> Result<Maze, MazeBuilderError> {
let radius = self.radius.ok_or(MazeBuilderError::NoRadius)?;
let mut maze = create_hex_maze(radius);
if let Some(start_pos) = self.start_position {
if maze.get_tile(&start_pos).is_none() {
if maze.get(&start_pos).is_none() {
return Err(MazeBuilderError::InvalidStartPosition(start_pos));
}
}
@ -190,8 +170,9 @@ impl MazeBuilder {
Ok(maze)
}
}
pub fn create_hex_maze(radius: u16) -> HexMaze {
let mut maze = HexMaze::new();
pub fn create_hex_maze(radius: u16) -> Maze {
let mut maze = Maze::new();
let radius = i32::from(radius);
for q in -radius..=radius {
@ -199,7 +180,7 @@ pub fn create_hex_maze(radius: u16) -> HexMaze {
let r2 = radius.min(-q + radius);
for r in r1..=r2 {
let pos = Hex::new(q, r);
maze.add_tile(pos);
maze.insert(pos);
}
}
@ -269,7 +250,7 @@ mod test {
Hex::new(1, -2),
];
for pos in expected_positions.iter() {
assert!(maze.get_tile(pos).is_some(), "Expected tile at {:?}", pos);
assert!(maze.get(pos).is_some(), "Expected tile at {:?}", pos);
}
}
}

23
src/errors.rs Normal file
View File

@ -0,0 +1,23 @@
use hexx::Hex;
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum MazeBuilderError {
/// Occurs when attempting to build a maze without specifying a radius.
#[error("Radius must be specified to build a maze")]
NoRadius,
/// Occurs when the specified start position is outside the maze bounds.
#[error("Start position {0:?} is outside maze bounds")]
InvalidStartPosition(Hex),
/// Occurs when maze generation fails.
#[error("Failed to generate maze: {0}")]
GenerationError(String),
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum MazeError {
#[error("Invalid coordinate: {0:?}")]
InvalidCoordinate(Hex),
}

View File

@ -1,9 +1,9 @@
use crate::HexMaze;
use crate::Maze;
use hexx::{EdgeDirection, Hex};
use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng};
use std::collections::HashSet;
pub(super) fn generate_backtracking(maze: &mut HexMaze, start_pos: Option<Hex>, seed: Option<u64>) {
pub(super) fn generate_backtracking(maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
if maze.is_empty() {
return;
}
@ -21,7 +21,7 @@ pub(super) fn generate_backtracking(maze: &mut HexMaze, start_pos: Option<Hex>,
}
fn recursive_backtrack<R: Rng>(
maze: &mut HexMaze,
maze: &mut Maze,
current: Hex,
visited: &mut HashSet<Hex>,
rng: &mut R,
@ -32,9 +32,9 @@ fn recursive_backtrack<R: Rng>(
for direction in directions {
let neighbor = current + direction;
if maze.get_tile(&neighbor).is_some() && !visited.contains(&neighbor) {
maze.remove_tile_wall(&current, direction);
maze.remove_tile_wall(&neighbor, direction.const_neg());
if maze.get(&neighbor).is_some() && !visited.contains(&neighbor) {
let _ = maze.remove_tile_wall(&current, direction);
let _ = maze.remove_tile_wall(&neighbor, direction.const_neg());
recursive_backtrack(maze, neighbor, visited, rng);
}
}
@ -100,7 +100,7 @@ mod test {
for dir in EdgeDirection::ALL_DIRECTIONS {
let neighbor = current + dir;
if let Some(walls) = maze.get_walls(&current) {
if !walls.contains(dir) && maze.get_tile(&neighbor).is_some() {
if !walls.contains(&dir) && maze.get(&neighbor).is_some() {
to_visit.push(neighbor);
}
}

View File

@ -1,5 +1,5 @@
mod backtrack;
use crate::HexMaze;
use crate::Maze;
use backtrack::generate_backtracking;
#[cfg(feature = "bevy")]
use bevy::prelude::*;
@ -16,7 +16,7 @@ pub enum GeneratorType {
RecursiveBacktracking,
}
impl GeneratorType {
pub fn generate(&self, maze: &mut HexMaze, start_pos: Option<Hex>, seed: Option<u64>) {
pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
match self {
Self::RecursiveBacktracking => generate_backtracking(maze, start_pos, seed),
}

View File

@ -7,7 +7,6 @@
//! - Efficient bit-flag representation of walls
//! - Multiple maze generation algorithms
//! - Maze builder pattern for easy maze creation
//!
//! # Examples
//!
@ -36,7 +35,7 @@
//! .build()
//! .expect("Failed to create maze");
//!
//! assert!(maze.get_tile(&Hex::new(1, -1)).is_some());
//! assert!(maze.get(&Hex::new(1, -1)).is_some());
//!```
//!
//! Manipulating walls:
@ -45,24 +44,28 @@
//! use hexlab::prelude::*;
//!
//! let mut walls = Walls::empty();
//! walls.add(EdgeDirection::FLAT_NORTH);
//! assert!(walls.contains(EdgeDirection::FLAT_NORTH));
//! assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
//! assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
//! assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
//! assert!(!walls.contains(&EdgeDirection::FLAT_SOUTH));
//!```
mod builder;
pub mod errors;
mod generator;
mod maze;
mod tile;
pub mod traits;
mod walls;
pub use builder::{MazeBuilder, MazeBuilderError};
pub use builder::MazeBuilder;
pub use errors::*;
pub use generator::GeneratorType;
pub use maze::HexMaze;
pub use tile::HexTile;
pub use maze::Maze;
pub use tile::Tile;
pub use traits::*;
pub use walls::Walls;
/// Prelude module containing commonly used types
pub mod prelude {
pub use super::{GeneratorType, HexMaze, HexTile, MazeBuilder, MazeBuilderError, Walls};
pub use super::{errors::*, traits::*, GeneratorType, Maze, MazeBuilder, Tile, Walls};
pub use hexx::{EdgeDirection, Hex, HexLayout};
}

View File

@ -1,4 +1,8 @@
use super::{HexTile, Walls};
use super::{Tile, Walls};
use crate::{
errors::MazeError,
traits::{TilePosition, WallStorage},
};
#[cfg(feature = "bevy")]
use bevy::prelude::*;
#[cfg(feature = "bevy_reflect")]
@ -12,15 +16,14 @@ use std::ops::{Deref, DerefMut};
///
/// This struct stores the layout of a hexagonal maze, including the positions
/// of tiles and their associated walls.
#[allow(clippy::module_name_repetitions)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy", derive(Component))]
#[cfg_attr(feature = "bevy", reflect(Component))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct HexMaze(HashMap<Hex, HexTile>);
pub struct Maze(HashMap<Hex, Tile>);
impl HexMaze {
impl Maze {
/// Creates a new empty maze
///
/// # Examples
@ -28,18 +31,23 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let maze = HexMaze::new();
/// let maze = Maze::new();
///
/// assert!(maze.is_empty());
/// assert_eq!(maze.len(), 0);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Adds a new tile at the specified coordinates
/// Inserts a new tile at the specified coordinates.
///
/// If the map did not have this key present, [`None`] is returned.
///
/// If the map did have this key present, the value is updated, and the old
/// value is returned.
///
/// # Arguments
///
@ -50,43 +58,48 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// let coord = Hex::ZERO;
/// maze.add_tile(coord);
///
/// assert_eq!(maze.len(), 1);
/// assert!(!maze.is_empty());
/// assert_eq!(maze.insert(coord), None);
/// assert_eq!(maze.insert(coord), Some(Tile::new(coord)));
/// ```
pub fn add_tile(&mut self, coords: Hex) {
let tile = HexTile::new(coords);
self.0.insert(coords, tile);
pub fn insert(&mut self, coords: Hex) -> Option<Tile> {
let tile = Tile::new(coords);
self.0.insert(coords, tile)
}
/// Adds a wall in the specified direction at the given coordinates.
/// Adds a new tile at the specified coordinates.
///
/// If the map did not have this key present, [`None`] is returned.
///
/// If the map did have this key present, the value is updated, and the old
/// value is returned.
///
/// It is recommended to use [`insert`].
///
/// [`insert`]: Maze::insert
///
/// # Arguments
///
/// - `coord` - The hexagonal coordinates of the tile.
/// - `direction` - The direction in which to add the wall.
/// - `coords` - The hexagonal coordinates where the tile should be added.
/// - `tile` - The tile to insert to.
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// let coord = Hex::ZERO;
/// maze.add_tile(coord);
/// let tile1 = Tile::new(coord);
/// let tile2 = Tile::new(Hex::new(1, 1));
///
/// maze.add_wall(coord, EdgeDirection::FLAT_NORTH);
/// let walls = maze.get_walls(&coord);
/// assert!(walls.is_some());
/// assert!(walls.unwrap().contains(EdgeDirection::FLAT_NORTH));
/// assert_eq!(maze.insert_with_tile(coord, tile1.clone()), None);
/// assert_eq!(maze.insert_with_tile(coord, tile2), Some(tile1));
/// ```
pub fn add_wall(&mut self, coord: Hex, direction: EdgeDirection) {
if let Some(tile) = self.0.get_mut(&coord) {
tile.walls.add(direction);
}
pub fn insert_with_tile(&mut self, coords: Hex, tile: Tile) -> Option<Tile> {
self.0.insert(coords, tile)
}
/// Returns a reference to the tile at the specified coordinates.
@ -100,20 +113,26 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// let coord = Hex::ZERO;
/// maze.add_tile(coord);
/// maze.insert(coord);
///
/// assert!(maze.get_tile(&coord).is_some());
/// assert!(maze.get_tile(&Hex::new(1, 1)).is_none());
/// assert!(maze.get(&coord).is_some());
/// assert!(maze.get(&Hex::new(1, 1)).is_none());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn get_tile(&self, coord: &Hex) -> Option<&HexTile> {
pub fn get(&self, coord: &Hex) -> Option<&Tile> {
self.0.get(coord)
}
/// Returns an optional reference to the walls at the specified coordinates.
#[inline]
#[must_use]
pub fn get_mut(&mut self, coord: &Hex) -> Option<&mut Tile> {
self.0.get_mut(coord)
}
/// Returns an optional mutable reference to the walls at the specified coordinates.
///
/// # Arguments
///
@ -124,16 +143,43 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// let coord = Hex::new(0, 0);
/// maze.add_tile(coord);
/// maze.insert(coord);
///
/// maze.add_wall(coord, EdgeDirection::FLAT_NORTH);
/// maze.add_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
/// let walls = maze.get_walls(&coord).unwrap();
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
/// assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
/// ```
#[inline]
#[must_use]
pub fn get_walls(&self, coord: &Hex) -> Option<&Walls> {
self.0.get(coord).map(HexTile::walls)
self.0.get(coord).map(Tile::walls)
}
/// Returns an optional mutable reference to the walls at the specified coordinates.
///
/// # Arguments
///
/// - `coord` - The hexagonal coordinates of the tile whose walls to retrieve.
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = Maze::new();
/// let coord = Hex::new(0, 0);
/// maze.insert(coord);
///
/// maze.add_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
/// let mut walls = maze.get_walls_mut(&coord).unwrap();
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
/// ```
#[inline]
#[must_use]
pub fn get_walls_mut(&mut self, coord: &Hex) -> Option<&mut Walls> {
self.0.get_mut(coord).map(Tile::walls_mut)
}
/// Returns the number of tiles in the maze.
@ -143,16 +189,16 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// assert_eq!(maze.len(), 0);
///
/// maze.add_tile(Hex::new(0, 0));
/// maze.insert(Hex::new(0, 0));
/// assert_eq!(maze.len(), 1);
///
/// maze.add_tile(Hex::new(1, -1));
/// maze.insert(Hex::new(1, -1));
/// assert_eq!(maze.len(), 2);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
@ -165,54 +211,147 @@ impl HexMaze {
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// assert!(maze.is_empty());
///
/// maze.add_tile(Hex::ZERO);
/// maze.insert(Hex::ZERO);
/// assert!(!maze.is_empty());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Adds a wall from a tile in the specified direction.
///
/// # Arguments
///
/// - `coord` - The hexagonal coordinates of the tile.
/// - `direction` - The direction of the wall to remove.
///
/// # Errors
///
/// Returns `MazeError::InvalidCoordinate` if the specified coordinate does not exist in the maze.
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// // Create a maze with a single tile at the origin
/// let mut tile = Tile::new(Hex::ZERO);
/// tile.walls_mut().toggle(Walls::all_directions());
/// let mut maze = Maze::from([tile]);
///
/// // Initially, the tile should have no walls
/// assert!(maze.get_walls(&Hex::ZERO).unwrap().is_empty());
///
/// // Add a wall to the north
/// assert!(maze.add_tile_wall(&Hex::ZERO, EdgeDirection::FLAT_NORTH).is_ok());
///
/// // Check that the wall was added
/// let walls = maze.get_walls(&Hex::ZERO).unwrap();
/// assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
/// assert_eq!(walls.count(), 1);
///
/// // Adding the same wall again should return true (no change)
/// assert_eq!(maze.add_tile_wall(&Hex::ZERO, EdgeDirection::FLAT_NORTH), Ok(true));
///
/// // Adding a wall to a non-existent tile should return an error
/// let invalid_coord = Hex::new(1, 1);
/// assert_eq!(
/// maze.add_tile_wall(&invalid_coord, EdgeDirection::FLAT_NORTH),
/// Err(MazeError::InvalidCoordinate(invalid_coord))
/// );
/// ```
pub fn add_tile_wall(
&mut self,
coord: &Hex,
direction: EdgeDirection,
) -> Result<bool, MazeError> {
self.0
.get_mut(coord)
.map(|tile| tile.walls.insert(direction))
.ok_or(MazeError::InvalidCoordinate(*coord))
}
/// Removes a wall from a tile in the specified direction.
///
/// # Arguments
///
/// - `coord` - The hexagonal coordinates of the tile.
/// - `direction` - The direction of the wall to remove.
///
/// # Errors
///
/// Returns `MazeError::InvalidCoordinate` if the specified coordinate does not exist in the maze.
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let mut maze = HexMaze::new();
/// let mut maze = Maze::new();
/// let coord = Hex::ZERO;
/// maze.add_tile(coord);
/// maze.insert(coord);
///
/// maze.add_wall(coord, EdgeDirection::FLAT_NORTH);
/// maze.add_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
/// maze.remove_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
///
/// let walls = maze.get_walls(&coord).unwrap();
/// assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
/// assert!(!walls.contains(&EdgeDirection::FLAT_NORTH));
/// ```
pub fn remove_tile_wall(&mut self, coord: &Hex, direction: EdgeDirection) {
if let Some(tile) = self.0.get_mut(coord) {
tile.walls.remove(direction);
}
pub fn remove_tile_wall(
&mut self,
coord: &Hex,
direction: EdgeDirection,
) -> Result<bool, MazeError> {
self.0
.get_mut(coord)
.map(|tile| tile.walls.remove(direction))
.ok_or(MazeError::InvalidCoordinate(*coord))
}
}
impl Deref for HexMaze {
type Target = HashMap<Hex, HexTile>;
impl FromIterator<Hex> for Maze {
fn from_iter<T: IntoIterator<Item = Hex>>(iter: T) -> Self {
Self(iter.into_iter().map(|hex| (hex, Tile::new(hex))).collect())
}
}
impl FromIterator<Tile> for Maze {
fn from_iter<T: IntoIterator<Item = Tile>>(iter: T) -> Self {
Self(iter.into_iter().map(|tile| (tile.pos(), tile)).collect())
}
}
impl FromIterator<(Hex, Tile)> for Maze {
fn from_iter<T: IntoIterator<Item = (Hex, Tile)>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl<const N: usize> From<[Hex; N]> for Maze {
fn from(value: [Hex; N]) -> Self {
value.into_iter().collect()
}
}
impl<const N: usize> From<[Tile; N]> for Maze {
fn from(value: [Tile; N]) -> Self {
value.into_iter().collect()
}
}
impl Deref for Maze {
type Target = HashMap<Hex, Tile>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for HexMaze {
impl DerefMut for Maze {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}

View File

@ -1,4 +1,7 @@
use super::Walls;
#[cfg(feature = "bevy_reflect")]
use crate::traits::WorldPositionable;
use crate::traits::{TilePosition, WallStorage};
#[cfg(feature = "bevy")]
use bevy::prelude::*;
use hexx::Hex;
@ -9,18 +12,90 @@ use std::fmt::Display;
/// Represents a single hexagonal tile in the maze
///
/// Each tile has a position and a set of walls defining its boundaries.
#[allow(clippy::module_name_repetitions)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy", derive(Component))]
#[cfg_attr(feature = "bevy", reflect(Component))]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct HexTile {
pub struct Tile {
pub(crate) pos: Hex,
pub(crate) walls: Walls,
}
impl HexTile {
impl TilePosition for Tile {
/// Returns position of the tile
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let tile = Tile::new(Hex::new(2, -2));
/// assert_eq!(tile.pos(), Hex::new(2, -2));
/// ```
#[inline]
fn pos(&self) -> Hex {
self.pos
}
}
impl WallStorage for Tile {
/// Returns an immutable reference to the tile's walls
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let tile = Tile::new(Hex::ZERO);
/// assert_eq!(*tile.walls(), Walls::default());
/// ```
#[inline]
fn walls(&self) -> &Walls {
&self.walls
}
/// Returns a mutable reference to the tile's walls
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let tile = Tile::new(Hex::ZERO);
/// assert_eq!(*tile.walls(), Walls::default());
/// ```
#[inline]
fn walls_mut(&mut self) -> &mut Walls {
&mut self.walls
}
}
#[cfg(feature = "bevy_reflect")]
impl WorldPositionable for Tile {
/// Converts the tile's position to a 2D vector based on the given layout.
///
/// # Arguments
///
/// - `layout` - The hexagonal layout used for conversion.
#[inline]
fn to_vec2(&self, layout: &HexLayout) -> glam::Vec2 {
layout.hex_to_world_pos(self.pos)
}
/// Converts the tile's position to a 3D vector based on the given layout.
///
/// # Arguments
///
/// - `layout` - The hexagonal layout used for conversion.
#[inline]
fn to_vec3(&self, layout: &HexLayout) -> glam::Vec3 {
let pos = self.to_vec2(layout);
glam::Vec3::new(pos.x, 0., pos.y)
}
}
impl Tile {
/// Creates a new tile with the given position and default walls.
///
/// # Arguments
@ -32,7 +107,7 @@ impl HexTile {
/// ```
/// use hexlab::prelude::*;
///
/// let tile = HexTile::new(Hex::new(1, -1));
/// let tile = Tile::new(Hex::new(1, -1));
/// assert_eq!(tile.pos(), Hex::new(1, -1));
/// assert_eq!(*tile.walls(), Walls::default());
/// ```
@ -43,68 +118,9 @@ impl HexTile {
walls: Walls::default(),
}
}
/// Returns a reference to the tile's walls
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let tile = HexTile::new(Hex::ZERO);
/// assert_eq!(*tile.walls(), Walls::default());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[must_use]
pub const fn walls(&self) -> &Walls {
&self.walls
}
/// Returns position of the tile
///
/// # Examples
///
/// ```
/// use hexlab::prelude::*;
///
/// let tile = HexTile::new(Hex::new(2, -2));
/// assert_eq!(tile.pos(), Hex::new(2, -2));
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[must_use]
pub const fn pos(&self) -> Hex {
self.pos
}
/// Converts the tile's position to a 2D vector based on the given layout.
///
/// # Arguments
///
/// - `layout` - The hexagonal layout used for conversion.
#[cfg(feature = "bevy_reflect")]
#[cfg_attr(not(debug_assertions), inline)]
#[must_use]
pub fn to_vec2(&self, layout: &HexLayout) -> glam::Vec2 {
layout.hex_to_world_pos(self.pos)
}
/// Converts the tile's position to a 3D vector based on the given layout.
///
/// # Arguments
///
/// - `layout` - The hexagonal layout used for conversion.
#[cfg(feature = "bevy_reflect")]
#[cfg_attr(not(debug_assertions), inline)]
#[must_use]
pub fn to_vec3(&self, layout: &HexLayout) -> glam::Vec3 {
use glam::Vec3;
let pos = self.to_vec2(layout);
Vec3::new(pos.x, 0., pos.y)
}
}
impl From<Hex> for HexTile {
impl From<Hex> for Tile {
fn from(value: Hex) -> Self {
Self {
pos: value,
@ -113,7 +129,7 @@ impl From<Hex> for HexTile {
}
}
impl Display for HexTile {
impl Display for Tile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.x, self.pos.y)
}
@ -130,19 +146,6 @@ mod test {
Hex::new(rng.gen(), rng.gen())
}
#[test]
fn tile_modification() {
let hex = random_hex();
let mut tile = HexTile::new(hex);
// Modify walls
tile.walls.remove(EdgeDirection::FLAT_TOP);
assert!(!tile.walls.contains(EdgeDirection::FLAT_TOP));
tile.walls.add(EdgeDirection::FLAT_TOP);
assert!(tile.walls.contains(EdgeDirection::FLAT_TOP));
}
#[test]
fn different_positions() {
let positions = [Hex::ZERO, Hex::new(1, 0), Hex::new(-1, 1), Hex::new(2, -2)];
@ -150,7 +153,7 @@ mod test {
// Create tiles at different positions
let tiles = positions
.iter()
.map(|&pos| HexTile::new(pos))
.map(|&pos| Tile::new(pos))
.collect::<Vec<_>>();
// Verify each tile has correct position
@ -170,7 +173,7 @@ mod test {
];
for pos in extreme_positions {
let tile = HexTile::new(pos);
let tile = Tile::new(pos);
assert_eq!(tile.pos, pos);
}
}
@ -178,7 +181,7 @@ mod test {
#[test]
fn hex_tile_creation_and_properties() {
let hex = random_hex();
let tile = HexTile::new(hex);
let tile = Tile::new(hex);
assert_eq!(tile.pos(), hex);
assert!(tile.walls().is_enclosed());
@ -187,7 +190,7 @@ mod test {
#[test]
fn hex_tile_from_hex() {
let hex = random_hex();
let tile = HexTile::from(hex);
let tile = Tile::from(hex);
assert_eq!(tile.pos, hex);
assert_eq!(tile.walls, Walls::default());
@ -196,7 +199,7 @@ mod test {
#[test]
fn hex_hex_into_tile() {
let hex = random_hex();
let tile: HexTile = hex.into();
let tile: Tile = hex.into();
assert_eq!(tile.pos, hex);
assert_eq!(tile.walls, Walls::default());
@ -204,16 +207,16 @@ mod test {
#[test]
fn hex_tile_display() {
let tile = HexTile::new(Hex::new(3, -3));
let tile = Tile::new(Hex::new(3, -3));
assert_eq!(format!("{tile}"), "(3,-3)");
}
#[test]
fn hex_tile_wall_modifications() {
let mut tile = HexTile::new(Hex::ZERO);
let mut tile = Tile::new(Hex::ZERO);
for direction in EdgeDirection::ALL_DIRECTIONS {
tile.walls.add(direction);
tile.walls.insert(direction);
}
assert_eq!(tile.walls.count(), 6);
@ -231,7 +234,7 @@ mod test {
#[test]
fn hex_tile_to_vec2() {
let layout = HexLayout::default();
let tile = HexTile::new(Hex::new(1, 0));
let tile = Tile::new(Hex::new(1, 0));
let vec2 = tile.to_vec2(&layout);
assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
}
@ -239,7 +242,7 @@ mod test {
#[test]
fn hex_tile_to_vec3() {
let layout = HexLayout::default();
let tile = HexTile::new(Hex::new(0, 1));
let tile = Tile::new(Hex::new(0, 1));
let vec3 = tile.to_vec3(&layout);
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
}

22
src/traits.rs Normal file
View File

@ -0,0 +1,22 @@
use crate::Walls;
use hexx::Hex;
pub trait TilePosition {
/// Returns position of the tile
#[must_use]
fn pos(&self) -> Hex;
}
#[cfg(feature = "bevy_reflect")]
pub trait WorldPositionable {
#[must_use]
fn to_vec2(&self, layout: &hexx::HexLayout) -> glam::Vec2;
#[must_use]
fn to_vec3(&self, layout: &hexx::HexLayout) -> glam::Vec3;
}
pub trait WallStorage {
#[must_use]
fn walls(&self) -> &Walls;
fn walls_mut(&mut self) -> &mut Walls;
}

View File

@ -16,15 +16,15 @@ use hexx::EdgeDirection;
///
/// // Create a hexagon with all walls
/// let walls = Walls::new();
/// assert!(walls.is_closed());
/// assert!(walls.is_enclosed());
///
/// // Create a hexagon with no walls
/// let mut walls = Walls::empty();
/// assert!(walls.is_empty());
///
/// // Add specific walls
/// walls.add(EdgeDirection::FLAT_NORTH);
/// walls.add(EdgeDirection::FLAT_SOUTH);
/// walls.insert(EdgeDirection::FLAT_NORTH);
/// walls.insert(EdgeDirection::FLAT_SOUTH);
/// assert_eq!(walls.count(), 2);
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -47,7 +47,7 @@ impl Walls {
/// let walls = Walls::new();
/// assert!(walls.is_enclosed());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
@ -63,7 +63,7 @@ impl Walls {
/// let walls = Walls::empty();
/// assert!(walls.is_empty());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self(0)
@ -79,17 +79,21 @@ impl Walls {
/// let walls = Walls::empty();
/// assert!(walls.is_empty());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
/// Adds a wall in the specified direction.
/// Insert a wall in the specified direction.
///
/// # Arguments
///
/// - `direction` - The direction in which to add the wall.
/// - `direction` - The direction in which to insert the wall.
///
/// # Returns
///
/// Returns `true` if a wall was present, `false` otherwise.
///
/// # Examples
///
@ -99,22 +103,24 @@ impl Walls {
/// let mut walls = Walls::empty();
/// assert_eq!(walls.count(), 0);
///
/// walls.add(1);
/// assert!(!walls.insert(1));
/// assert_eq!(walls.count(), 1);
///
/// walls.add(1);
/// assert!(walls.insert(1));
/// assert_eq!(walls.count(), 1);
///
/// walls.add(EdgeDirection::FLAT_NORTH);
/// assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
/// assert_eq!(walls.count(), 2);
///
/// ```
#[cfg_attr(not(debug_assertions), inline)]
pub fn add<T>(&mut self, direction: T)
#[inline]
pub fn insert<T>(&mut self, direction: T) -> bool
where
T: Into<Self> + Copy,
{
self.0 |= direction.into().0;
let mask = direction.into().0;
let was_present = self.0 & mask != 0;
self.0 |= mask;
was_present
}
/// Removes a wall in the specified direction.
@ -123,6 +129,10 @@ impl Walls {
///
/// - `direction` - The direction from which to remove the wall.
///
/// # Returns
///
/// Returns `true` if a wall was present and removed, `false` otherwise.
///
/// # Examples
///
/// ```
@ -139,16 +149,15 @@ impl Walls {
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
/// assert_eq!(walls.count(), 4);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
pub fn remove<T>(&mut self, direction: T) -> bool
where
T: Into<Self> + Copy,
{
let was_removed = self.contains(direction);
if was_removed {
self.0 &= !direction.into().0;
}
was_removed
let mask = direction.into().0;
let was_present = self.0 & mask != 0;
self.0 &= !mask;
was_present
}
/// Checks if there is a wall in the specified direction.
@ -163,17 +172,17 @@ impl Walls {
/// use hexlab::prelude::*;
///
/// let mut walls = Walls::empty();
/// walls.add(EdgeDirection::FLAT_NORTH);
/// walls.insert(EdgeDirection::FLAT_NORTH);
///
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
/// assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
/// assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
/// assert!(!walls.contains(&EdgeDirection::FLAT_SOUTH));
/// ```
#[cfg_attr(not(debug_assertions), inline)]
pub fn contains<T>(&self, other: T) -> bool
#[inline]
pub fn contains<T>(&self, direction: &T) -> bool
where
T: Into<Self> + Copy,
{
self.0 & other.into().0 != 0
self.0 & (*direction).into().0 != 0
}
/// Returns the raw bit representation of the walls
@ -189,7 +198,7 @@ impl Walls {
/// let walls = Walls::empty();
/// assert_eq!(walls.as_bits(), 0);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn as_bits(&self) -> u8 {
self.0
@ -205,13 +214,13 @@ impl Walls {
/// let mut walls = Walls::empty();
/// assert!(walls.is_empty());
///
/// walls.add(0);
/// walls.insert(0);
/// assert_eq!(walls.count(), 1);
///
/// walls.add(1);
/// walls.insert(1);
/// assert_eq!(walls.count(), 2);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn count(&self) -> u8 {
u8::try_from(self.0.count_ones()).unwrap_or_default()
@ -226,7 +235,7 @@ impl Walls {
///
/// assert_eq!(Walls::all_directions().as_bits(), 0b11_1111);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub const fn all_directions() -> Self {
Self(0b11_1111)
@ -262,29 +271,10 @@ impl Walls {
where
T: Into<Self> + Copy,
{
let is_present = self.contains(direction);
if is_present {
self.remove(direction);
} else {
self.add(direction);
}
is_present
}
/// Checks if walls are present in all six directions.
///
/// # Returns
///
/// `true` if the hexagon has all possible walls, making it completely enclosed.
///
/// # Deprecated
///
/// This method is deprecated since version 0.4.0. Use `is_enclosed()` instead.
#[cfg_attr(not(debug_assertions), inline)]
#[must_use]
#[deprecated(since = "0.4.0", note = "use `walls::Walls::is_enclosed()`")]
pub fn is_closed(&self) -> bool {
self.is_enclosed()
let mask = direction.into().0;
let was_present = self.0 & mask != 0;
self.0 ^= mask;
was_present
}
/// Checks if walls are present in all six directions.
@ -304,7 +294,7 @@ impl Walls {
/// walls.remove(0);
/// assert!(!walls.is_enclosed());
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
#[must_use]
pub fn is_enclosed(&self) -> bool {
self.count() == 6
@ -317,7 +307,7 @@ impl Walls {
///
/// # Arguments
///
/// - `other` - The walls to add, specified as a `Walls` instance or any type
/// - `other` - The walls to insert, specified as a `Walls` instance or any type
/// that can be converted into `Walls`.
///
///
@ -329,10 +319,10 @@ impl Walls {
/// let mut walls = Walls::empty();
/// walls.fill([EdgeDirection::FLAT_NORTH ,EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
///
/// assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
/// assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
/// assert_eq!(walls.count(), 3);
/// ```
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
pub fn fill<T>(&mut self, other: T)
where
T: Into<Self>,
@ -398,15 +388,15 @@ mod test {
#[test]
fn as_bits_single_wall() {
let mut walls = Walls::empty();
walls.add(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_NORTH);
assert_eq!(walls.as_bits(), 0b010000);
}
#[test]
fn as_bits_multiple_walls() {
let mut walls = Walls::empty();
walls.add(EdgeDirection::FLAT_NORTH);
walls.add(EdgeDirection::FLAT_SOUTH);
walls.insert(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b010010);
}
@ -432,12 +422,12 @@ mod test {
assert_eq!(walls.as_bits(), 0);
}
// add
// insert
#[test]
fn add_single_wall() {
fn insert_single_wall() {
let mut walls = Walls::empty();
walls.add(EdgeDirection::FLAT_NORTH);
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
walls.insert(EdgeDirection::FLAT_NORTH);
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert_eq!(walls.count(), 1);
}
@ -446,30 +436,30 @@ mod test {
fn remove_existing_wall() {
let mut walls = Walls::new();
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
assert!(!walls.contains(&EdgeDirection::FLAT_NORTH));
}
#[test]
fn remove_nonexistent_wall() {
let mut walls = Walls::empty();
assert!(!walls.remove(EdgeDirection::FLAT_NORTH));
walls.add(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_NORTH);
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
}
// toggle
#[test]
fn toggle_adds_wall() {
fn toggle_wall() {
let mut walls = Walls::empty();
assert!(!walls.toggle(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
}
#[test]
fn toggle_removes_wall() {
let mut walls = Walls::new();
assert!(walls.toggle(EdgeDirection::FLAT_NORTH));
assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
assert!(!walls.contains(&EdgeDirection::FLAT_NORTH));
}
// fill
@ -477,41 +467,41 @@ mod test {
fn fill_adds_multiple_walls() {
let mut walls = Walls::empty();
walls.fill([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
assert_eq!(walls.count(), 2);
}
#[test]
fn fill_preserves_existing_walls() {
let mut walls = Walls::empty();
walls.add(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_NORTH);
walls.fill([EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
assert!(walls.contains(EdgeDirection::FLAT_SOUTH_EAST));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH_EAST));
assert_eq!(walls.count(), 3);
}
#[test]
fn from_edge_direction_conversion() {
let walls: Walls = EdgeDirection::FLAT_NORTH.into();
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert_eq!(walls.count(), 1);
}
#[test]
fn from_u8_conversion() {
let walls: Walls = 0u8.into();
assert!(walls.contains(EdgeDirection::FLAT_SOUTH_EAST));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH_EAST));
assert_eq!(walls.count(), 1);
}
#[test]
fn from_array_conversion() {
let walls: Walls = [EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH].into();
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
assert_eq!(walls.count(), 2);
}
@ -542,8 +532,8 @@ mod test {
];
let walls: Walls = directions.into_iter().collect();
assert_eq!(walls.count(), 2);
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
}
#[test]
@ -551,14 +541,14 @@ mod test {
let mut walls = Walls::empty();
// Test single bit operations
walls.add(EdgeDirection::FLAT_NORTH);
walls.insert(EdgeDirection::FLAT_NORTH);
assert_eq!(walls.as_bits(), 0b010000);
walls.add(EdgeDirection::FLAT_SOUTH);
walls.insert(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b010010);
// Test removing middle bit
walls.add(EdgeDirection::FLAT_SOUTH_EAST);
walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
assert_eq!(walls.as_bits(), 0b010011);
walls.remove(EdgeDirection::FLAT_SOUTH);
assert_eq!(walls.as_bits(), 0b010001);

View File

@ -29,7 +29,7 @@ fn valid_start_position(#[case] start_pos: Hex) {
.with_radius(3)
.with_start_position(start_pos)
.build());
assert_some!(maze.get_tile(&start_pos));
assert_some!(maze.get(&start_pos));
}
#[test]
@ -67,13 +67,13 @@ fn maze_connectivity() {
let maze = assert_ok!(MazeBuilder::new().with_radius(3).build());
// Helper function to count accessible neighbors
fn count_accessible_neighbors(maze: &HexMaze, pos: Hex) -> usize {
fn count_accessible_neighbors(maze: &Maze, pos: Hex) -> usize {
hexx::EdgeDirection::ALL_DIRECTIONS
.iter()
.filter(|&&dir| {
let neighbor = pos + dir;
if let Some(walls) = maze.get_walls(&pos) {
!walls.contains(dir) && maze.get_tile(&neighbor).is_some()
!walls.contains(&dir) && maze.get(&neighbor).is_some()
} else {
false
}
@ -107,7 +107,7 @@ fn maze_boundaries() {
let pos = Hex::new(q, r);
if q.abs() + r.abs() <= radius {
assert!(
maze.get_tile(&pos).is_some(),
maze.get(&pos).is_some(),
"Expected tile at {:?} to exist",
pos
);

View File

@ -10,12 +10,12 @@ fn generator_type(
#[case] start_pos: Option<Hex>,
#[case] seed: Option<u64>,
) {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
for q in -3..=3 {
for r in -3..=3 {
let hex = Hex::new(q, r);
if hex.length() <= 3 {
maze.add_tile(hex);
maze.insert(hex);
}
}
}
@ -36,7 +36,7 @@ fn generator_type(
for dir in EdgeDirection::ALL_DIRECTIONS {
let neighbor = current + dir;
if let Some(walls) = maze.get_walls(&current) {
if !walls.contains(dir) && maze.get_tile(&neighbor).is_some() {
if !walls.contains(&dir) && maze.get(&neighbor).is_some() {
to_visit.push(neighbor);
}
}
@ -57,7 +57,7 @@ fn generator_type(
#[test]
fn test_empty_maze() {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
GeneratorType::RecursiveBacktracking.generate(&mut maze, None, None);
assert!(
maze.is_empty(),

View File

@ -2,28 +2,28 @@ use hexlab::prelude::*;
#[test]
fn hex_maze_creation_and_basic_operations() {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
assert!(maze.is_empty());
let center = Hex::ZERO;
maze.add_tile(center);
maze.insert(center);
assert_eq!(maze.len(), 1);
assert!(!maze.is_empty());
let tile = maze.get_tile(&center);
let tile = maze.get(&center);
assert!(tile.is_some());
assert_eq!(tile.unwrap().pos(), center);
}
#[test]
fn hex_maze_wall_operations() {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
let center = Hex::ZERO;
maze.add_tile(center);
maze.insert(center);
// Add walls
for direction in EdgeDirection::ALL_DIRECTIONS {
maze.add_wall(center, direction);
let _ = maze.add_tile_wall(&center, direction);
}
let walls = maze.get_walls(&center).unwrap();
@ -31,7 +31,7 @@ fn hex_maze_wall_operations() {
// Remove walls
for direction in EdgeDirection::ALL_DIRECTIONS {
maze.remove_tile_wall(&center, direction);
let _ = maze.remove_tile_wall(&center, direction);
}
let walls = maze.get_walls(&center).unwrap();
@ -40,29 +40,29 @@ fn hex_maze_wall_operations() {
#[test]
fn hex_maze_multiple_tiles() {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
let tiles = [Hex::ZERO, Hex::new(1, -1), Hex::new(0, 1), Hex::new(-1, 1)];
for &tile in &tiles {
maze.add_tile(tile);
maze.insert(tile);
}
assert_eq!(maze.len(), tiles.len());
for &tile in &tiles {
assert!(maze.get_tile(&tile).is_some());
assert!(maze.get(&tile).is_some());
}
}
#[test]
fn hex_maze_edge_cases() {
let mut maze = HexMaze::new();
let mut maze = Maze::new();
let non_existent = Hex::new(10, 10);
// Operations on non-existent tiles should not panic
maze.add_wall(non_existent, EdgeDirection::FLAT_NORTH);
maze.remove_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
let _ = maze.add_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
let _ = maze.remove_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
assert!(maze.get_tile(&non_existent).is_none());
assert!(maze.get(&non_existent).is_none());
assert!(maze.get_walls(&non_existent).is_none());
}