mirror of
https://github.com/kristoferssolo/hexlab.git
synced 2025-10-21 19:40:34 +00:00
feat(traits): add traits
This commit is contained in:
parent
434a23b15e
commit
91b1326bd4
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2375,7 +2375,7 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A hexagonal maze generation and manipulation library"
|
description = "A hexagonal maze generation and manipulation library"
|
||||||
repository = "https://github.com/kristoferssolo/hexlab"
|
repository = "https://github.com/kristoferssolo/hexlab"
|
||||||
|
|||||||
@ -1,25 +1,5 @@
|
|||||||
use crate::{GeneratorType, HexMaze};
|
use crate::{errors::MazeBuilderError, GeneratorType, Maze};
|
||||||
use hexx::Hex;
|
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.
|
/// A builder pattern for creating hexagonal mazes.
|
||||||
///
|
///
|
||||||
@ -85,7 +65,7 @@ pub struct MazeBuilder {
|
|||||||
|
|
||||||
impl MazeBuilder {
|
impl MazeBuilder {
|
||||||
/// Creates a new [`MazeBuilder`] instance with default settings.
|
/// Creates a new [`MazeBuilder`] instance with default settings.
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
@ -101,7 +81,7 @@ impl MazeBuilder {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `radius` - The number of tiles from the center to the edge of the hexagon.
|
/// - `radius` - The number of tiles from the center to the edge of the hexagon.
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_radius(mut self, radius: u16) -> Self {
|
pub const fn with_radius(mut self, radius: u16) -> Self {
|
||||||
self.radius = Some(radius);
|
self.radius = Some(radius);
|
||||||
@ -115,7 +95,7 @@ impl MazeBuilder {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `seed` - The random seed value.
|
/// - `seed` - The random seed value.
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_seed(mut self, seed: u64) -> Self {
|
pub const fn with_seed(mut self, seed: u64) -> Self {
|
||||||
self.seed = Some(seed);
|
self.seed = Some(seed);
|
||||||
@ -129,7 +109,7 @@ impl MazeBuilder {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `generator_type` - The maze generation algorithm to use.
|
/// - `generator_type` - The maze generation algorithm to use.
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_generator(mut self, generator_type: GeneratorType) -> Self {
|
pub const fn with_generator(mut self, generator_type: GeneratorType) -> Self {
|
||||||
self.generator_type = generator_type;
|
self.generator_type = generator_type;
|
||||||
@ -140,7 +120,7 @@ impl MazeBuilder {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `pos` - The hexagonal coordinates for the starting position.
|
/// - `pos` - The hexagonal coordinates for the starting position.
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_start_position(mut self, pos: Hex) -> Self {
|
pub const fn with_start_position(mut self, pos: Hex) -> Self {
|
||||||
self.start_position = Some(pos);
|
self.start_position = Some(pos);
|
||||||
@ -172,12 +152,12 @@ impl MazeBuilder {
|
|||||||
/// let maze = result.unwrap();
|
/// let maze = result.unwrap();
|
||||||
/// assert!(!maze.is_empty());
|
/// 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 radius = self.radius.ok_or(MazeBuilderError::NoRadius)?;
|
||||||
let mut maze = create_hex_maze(radius);
|
let mut maze = create_hex_maze(radius);
|
||||||
|
|
||||||
if let Some(start_pos) = self.start_position {
|
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));
|
return Err(MazeBuilderError::InvalidStartPosition(start_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,8 +170,9 @@ impl MazeBuilder {
|
|||||||
Ok(maze)
|
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);
|
let radius = i32::from(radius);
|
||||||
|
|
||||||
for q in -radius..=radius {
|
for q in -radius..=radius {
|
||||||
@ -199,7 +180,7 @@ pub fn create_hex_maze(radius: u16) -> HexMaze {
|
|||||||
let r2 = radius.min(-q + radius);
|
let r2 = radius.min(-q + radius);
|
||||||
for r in r1..=r2 {
|
for r in r1..=r2 {
|
||||||
let pos = Hex::new(q, r);
|
let pos = Hex::new(q, r);
|
||||||
maze.add_tile(pos);
|
maze.insert(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +250,7 @@ mod test {
|
|||||||
Hex::new(1, -2),
|
Hex::new(1, -2),
|
||||||
];
|
];
|
||||||
for pos in expected_positions.iter() {
|
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
23
src/errors.rs
Normal 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),
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
use crate::HexMaze;
|
use crate::Maze;
|
||||||
use hexx::{EdgeDirection, Hex};
|
use hexx::{EdgeDirection, Hex};
|
||||||
use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng};
|
use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng};
|
||||||
use std::collections::HashSet;
|
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() {
|
if maze.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ pub(super) fn generate_backtracking(maze: &mut HexMaze, start_pos: Option<Hex>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn recursive_backtrack<R: Rng>(
|
fn recursive_backtrack<R: Rng>(
|
||||||
maze: &mut HexMaze,
|
maze: &mut Maze,
|
||||||
current: Hex,
|
current: Hex,
|
||||||
visited: &mut HashSet<Hex>,
|
visited: &mut HashSet<Hex>,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
@ -32,9 +32,9 @@ fn recursive_backtrack<R: Rng>(
|
|||||||
|
|
||||||
for direction in directions {
|
for direction in directions {
|
||||||
let neighbor = current + direction;
|
let neighbor = current + direction;
|
||||||
if maze.get_tile(&neighbor).is_some() && !visited.contains(&neighbor) {
|
if maze.get(&neighbor).is_some() && !visited.contains(&neighbor) {
|
||||||
maze.remove_tile_wall(¤t, direction);
|
let _ = maze.remove_tile_wall(¤t, direction);
|
||||||
maze.remove_tile_wall(&neighbor, direction.const_neg());
|
let _ = maze.remove_tile_wall(&neighbor, direction.const_neg());
|
||||||
recursive_backtrack(maze, neighbor, visited, rng);
|
recursive_backtrack(maze, neighbor, visited, rng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ mod test {
|
|||||||
for dir in EdgeDirection::ALL_DIRECTIONS {
|
for dir in EdgeDirection::ALL_DIRECTIONS {
|
||||||
let neighbor = current + dir;
|
let neighbor = current + dir;
|
||||||
if let Some(walls) = maze.get_walls(¤t) {
|
if let Some(walls) = maze.get_walls(¤t) {
|
||||||
if !walls.contains(dir) && maze.get_tile(&neighbor).is_some() {
|
if !walls.contains(&dir) && maze.get(&neighbor).is_some() {
|
||||||
to_visit.push(neighbor);
|
to_visit.push(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
mod backtrack;
|
mod backtrack;
|
||||||
use crate::HexMaze;
|
use crate::Maze;
|
||||||
use backtrack::generate_backtracking;
|
use backtrack::generate_backtracking;
|
||||||
#[cfg(feature = "bevy")]
|
#[cfg(feature = "bevy")]
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@ -16,7 +16,7 @@ pub enum GeneratorType {
|
|||||||
RecursiveBacktracking,
|
RecursiveBacktracking,
|
||||||
}
|
}
|
||||||
impl GeneratorType {
|
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 {
|
match self {
|
||||||
Self::RecursiveBacktracking => generate_backtracking(maze, start_pos, seed),
|
Self::RecursiveBacktracking => generate_backtracking(maze, start_pos, seed),
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/lib.rs
21
src/lib.rs
@ -7,7 +7,6 @@
|
|||||||
//! - Efficient bit-flag representation of walls
|
//! - Efficient bit-flag representation of walls
|
||||||
//! - Multiple maze generation algorithms
|
//! - Multiple maze generation algorithms
|
||||||
//! - Maze builder pattern for easy maze creation
|
//! - Maze builder pattern for easy maze creation
|
||||||
|
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
@ -36,7 +35,7 @@
|
|||||||
//! .build()
|
//! .build()
|
||||||
//! .expect("Failed to create maze");
|
//! .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:
|
//! Manipulating walls:
|
||||||
@ -45,24 +44,28 @@
|
|||||||
//! use hexlab::prelude::*;
|
//! use hexlab::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! let mut walls = Walls::empty();
|
//! let mut walls = Walls::empty();
|
||||||
//! walls.add(EdgeDirection::FLAT_NORTH);
|
//! assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
|
||||||
//! assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
//! assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
//! assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
|
//! assert!(!walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
//!```
|
//!```
|
||||||
mod builder;
|
mod builder;
|
||||||
|
pub mod errors;
|
||||||
mod generator;
|
mod generator;
|
||||||
mod maze;
|
mod maze;
|
||||||
mod tile;
|
mod tile;
|
||||||
|
pub mod traits;
|
||||||
mod walls;
|
mod walls;
|
||||||
|
|
||||||
pub use builder::{MazeBuilder, MazeBuilderError};
|
pub use builder::MazeBuilder;
|
||||||
|
pub use errors::*;
|
||||||
pub use generator::GeneratorType;
|
pub use generator::GeneratorType;
|
||||||
pub use maze::HexMaze;
|
pub use maze::Maze;
|
||||||
pub use tile::HexTile;
|
pub use tile::Tile;
|
||||||
|
pub use traits::*;
|
||||||
pub use walls::Walls;
|
pub use walls::Walls;
|
||||||
|
|
||||||
/// Prelude module containing commonly used types
|
/// Prelude module containing commonly used types
|
||||||
pub mod prelude {
|
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};
|
pub use hexx::{EdgeDirection, Hex, HexLayout};
|
||||||
}
|
}
|
||||||
|
|||||||
253
src/maze.rs
253
src/maze.rs
@ -1,4 +1,8 @@
|
|||||||
use super::{HexTile, Walls};
|
use super::{Tile, Walls};
|
||||||
|
use crate::{
|
||||||
|
errors::MazeError,
|
||||||
|
traits::{TilePosition, WallStorage},
|
||||||
|
};
|
||||||
#[cfg(feature = "bevy")]
|
#[cfg(feature = "bevy")]
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[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
|
/// This struct stores the layout of a hexagonal maze, including the positions
|
||||||
/// of tiles and their associated walls.
|
/// of tiles and their associated walls.
|
||||||
#[allow(clippy::module_name_repetitions)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
||||||
#[cfg_attr(feature = "bevy", derive(Component))]
|
#[cfg_attr(feature = "bevy", derive(Component))]
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
#[cfg_attr(feature = "bevy", reflect(Component))]
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[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
|
/// Creates a new empty maze
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -28,18 +31,23 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let maze = HexMaze::new();
|
/// let maze = Maze::new();
|
||||||
///
|
///
|
||||||
/// assert!(maze.is_empty());
|
/// assert!(maze.is_empty());
|
||||||
/// assert_eq!(maze.len(), 0);
|
/// assert_eq!(maze.len(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
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
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -50,43 +58,48 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::ZERO;
|
/// let coord = Hex::ZERO;
|
||||||
/// maze.add_tile(coord);
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(maze.len(), 1);
|
/// assert_eq!(maze.insert(coord), None);
|
||||||
/// assert!(!maze.is_empty());
|
/// assert_eq!(maze.insert(coord), Some(Tile::new(coord)));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_tile(&mut self, coords: Hex) {
|
pub fn insert(&mut self, coords: Hex) -> Option<Tile> {
|
||||||
let tile = HexTile::new(coords);
|
let tile = Tile::new(coords);
|
||||||
self.0.insert(coords, tile);
|
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
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `coord` - The hexagonal coordinates of the tile.
|
/// - `coords` - The hexagonal coordinates where the tile should be added.
|
||||||
/// - `direction` - The direction in which to add the wall.
|
/// - `tile` - The tile to insert to.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::ZERO;
|
/// 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);
|
/// assert_eq!(maze.insert_with_tile(coord, tile1.clone()), None);
|
||||||
/// let walls = maze.get_walls(&coord);
|
/// assert_eq!(maze.insert_with_tile(coord, tile2), Some(tile1));
|
||||||
/// assert!(walls.is_some());
|
|
||||||
/// assert!(walls.unwrap().contains(EdgeDirection::FLAT_NORTH));
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_wall(&mut self, coord: Hex, direction: EdgeDirection) {
|
pub fn insert_with_tile(&mut self, coords: Hex, tile: Tile) -> Option<Tile> {
|
||||||
if let Some(tile) = self.0.get_mut(&coord) {
|
self.0.insert(coords, tile)
|
||||||
tile.walls.add(direction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the tile at the specified coordinates.
|
/// Returns a reference to the tile at the specified coordinates.
|
||||||
@ -100,20 +113,26 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::ZERO;
|
/// let coord = Hex::ZERO;
|
||||||
/// maze.add_tile(coord);
|
/// maze.insert(coord);
|
||||||
///
|
///
|
||||||
/// assert!(maze.get_tile(&coord).is_some());
|
/// assert!(maze.get(&coord).is_some());
|
||||||
/// assert!(maze.get_tile(&Hex::new(1, 1)).is_none());
|
/// assert!(maze.get(&Hex::new(1, 1)).is_none());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_tile(&self, coord: &Hex) -> Option<&HexTile> {
|
pub fn get(&self, coord: &Hex) -> Option<&Tile> {
|
||||||
self.0.get(coord)
|
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
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -124,16 +143,43 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::new(0, 0);
|
/// 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();
|
/// 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> {
|
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.
|
/// Returns the number of tiles in the maze.
|
||||||
@ -143,16 +189,16 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// assert_eq!(maze.len(), 0);
|
/// assert_eq!(maze.len(), 0);
|
||||||
///
|
///
|
||||||
/// maze.add_tile(Hex::new(0, 0));
|
/// maze.insert(Hex::new(0, 0));
|
||||||
/// assert_eq!(maze.len(), 1);
|
/// assert_eq!(maze.len(), 1);
|
||||||
///
|
///
|
||||||
/// maze.add_tile(Hex::new(1, -1));
|
/// maze.insert(Hex::new(1, -1));
|
||||||
/// assert_eq!(maze.len(), 2);
|
/// assert_eq!(maze.len(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
@ -165,54 +211,147 @@ impl HexMaze {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// assert!(maze.is_empty());
|
/// assert!(maze.is_empty());
|
||||||
///
|
///
|
||||||
/// maze.add_tile(Hex::ZERO);
|
/// maze.insert(Hex::ZERO);
|
||||||
/// assert!(!maze.is_empty());
|
/// assert!(!maze.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
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.
|
/// Removes a wall from a tile in the specified direction.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `coord` - The hexagonal coordinates of the tile.
|
/// - `coord` - The hexagonal coordinates of the tile.
|
||||||
/// - `direction` - The direction of the wall to remove.
|
/// - `direction` - The direction of the wall to remove.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns `MazeError::InvalidCoordinate` if the specified coordinate does not exist in the maze.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = HexMaze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::ZERO;
|
/// 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);
|
/// maze.remove_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
|
||||||
///
|
///
|
||||||
/// let walls = maze.get_walls(&coord).unwrap();
|
/// 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) {
|
pub fn remove_tile_wall(
|
||||||
if let Some(tile) = self.0.get_mut(coord) {
|
&mut self,
|
||||||
tile.walls.remove(direction);
|
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 {
|
impl FromIterator<Hex> for Maze {
|
||||||
type Target = HashMap<Hex, HexTile>;
|
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 {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for HexMaze {
|
impl DerefMut for Maze {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
|
|||||||
179
src/tile.rs
179
src/tile.rs
@ -1,4 +1,7 @@
|
|||||||
use super::Walls;
|
use super::Walls;
|
||||||
|
#[cfg(feature = "bevy_reflect")]
|
||||||
|
use crate::traits::WorldPositionable;
|
||||||
|
use crate::traits::{TilePosition, WallStorage};
|
||||||
#[cfg(feature = "bevy")]
|
#[cfg(feature = "bevy")]
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use hexx::Hex;
|
use hexx::Hex;
|
||||||
@ -9,18 +12,90 @@ use std::fmt::Display;
|
|||||||
/// Represents a single hexagonal tile in the maze
|
/// Represents a single hexagonal tile in the maze
|
||||||
///
|
///
|
||||||
/// Each tile has a position and a set of walls defining its boundaries.
|
/// 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 = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
||||||
#[cfg_attr(feature = "bevy", derive(Component))]
|
#[cfg_attr(feature = "bevy", derive(Component))]
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
#[cfg_attr(feature = "bevy", reflect(Component))]
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct HexTile {
|
pub struct Tile {
|
||||||
pub(crate) pos: Hex,
|
pub(crate) pos: Hex,
|
||||||
pub(crate) walls: Walls,
|
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.
|
/// Creates a new tile with the given position and default walls.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -32,7 +107,7 @@ impl HexTile {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// 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.pos(), Hex::new(1, -1));
|
||||||
/// assert_eq!(*tile.walls(), Walls::default());
|
/// assert_eq!(*tile.walls(), Walls::default());
|
||||||
/// ```
|
/// ```
|
||||||
@ -43,68 +118,9 @@ impl HexTile {
|
|||||||
walls: Walls::default(),
|
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 {
|
fn from(value: Hex) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos: value,
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "({},{})", self.pos.x, self.pos.y)
|
write!(f, "({},{})", self.pos.x, self.pos.y)
|
||||||
}
|
}
|
||||||
@ -130,19 +146,6 @@ mod test {
|
|||||||
Hex::new(rng.gen(), rng.gen())
|
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]
|
#[test]
|
||||||
fn different_positions() {
|
fn different_positions() {
|
||||||
let positions = [Hex::ZERO, Hex::new(1, 0), Hex::new(-1, 1), Hex::new(2, -2)];
|
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
|
// Create tiles at different positions
|
||||||
let tiles = positions
|
let tiles = positions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&pos| HexTile::new(pos))
|
.map(|&pos| Tile::new(pos))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Verify each tile has correct position
|
// Verify each tile has correct position
|
||||||
@ -170,7 +173,7 @@ mod test {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for pos in extreme_positions {
|
for pos in extreme_positions {
|
||||||
let tile = HexTile::new(pos);
|
let tile = Tile::new(pos);
|
||||||
assert_eq!(tile.pos, pos);
|
assert_eq!(tile.pos, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +181,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_creation_and_properties() {
|
fn hex_tile_creation_and_properties() {
|
||||||
let hex = random_hex();
|
let hex = random_hex();
|
||||||
let tile = HexTile::new(hex);
|
let tile = Tile::new(hex);
|
||||||
|
|
||||||
assert_eq!(tile.pos(), hex);
|
assert_eq!(tile.pos(), hex);
|
||||||
assert!(tile.walls().is_enclosed());
|
assert!(tile.walls().is_enclosed());
|
||||||
@ -187,7 +190,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_from_hex() {
|
fn hex_tile_from_hex() {
|
||||||
let hex = random_hex();
|
let hex = random_hex();
|
||||||
let tile = HexTile::from(hex);
|
let tile = Tile::from(hex);
|
||||||
|
|
||||||
assert_eq!(tile.pos, hex);
|
assert_eq!(tile.pos, hex);
|
||||||
assert_eq!(tile.walls, Walls::default());
|
assert_eq!(tile.walls, Walls::default());
|
||||||
@ -196,7 +199,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hex_hex_into_tile() {
|
fn hex_hex_into_tile() {
|
||||||
let hex = random_hex();
|
let hex = random_hex();
|
||||||
let tile: HexTile = hex.into();
|
let tile: Tile = hex.into();
|
||||||
|
|
||||||
assert_eq!(tile.pos, hex);
|
assert_eq!(tile.pos, hex);
|
||||||
assert_eq!(tile.walls, Walls::default());
|
assert_eq!(tile.walls, Walls::default());
|
||||||
@ -204,16 +207,16 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_display() {
|
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)");
|
assert_eq!(format!("{tile}"), "(3,-3)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_wall_modifications() {
|
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 {
|
for direction in EdgeDirection::ALL_DIRECTIONS {
|
||||||
tile.walls.add(direction);
|
tile.walls.insert(direction);
|
||||||
}
|
}
|
||||||
assert_eq!(tile.walls.count(), 6);
|
assert_eq!(tile.walls.count(), 6);
|
||||||
|
|
||||||
@ -231,7 +234,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_to_vec2() {
|
fn hex_tile_to_vec2() {
|
||||||
let layout = HexLayout::default();
|
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);
|
let vec2 = tile.to_vec2(&layout);
|
||||||
assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
|
assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
|
||||||
}
|
}
|
||||||
@ -239,7 +242,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hex_tile_to_vec3() {
|
fn hex_tile_to_vec3() {
|
||||||
let layout = HexLayout::default();
|
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);
|
let vec3 = tile.to_vec3(&layout);
|
||||||
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
|
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/traits.rs
Normal file
22
src/traits.rs
Normal 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;
|
||||||
|
}
|
||||||
162
src/walls.rs
162
src/walls.rs
@ -16,15 +16,15 @@ use hexx::EdgeDirection;
|
|||||||
///
|
///
|
||||||
/// // Create a hexagon with all walls
|
/// // Create a hexagon with all walls
|
||||||
/// let walls = Walls::new();
|
/// let walls = Walls::new();
|
||||||
/// assert!(walls.is_closed());
|
/// assert!(walls.is_enclosed());
|
||||||
///
|
///
|
||||||
/// // Create a hexagon with no walls
|
/// // Create a hexagon with no walls
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
///
|
///
|
||||||
/// // Add specific walls
|
/// // Add specific walls
|
||||||
/// walls.add(EdgeDirection::FLAT_NORTH);
|
/// walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
/// walls.add(EdgeDirection::FLAT_SOUTH);
|
/// walls.insert(EdgeDirection::FLAT_SOUTH);
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
@ -47,7 +47,7 @@ impl Walls {
|
|||||||
/// let walls = Walls::new();
|
/// let walls = Walls::new();
|
||||||
/// assert!(walls.is_enclosed());
|
/// assert!(walls.is_enclosed());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
@ -63,7 +63,7 @@ impl Walls {
|
|||||||
/// let walls = Walls::empty();
|
/// let walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn empty() -> Self {
|
pub const fn empty() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
@ -79,17 +79,21 @@ impl Walls {
|
|||||||
/// let walls = Walls::empty();
|
/// let walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_empty(&self) -> bool {
|
pub const fn is_empty(&self) -> bool {
|
||||||
self.0 == 0
|
self.0 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a wall in the specified direction.
|
/// Insert a wall in the specified direction.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -99,22 +103,24 @@ impl Walls {
|
|||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// assert_eq!(walls.count(), 0);
|
/// assert_eq!(walls.count(), 0);
|
||||||
///
|
///
|
||||||
/// walls.add(1);
|
/// assert!(!walls.insert(1));
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert_eq!(walls.count(), 1);
|
||||||
///
|
///
|
||||||
/// walls.add(1);
|
/// assert!(walls.insert(1));
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert_eq!(walls.count(), 1);
|
||||||
///
|
///
|
||||||
/// walls.add(EdgeDirection::FLAT_NORTH);
|
/// assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
pub fn add<T>(&mut self, direction: T)
|
pub fn insert<T>(&mut self, direction: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
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.
|
/// Removes a wall in the specified direction.
|
||||||
@ -123,6 +129,10 @@ impl Walls {
|
|||||||
///
|
///
|
||||||
/// - `direction` - The direction from which to remove the wall.
|
/// - `direction` - The direction from which to remove the wall.
|
||||||
///
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns `true` if a wall was present and removed, `false` otherwise.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -139,16 +149,15 @@ impl Walls {
|
|||||||
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
/// assert_eq!(walls.count(), 4);
|
/// assert_eq!(walls.count(), 4);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
pub fn remove<T>(&mut self, direction: T) -> bool
|
pub fn remove<T>(&mut self, direction: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
let was_removed = self.contains(direction);
|
let mask = direction.into().0;
|
||||||
if was_removed {
|
let was_present = self.0 & mask != 0;
|
||||||
self.0 &= !direction.into().0;
|
self.0 &= !mask;
|
||||||
}
|
was_present
|
||||||
was_removed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if there is a wall in the specified direction.
|
/// Checks if there is a wall in the specified direction.
|
||||||
@ -163,17 +172,17 @@ impl Walls {
|
|||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// 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_NORTH));
|
||||||
/// assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
|
/// assert!(!walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
pub fn contains<T>(&self, other: T) -> bool
|
pub fn contains<T>(&self, direction: &T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
self.0 & other.into().0 != 0
|
self.0 & (*direction).into().0 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw bit representation of the walls
|
/// Returns the raw bit representation of the walls
|
||||||
@ -189,7 +198,7 @@ impl Walls {
|
|||||||
/// let walls = Walls::empty();
|
/// let walls = Walls::empty();
|
||||||
/// assert_eq!(walls.as_bits(), 0);
|
/// assert_eq!(walls.as_bits(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn as_bits(&self) -> u8 {
|
pub const fn as_bits(&self) -> u8 {
|
||||||
self.0
|
self.0
|
||||||
@ -205,13 +214,13 @@ impl Walls {
|
|||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
///
|
///
|
||||||
/// walls.add(0);
|
/// walls.insert(0);
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert_eq!(walls.count(), 1);
|
||||||
///
|
///
|
||||||
/// walls.add(1);
|
/// walls.insert(1);
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn count(&self) -> u8 {
|
pub fn count(&self) -> u8 {
|
||||||
u8::try_from(self.0.count_ones()).unwrap_or_default()
|
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);
|
/// assert_eq!(Walls::all_directions().as_bits(), 0b11_1111);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn all_directions() -> Self {
|
pub const fn all_directions() -> Self {
|
||||||
Self(0b11_1111)
|
Self(0b11_1111)
|
||||||
@ -262,29 +271,10 @@ impl Walls {
|
|||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
let is_present = self.contains(direction);
|
let mask = direction.into().0;
|
||||||
if is_present {
|
let was_present = self.0 & mask != 0;
|
||||||
self.remove(direction);
|
self.0 ^= mask;
|
||||||
} else {
|
was_present
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if walls are present in all six directions.
|
/// Checks if walls are present in all six directions.
|
||||||
@ -304,7 +294,7 @@ impl Walls {
|
|||||||
/// walls.remove(0);
|
/// walls.remove(0);
|
||||||
/// assert!(!walls.is_enclosed());
|
/// assert!(!walls.is_enclosed());
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_enclosed(&self) -> bool {
|
pub fn is_enclosed(&self) -> bool {
|
||||||
self.count() == 6
|
self.count() == 6
|
||||||
@ -317,7 +307,7 @@ impl Walls {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # 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`.
|
/// that can be converted into `Walls`.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@ -329,10 +319,10 @@ impl Walls {
|
|||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// walls.fill([EdgeDirection::FLAT_NORTH ,EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
|
/// 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);
|
/// assert_eq!(walls.count(), 3);
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(not(debug_assertions), inline)]
|
#[inline]
|
||||||
pub fn fill<T>(&mut self, other: T)
|
pub fn fill<T>(&mut self, other: T)
|
||||||
where
|
where
|
||||||
T: Into<Self>,
|
T: Into<Self>,
|
||||||
@ -398,15 +388,15 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn as_bits_single_wall() {
|
fn as_bits_single_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.add(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b010000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn as_bits_multiple_walls() {
|
fn as_bits_multiple_walls() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.add(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
walls.add(EdgeDirection::FLAT_SOUTH);
|
walls.insert(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,12 +422,12 @@ mod test {
|
|||||||
assert_eq!(walls.as_bits(), 0);
|
assert_eq!(walls.as_bits(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add
|
// insert
|
||||||
#[test]
|
#[test]
|
||||||
fn add_single_wall() {
|
fn insert_single_wall() {
|
||||||
let mut walls = Walls::empty();
|
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_NORTH));
|
||||||
assert_eq!(walls.count(), 1);
|
assert_eq!(walls.count(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,30 +436,30 @@ mod test {
|
|||||||
fn remove_existing_wall() {
|
fn remove_existing_wall() {
|
||||||
let mut walls = Walls::new();
|
let mut walls = Walls::new();
|
||||||
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(!walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_nonexistent_wall() {
|
fn remove_nonexistent_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
assert!(!walls.remove(EdgeDirection::FLAT_NORTH));
|
assert!(!walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
walls.add(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggle
|
// toggle
|
||||||
#[test]
|
#[test]
|
||||||
fn toggle_adds_wall() {
|
fn toggle_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
assert!(!walls.toggle(EdgeDirection::FLAT_NORTH));
|
assert!(!walls.toggle(EdgeDirection::FLAT_NORTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn toggle_removes_wall() {
|
fn toggle_removes_wall() {
|
||||||
let mut walls = Walls::new();
|
let mut walls = Walls::new();
|
||||||
assert!(walls.toggle(EdgeDirection::FLAT_NORTH));
|
assert!(walls.toggle(EdgeDirection::FLAT_NORTH));
|
||||||
assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(!walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill
|
// fill
|
||||||
@ -477,41 +467,41 @@ mod test {
|
|||||||
fn fill_adds_multiple_walls() {
|
fn fill_adds_multiple_walls() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.fill([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
walls.fill([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
assert_eq!(walls.count(), 2);
|
assert_eq!(walls.count(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fill_preserves_existing_walls() {
|
fn fill_preserves_existing_walls() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.add(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
walls.fill([EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
|
walls.fill([EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH_EAST));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH_EAST));
|
||||||
assert_eq!(walls.count(), 3);
|
assert_eq!(walls.count(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_conversion() {
|
fn from_edge_direction_conversion() {
|
||||||
let walls: Walls = EdgeDirection::FLAT_NORTH.into();
|
let walls: Walls = EdgeDirection::FLAT_NORTH.into();
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
assert_eq!(walls.count(), 1);
|
assert_eq!(walls.count(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_u8_conversion() {
|
fn from_u8_conversion() {
|
||||||
let walls: Walls = 0u8.into();
|
let walls: Walls = 0u8.into();
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH_EAST));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH_EAST));
|
||||||
assert_eq!(walls.count(), 1);
|
assert_eq!(walls.count(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_array_conversion() {
|
fn from_array_conversion() {
|
||||||
let walls: Walls = [EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH].into();
|
let walls: Walls = [EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH].into();
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
assert_eq!(walls.count(), 2);
|
assert_eq!(walls.count(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,8 +532,8 @@ mod test {
|
|||||||
];
|
];
|
||||||
let walls: Walls = directions.into_iter().collect();
|
let walls: Walls = directions.into_iter().collect();
|
||||||
assert_eq!(walls.count(), 2);
|
assert_eq!(walls.count(), 2);
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
||||||
assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
|
assert!(walls.contains(&EdgeDirection::FLAT_SOUTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -551,14 +541,14 @@ mod test {
|
|||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
|
|
||||||
// Test single bit operations
|
// Test single bit operations
|
||||||
walls.add(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b010000);
|
||||||
|
|
||||||
walls.add(EdgeDirection::FLAT_SOUTH);
|
walls.insert(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
|
|
||||||
// Test removing middle bit
|
// Test removing middle bit
|
||||||
walls.add(EdgeDirection::FLAT_SOUTH_EAST);
|
walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b010011);
|
assert_eq!(walls.as_bits(), 0b010011);
|
||||||
walls.remove(EdgeDirection::FLAT_SOUTH);
|
walls.remove(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010001);
|
assert_eq!(walls.as_bits(), 0b010001);
|
||||||
|
|||||||
@ -29,7 +29,7 @@ fn valid_start_position(#[case] start_pos: Hex) {
|
|||||||
.with_radius(3)
|
.with_radius(3)
|
||||||
.with_start_position(start_pos)
|
.with_start_position(start_pos)
|
||||||
.build());
|
.build());
|
||||||
assert_some!(maze.get_tile(&start_pos));
|
assert_some!(maze.get(&start_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -67,13 +67,13 @@ 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
|
||||||
fn count_accessible_neighbors(maze: &HexMaze, 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;
|
||||||
if let Some(walls) = maze.get_walls(&pos) {
|
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 {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ fn maze_boundaries() {
|
|||||||
let pos = Hex::new(q, r);
|
let pos = Hex::new(q, r);
|
||||||
if q.abs() + r.abs() <= radius {
|
if q.abs() + r.abs() <= radius {
|
||||||
assert!(
|
assert!(
|
||||||
maze.get_tile(&pos).is_some(),
|
maze.get(&pos).is_some(),
|
||||||
"Expected tile at {:?} to exist",
|
"Expected tile at {:?} to exist",
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,12 +10,12 @@ fn generator_type(
|
|||||||
#[case] start_pos: Option<Hex>,
|
#[case] start_pos: Option<Hex>,
|
||||||
#[case] seed: Option<u64>,
|
#[case] seed: Option<u64>,
|
||||||
) {
|
) {
|
||||||
let mut maze = HexMaze::new();
|
let mut maze = Maze::new();
|
||||||
for q in -3..=3 {
|
for q in -3..=3 {
|
||||||
for r in -3..=3 {
|
for r in -3..=3 {
|
||||||
let hex = Hex::new(q, r);
|
let hex = Hex::new(q, r);
|
||||||
if hex.length() <= 3 {
|
if hex.length() <= 3 {
|
||||||
maze.add_tile(hex);
|
maze.insert(hex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ fn generator_type(
|
|||||||
for dir in EdgeDirection::ALL_DIRECTIONS {
|
for dir in EdgeDirection::ALL_DIRECTIONS {
|
||||||
let neighbor = current + dir;
|
let neighbor = current + dir;
|
||||||
if let Some(walls) = maze.get_walls(¤t) {
|
if let Some(walls) = maze.get_walls(¤t) {
|
||||||
if !walls.contains(dir) && maze.get_tile(&neighbor).is_some() {
|
if !walls.contains(&dir) && maze.get(&neighbor).is_some() {
|
||||||
to_visit.push(neighbor);
|
to_visit.push(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ fn generator_type(
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_maze() {
|
fn test_empty_maze() {
|
||||||
let mut maze = HexMaze::new();
|
let mut maze = Maze::new();
|
||||||
GeneratorType::RecursiveBacktracking.generate(&mut maze, None, None);
|
GeneratorType::RecursiveBacktracking.generate(&mut maze, None, None);
|
||||||
assert!(
|
assert!(
|
||||||
maze.is_empty(),
|
maze.is_empty(),
|
||||||
|
|||||||
@ -2,28 +2,28 @@ use hexlab::prelude::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_maze_creation_and_basic_operations() {
|
fn hex_maze_creation_and_basic_operations() {
|
||||||
let mut maze = HexMaze::new();
|
let mut maze = Maze::new();
|
||||||
assert!(maze.is_empty());
|
assert!(maze.is_empty());
|
||||||
|
|
||||||
let center = Hex::ZERO;
|
let center = Hex::ZERO;
|
||||||
maze.add_tile(center);
|
maze.insert(center);
|
||||||
assert_eq!(maze.len(), 1);
|
assert_eq!(maze.len(), 1);
|
||||||
assert!(!maze.is_empty());
|
assert!(!maze.is_empty());
|
||||||
|
|
||||||
let tile = maze.get_tile(¢er);
|
let tile = maze.get(¢er);
|
||||||
assert!(tile.is_some());
|
assert!(tile.is_some());
|
||||||
assert_eq!(tile.unwrap().pos(), center);
|
assert_eq!(tile.unwrap().pos(), center);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_maze_wall_operations() {
|
fn hex_maze_wall_operations() {
|
||||||
let mut maze = HexMaze::new();
|
let mut maze = Maze::new();
|
||||||
let center = Hex::ZERO;
|
let center = Hex::ZERO;
|
||||||
maze.add_tile(center);
|
maze.insert(center);
|
||||||
|
|
||||||
// Add walls
|
// Add walls
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
for direction in EdgeDirection::ALL_DIRECTIONS {
|
||||||
maze.add_wall(center, direction);
|
let _ = maze.add_tile_wall(¢er, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
let walls = maze.get_walls(¢er).unwrap();
|
let walls = maze.get_walls(¢er).unwrap();
|
||||||
@ -31,7 +31,7 @@ fn hex_maze_wall_operations() {
|
|||||||
|
|
||||||
// Remove walls
|
// Remove walls
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
for direction in EdgeDirection::ALL_DIRECTIONS {
|
||||||
maze.remove_tile_wall(¢er, direction);
|
let _ = maze.remove_tile_wall(¢er, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
let walls = maze.get_walls(¢er).unwrap();
|
let walls = maze.get_walls(¢er).unwrap();
|
||||||
@ -40,29 +40,29 @@ fn hex_maze_wall_operations() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_maze_multiple_tiles() {
|
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)];
|
let tiles = [Hex::ZERO, Hex::new(1, -1), Hex::new(0, 1), Hex::new(-1, 1)];
|
||||||
|
|
||||||
for &tile in &tiles {
|
for &tile in &tiles {
|
||||||
maze.add_tile(tile);
|
maze.insert(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(maze.len(), tiles.len());
|
assert_eq!(maze.len(), tiles.len());
|
||||||
|
|
||||||
for &tile in &tiles {
|
for &tile in &tiles {
|
||||||
assert!(maze.get_tile(&tile).is_some());
|
assert!(maze.get(&tile).is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_maze_edge_cases() {
|
fn hex_maze_edge_cases() {
|
||||||
let mut maze = HexMaze::new();
|
let mut maze = Maze::new();
|
||||||
let non_existent = Hex::new(10, 10);
|
let non_existent = Hex::new(10, 10);
|
||||||
|
|
||||||
// Operations on non-existent tiles should not panic
|
// Operations on non-existent tiles should not panic
|
||||||
maze.add_wall(non_existent, EdgeDirection::FLAT_NORTH);
|
let _ = maze.add_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
|
||||||
maze.remove_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());
|
assert!(maze.get_walls(&non_existent).is_none());
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user