mirror of
https://github.com/kristoferssolo/hexlab.git
synced 2025-10-21 19:40:34 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81991027bb | |||
| b874f05471 | |||
| 5585966da6 | |||
| cafcb3545f | |||
| e0f180aeee | |||
| 4eab4d1198 | |||
| 6cd7550086 | |||
| fae8e91b54 | |||
| d66e4c4bb2 | |||
| 2b3a375c4f |
86
.github/workflows/ci.yml
vendored
86
.github/workflows/ci.yml
vendored
@ -9,76 +9,30 @@ env:
|
|||||||
RUSTFLAGS: --deny warnings
|
RUSTFLAGS: --deny warnings
|
||||||
RUSTDOCFLAGS: --deny warnings
|
RUSTDOCFLAGS: --deny warnings
|
||||||
jobs:
|
jobs:
|
||||||
# Run tests.
|
build-and-test:
|
||||||
test:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30
|
env:
|
||||||
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
|
RUSTC_WRAPPER: "sccache"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Install dependencies
|
|
||||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
|
||||||
- name: Populate target directory from cache
|
|
||||||
uses: Leafwing-Studios/cargo-cache@v2
|
|
||||||
with:
|
with:
|
||||||
sweep-cache: true
|
toolchain: stable
|
||||||
- name: Run tests
|
components: clippy, rustfmt
|
||||||
|
- name: Run sccache-cache
|
||||||
|
uses: mozilla-actions/sccache-action@v0.0.9
|
||||||
|
- name: Install cargo-nextest
|
||||||
|
uses: taiki-e/install-action@cargo-nextest
|
||||||
|
- name: Run Clippy
|
||||||
|
run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
|
||||||
|
- name: Run formatting
|
||||||
|
run: cargo fmt --all --check
|
||||||
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
cargo test --locked --workspace --all-features --all-targets
|
cargo nextest run --all-features --all-targets
|
||||||
# Workaround for https://github.com/rust-lang/cargo/issues/6669
|
|
||||||
cargo test --locked --workspace --all-features --doc
|
cargo test --locked --workspace --all-features --doc
|
||||||
# Run clippy lints.
|
- name: Check Documentation
|
||||||
clippy:
|
|
||||||
name: Clippy
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
components: clippy
|
|
||||||
- name: Install dependencies
|
|
||||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
|
||||||
- name: Populate target directory from cache
|
|
||||||
uses: Leafwing-Studios/cargo-cache@v2
|
|
||||||
with:
|
|
||||||
sweep-cache: true
|
|
||||||
- name: Run clippy lints
|
|
||||||
run: cargo clippy --locked --workspace --all-features -- --deny warnings
|
|
||||||
# Check formatting.
|
|
||||||
format:
|
|
||||||
name: Format
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
components: rustfmt
|
|
||||||
- name: Run cargo fmt
|
|
||||||
run: cargo fmt --all -- --check
|
|
||||||
# Check documentation.
|
|
||||||
doc:
|
|
||||||
name: Docs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Install dependencies
|
|
||||||
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
|
||||||
- name: Populate target directory from cache
|
|
||||||
uses: Leafwing-Studios/cargo-cache@v2
|
|
||||||
with:
|
|
||||||
sweep-cache: true
|
|
||||||
- name: Check documentation
|
|
||||||
run: cargo doc --locked --workspace --all-features --document-private-items --no-deps
|
run: cargo doc --locked --workspace --all-features --document-private-items --no-deps
|
||||||
|
|||||||
107
Cargo.lock
generated
107
Cargo.lock
generated
@ -1248,7 +1248,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
@ -1631,7 +1631,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rangemap",
|
"rangemap",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"self_cell",
|
"self_cell",
|
||||||
"swash",
|
"swash",
|
||||||
@ -1743,6 +1743,18 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deprecate-until"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a3767f826efbbe5a5ae093920b58b43b01734202be697e1354914e862e8e704"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"semver",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -2014,21 +2026,6 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -2036,7 +2033,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2045,17 +2041,6 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-executor"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -2086,12 +2071,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-sink"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -2110,13 +2089,9 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
@ -2375,7 +2350,7 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
version = "0.5.0"
|
version = "0.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
@ -2383,6 +2358,7 @@ dependencies = [
|
|||||||
"claims",
|
"claims",
|
||||||
"glam",
|
"glam",
|
||||||
"hexx",
|
"hexx",
|
||||||
|
"pathfinding",
|
||||||
"rand",
|
"rand",
|
||||||
"rstest",
|
"rstest",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2391,9 +2367,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexx"
|
name = "hexx"
|
||||||
version = "0.19.0"
|
version = "0.19.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34b47b6f7ee46bba869534a92306b7e7f549bec38114a4ba0288b9321617db22"
|
checksum = "4b450e02a24a4a981c895be4cd2752e2401996c545971309730c4e812b984691"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"glam",
|
"glam",
|
||||||
@ -2457,6 +2433,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "integer-sqrt"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-kit-sys"
|
name = "io-kit-sys"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -2724,7 +2709,7 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"pp-rs",
|
"pp-rs",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"spirv",
|
"spirv",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
@ -2745,7 +2730,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
@ -3203,6 +3188,20 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinding"
|
||||||
|
version = "4.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "301ad6aa19104eeb9af172b3d6a4ab8a5ea26234890baf2fcb1cbbc3f05f674b"
|
||||||
|
dependencies = [
|
||||||
|
"deprecate-until",
|
||||||
|
"indexmap",
|
||||||
|
"integer-sqrt",
|
||||||
|
"num-traits",
|
||||||
|
"rustc-hash 2.1.0",
|
||||||
|
"thiserror 2.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -3570,21 +3569,21 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest"
|
name = "rstest"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035"
|
checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
|
||||||
"futures-timer",
|
"futures-timer",
|
||||||
|
"futures-util",
|
||||||
"rstest_macros",
|
"rstest_macros",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest_macros"
|
name = "rstest_macros"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a"
|
checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"glob",
|
"glob",
|
||||||
@ -3604,6 +3603,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -4320,7 +4325,7 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
@ -4362,7 +4367,7 @@ dependencies = [
|
|||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||||
version = "0.5.0"
|
version = "0.6.1"
|
||||||
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"
|
||||||
@ -26,6 +26,7 @@ thiserror = "2.0"
|
|||||||
bevy = { version = "0.15", optional = true }
|
bevy = { version = "0.15", optional = true }
|
||||||
bevy_utils = { version = "0.15", optional = true }
|
bevy_utils = { version = "0.15", optional = true }
|
||||||
glam = { version = "0.29", optional = true }
|
glam = { version = "0.29", optional = true }
|
||||||
|
pathfinding = { version = "4.13", optional = true }
|
||||||
|
|
||||||
|
|
||||||
[dependencies.bevy_reflect]
|
[dependencies.bevy_reflect]
|
||||||
@ -36,7 +37,7 @@ features = ["glam"]
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
claims = "0.8"
|
claims = "0.8"
|
||||||
rstest = "0.23"
|
rstest = "0.24"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
@ -48,7 +49,8 @@ bevy_reflect = [
|
|||||||
"hexx/bevy_reflect",
|
"hexx/bevy_reflect",
|
||||||
"dep:glam",
|
"dep:glam",
|
||||||
]
|
]
|
||||||
full = ["serde", "bevy"]
|
pathfinding = ["dep:pathfinding"]
|
||||||
|
full = ["serde", "bevy", "pathfinding"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1 # Better compile times with some optimization
|
opt-level = 1 # Better compile times with some optimization
|
||||||
|
|||||||
@ -42,7 +42,7 @@ fn main() {
|
|||||||
.with_radius(5)
|
.with_radius(5)
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to create maze");
|
.expect("Failed to create maze");
|
||||||
println!("Maze size: {}", maze.len());
|
println!("Maze size: {}", maze.count());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ use hexx::Hex;
|
|||||||
///
|
///
|
||||||
/// // A radius of 5 creates 61 hexagonal tiles
|
/// // A radius of 5 creates 61 hexagonal tiles
|
||||||
/// assert!(!maze.is_empty());
|
/// assert!(!maze.is_empty());
|
||||||
/// assert_eq!(maze.len(), 91);
|
/// assert_eq!(maze.count(), 91);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Using a seed for reproducible results:
|
/// Using a seed for reproducible results:
|
||||||
@ -40,7 +40,7 @@ use hexx::Hex;
|
|||||||
/// .expect("Failed to create maze");
|
/// .expect("Failed to create maze");
|
||||||
///
|
///
|
||||||
/// // Same seed should produce identical mazes
|
/// // Same seed should produce identical mazes
|
||||||
/// assert_eq!(maze1.len(), maze2.len());
|
/// assert_eq!(maze1.count(), maze2.count());
|
||||||
/// assert_eq!(maze1, maze2);
|
/// assert_eq!(maze1, maze2);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -190,7 +190,7 @@ pub fn create_hex_maze(radius: u16) -> Maze {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use claims::assert_gt;
|
use claims::{assert_gt, assert_some};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -211,18 +211,18 @@ mod test {
|
|||||||
#[case(100, 30301)]
|
#[case(100, 30301)]
|
||||||
fn create_hex_maze_various_radii(#[case] radius: u16, #[case] expected_size: usize) {
|
fn create_hex_maze_various_radii(#[case] radius: u16, #[case] expected_size: usize) {
|
||||||
let maze = create_hex_maze(radius);
|
let maze = create_hex_maze(radius);
|
||||||
assert_eq!(maze.len(), expected_size);
|
assert_eq!(maze.count(), expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_hex_maze_large_radius() {
|
fn create_hex_maze_large_radius() {
|
||||||
let large_radius = 1000;
|
let large_radius = 1000;
|
||||||
let maze = create_hex_maze(large_radius);
|
let maze = create_hex_maze(large_radius);
|
||||||
assert_gt!(maze.len(), 0);
|
assert_gt!(maze.count(), 0);
|
||||||
|
|
||||||
// Calculate expected size for this radius
|
// Calculate expected size for this radius
|
||||||
let expected_size = 3 * (large_radius as usize).pow(2) + 3 * large_radius as usize + 1;
|
let expected_size = 3 * (large_radius as usize).pow(2) + 3 * large_radius as usize + 1;
|
||||||
assert_eq!(maze.len(), expected_size);
|
assert_eq!(maze.count(), expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -249,8 +249,8 @@ mod test {
|
|||||||
Hex::new(0, -2),
|
Hex::new(0, -2),
|
||||||
Hex::new(1, -2),
|
Hex::new(1, -2),
|
||||||
];
|
];
|
||||||
for pos in expected_positions.iter() {
|
for pos in &expected_positions {
|
||||||
assert!(maze.get(pos).is_some(), "Expected tile at {:?}", pos);
|
assert_some!(maze.get(pos), "Expected tile at {pos:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use hexx::Hex;
|
use hexx::{EdgeDirection, Hex};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::Tile;
|
||||||
|
|
||||||
#[derive(Debug, Error, PartialEq, Eq)]
|
#[derive(Debug, Error, PartialEq, Eq)]
|
||||||
pub enum MazeBuilderError {
|
pub enum MazeBuilderError {
|
||||||
/// Occurs when attempting to build a maze without specifying a radius.
|
/// Occurs when attempting to build a maze without specifying a radius.
|
||||||
@ -18,6 +20,22 @@ pub enum MazeBuilderError {
|
|||||||
|
|
||||||
#[derive(Debug, Error, PartialEq, Eq)]
|
#[derive(Debug, Error, PartialEq, Eq)]
|
||||||
pub enum MazeError {
|
pub enum MazeError {
|
||||||
|
/// Error when attempting to access or modify a tile at a non-existent coordinate.
|
||||||
#[error("Invalid coordinate: {0:?}")]
|
#[error("Invalid coordinate: {0:?}")]
|
||||||
InvalidCoordinate(Hex),
|
InvalidCoordinate(Hex),
|
||||||
|
|
||||||
|
/// Error when a tile's internal position doesn't match its insertion coordinate.
|
||||||
|
#[error("Tile position ({tile_pos:?}) does not match insertion coordinates ({insert_pos:?})")]
|
||||||
|
PositionMismatch { tile_pos: Hex, insert_pos: Hex },
|
||||||
|
|
||||||
|
/// Error when attempting to insert a tile at an already occupied position.
|
||||||
|
#[error("A tile {old_tile:?} already exists at position {pos:?}")]
|
||||||
|
TileAlreadyExists { pos: Hex, old_tile: Tile },
|
||||||
|
|
||||||
|
/// Error when a wall operation fails at the specified coordinate and direction.
|
||||||
|
#[error("Cannot add wall at {coord:?} in direction {direction:?}")]
|
||||||
|
WallOperationFailed {
|
||||||
|
coord: Hex,
|
||||||
|
direction: EdgeDirection,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ fn recursive_backtrack<R: Rng>(
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::builder::create_hex_maze;
|
use crate::builder::create_hex_maze;
|
||||||
|
use claims::assert_some;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@ -72,7 +73,7 @@ mod test {
|
|||||||
recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
|
recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
|
||||||
|
|
||||||
for &pos in maze.keys() {
|
for &pos in maze.keys() {
|
||||||
let walls = maze.get_walls(&pos).unwrap();
|
let walls = assert_some!(maze.get_walls(&pos));
|
||||||
assert!(
|
assert!(
|
||||||
walls.count() < 6,
|
walls.count() < 6,
|
||||||
"At least one wall should be removed for each tile"
|
"At least one wall should be removed for each tile"
|
||||||
@ -100,12 +101,16 @@ 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(&neighbor).is_some() {
|
if !walls.contains(dir) && maze.get(&neighbor).is_some() {
|
||||||
to_visit.push(neighbor);
|
to_visit.push(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(connected.len(), maze.len(), "All tiles should be connected");
|
assert_eq!(
|
||||||
|
connected.len(),
|
||||||
|
maze.count(),
|
||||||
|
"All tiles should be connected"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ pub enum GeneratorType {
|
|||||||
#[default]
|
#[default]
|
||||||
RecursiveBacktracking,
|
RecursiveBacktracking,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneratorType {
|
impl GeneratorType {
|
||||||
pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
|
pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
//! .build()
|
//! .build()
|
||||||
//! .expect("Failed to create maze");
|
//! .expect("Failed to create maze");
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(maze.len(), 37); // A radius of 3 should create 37 tiles
|
//! assert_eq!(maze.count(), 37); // A radius of 3 should create 37 tiles
|
||||||
//!```
|
//!```
|
||||||
//!
|
//!
|
||||||
//! Customizing maze generation:
|
//! Customizing maze generation:
|
||||||
@ -45,13 +45,15 @@
|
|||||||
//!
|
//!
|
||||||
//! let mut walls = Walls::empty();
|
//! let mut walls = Walls::empty();
|
||||||
//! assert!(!walls.insert(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;
|
pub mod errors;
|
||||||
mod generator;
|
mod generator;
|
||||||
mod maze;
|
mod maze;
|
||||||
|
#[cfg(feature = "pathfinding")]
|
||||||
|
mod pathfinding;
|
||||||
mod tile;
|
mod tile;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
mod walls;
|
mod walls;
|
||||||
|
|||||||
68
src/maze.rs
68
src/maze.rs
@ -34,7 +34,7 @@ impl Maze {
|
|||||||
/// let maze = Maze::new();
|
/// let maze = Maze::new();
|
||||||
///
|
///
|
||||||
/// assert!(maze.is_empty());
|
/// assert!(maze.is_empty());
|
||||||
/// assert_eq!(maze.len(), 0);
|
/// assert_eq!(maze.count(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -69,14 +69,7 @@ impl Maze {
|
|||||||
self.0.insert(coords, tile)
|
self.0.insert(coords, tile)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new tile at the specified coordinates.
|
/// Adds a new tile at the specified coordinates. It is recommended to use [`insert`].
|
||||||
///
|
|
||||||
/// 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
|
/// [`insert`]: Maze::insert
|
||||||
///
|
///
|
||||||
@ -85,21 +78,50 @@ impl Maze {
|
|||||||
/// - `coords` - The hexagonal coordinates where the tile should be added.
|
/// - `coords` - The hexagonal coordinates where the tile should be added.
|
||||||
/// - `tile` - The tile to insert to.
|
/// - `tile` - The tile to insert to.
|
||||||
///
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`MazeError::PositionMismatch`] if the tile's position doesn't match the insertion coordinates.
|
||||||
|
/// Returns [`MazeError::TileAlreadyExists`] if a tile already exists at the specified coordinates.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = Maze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// let coord = Hex::ZERO;
|
|
||||||
/// let tile1 = Tile::new(coord);
|
|
||||||
/// let tile2 = Tile::new(Hex::new(1, 1));
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(maze.insert_with_tile(coord, tile1.clone()), None);
|
/// assert_eq!(
|
||||||
/// assert_eq!(maze.insert_with_tile(coord, tile2), Some(tile1));
|
/// maze.insert_with_tile(Hex::new(2, 2), Tile::new(Hex::ZERO)),
|
||||||
|
/// Err(MazeError::PositionMismatch {
|
||||||
|
/// tile_pos: Hex::ZERO,
|
||||||
|
/// insert_pos: Hex::new(2, 2)
|
||||||
|
/// })
|
||||||
|
/// );
|
||||||
|
/// let tile = Tile::new(Hex::ZERO);
|
||||||
|
/// assert_eq!(maze.insert_with_tile(Hex::ZERO, tile.clone()), Ok(tile.clone()));
|
||||||
|
/// assert_eq!(
|
||||||
|
/// maze.insert_with_tile(Hex::ZERO, tile.clone()),
|
||||||
|
/// Err(MazeError::TileAlreadyExists {
|
||||||
|
/// pos: Hex::ZERO,
|
||||||
|
/// old_tile: tile
|
||||||
|
/// })
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert_with_tile(&mut self, coords: Hex, tile: Tile) -> Option<Tile> {
|
pub fn insert_with_tile(&mut self, coords: Hex, tile: Tile) -> Result<Tile, MazeError> {
|
||||||
self.0.insert(coords, tile)
|
if tile.pos != coords {
|
||||||
|
return Err(MazeError::PositionMismatch {
|
||||||
|
tile_pos: tile.pos,
|
||||||
|
insert_pos: coords,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.0
|
||||||
|
.insert(coords, tile.clone())
|
||||||
|
.map_or(Ok(tile), |old_tile| {
|
||||||
|
Err(MazeError::TileAlreadyExists {
|
||||||
|
pos: coords,
|
||||||
|
old_tile,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the tile at the specified coordinates.
|
/// Returns a reference to the tile at the specified coordinates.
|
||||||
@ -149,7 +171,7 @@ impl Maze {
|
|||||||
///
|
///
|
||||||
/// maze.add_tile_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]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -190,17 +212,17 @@ impl Maze {
|
|||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut maze = Maze::new();
|
/// let mut maze = Maze::new();
|
||||||
/// assert_eq!(maze.len(), 0);
|
/// assert_eq!(maze.count(), 0);
|
||||||
///
|
///
|
||||||
/// maze.insert(Hex::new(0, 0));
|
/// maze.insert(Hex::new(0, 0));
|
||||||
/// assert_eq!(maze.len(), 1);
|
/// assert_eq!(maze.count(), 1);
|
||||||
///
|
///
|
||||||
/// maze.insert(Hex::new(1, -1));
|
/// maze.insert(Hex::new(1, -1));
|
||||||
/// assert_eq!(maze.len(), 2);
|
/// assert_eq!(maze.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn len(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +274,7 @@ impl Maze {
|
|||||||
///
|
///
|
||||||
/// // Check that the wall was added
|
/// // Check that the wall was added
|
||||||
/// let walls = maze.get_walls(&Hex::ZERO).unwrap();
|
/// let walls = maze.get_walls(&Hex::ZERO).unwrap();
|
||||||
/// assert!(walls.contains(&EdgeDirection::FLAT_NORTH));
|
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert_eq!(walls.count(), 1);
|
||||||
///
|
///
|
||||||
/// // Adding the same wall again should return true (no change)
|
/// // Adding the same wall again should return true (no change)
|
||||||
@ -300,7 +322,7 @@ impl Maze {
|
|||||||
/// 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(
|
pub fn remove_tile_wall(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
84
src/pathfinding.rs
Normal file
84
src/pathfinding.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! Maze pathfinding implementation for hexagonal grids.
|
||||||
|
//!
|
||||||
|
//! This module provides functionality for finding paths through a hexagonal maze
|
||||||
|
//! using the A* pathfinding algorithm. The maze is represented as a collection of
|
||||||
|
//! hexagonal cells, where each cell may have walls on any of its six edges.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use hexlab::prelude::*;
|
||||||
|
//!
|
||||||
|
//! let maze = MazeBuilder::new()
|
||||||
|
//! .with_radius(3)
|
||||||
|
//! .with_seed(12345)
|
||||||
|
//! .build()
|
||||||
|
//! .expect("Failed to create maze");
|
||||||
|
//! assert!(maze.find_path(Hex::ZERO, Hex::new(-1, 3)).is_some());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Implementation Details
|
||||||
|
//!
|
||||||
|
//! The pathfinding algorithm uses Manhattan distance as a heuristic and considers
|
||||||
|
//! walls between cells when determining valid paths. Each step between adjacent
|
||||||
|
//! cells has a cost of 1.
|
||||||
|
use hexx::{EdgeDirection, Hex};
|
||||||
|
use pathfinding::prelude::*;
|
||||||
|
|
||||||
|
use crate::Maze;
|
||||||
|
|
||||||
|
impl Maze {
|
||||||
|
#[must_use]
|
||||||
|
/// Finds the shortest path between two hexagonal positions in the maze using A* pathfinding.
|
||||||
|
///
|
||||||
|
/// This function calculates the optimal path while taking into account walls between cells.
|
||||||
|
/// The path cost between adjacent cells is always 1, and Manhattan distance is used as the
|
||||||
|
/// heuristic for pathfinding.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `from` - The starting hexagonal position
|
||||||
|
/// * `to` - The target hexagonal position
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Some(Vec<Hex>)` - A vector of hexagonal positions representing the path from start to target
|
||||||
|
/// * `None` - If no valid path exists between the positions
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use hexlab::prelude::*;
|
||||||
|
///
|
||||||
|
/// let maze = MazeBuilder::new()
|
||||||
|
/// .with_radius(3)
|
||||||
|
/// .with_seed(12345)
|
||||||
|
/// .build()
|
||||||
|
/// .expect("Failed to create maze");
|
||||||
|
/// assert!(maze.find_path(Hex::ZERO, Hex::new(-1, 3)).is_some());
|
||||||
|
/// ```
|
||||||
|
pub fn find_path(&self, from: Hex, to: Hex) -> Option<Vec<Hex>> {
|
||||||
|
let successors = |pos: &Hex| {
|
||||||
|
{
|
||||||
|
EdgeDirection::ALL_DIRECTIONS.iter().filter_map(|&dir| {
|
||||||
|
let neighbor = pos.neighbor(dir);
|
||||||
|
if let Some(current_tile) = self.get(pos) {
|
||||||
|
if self.get(&neighbor).is_some() && !current_tile.walls.contains(dir) {
|
||||||
|
return Some((neighbor, 1)); // Cost of 1 for each step
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let heuristic = |pos: &Hex| {
|
||||||
|
// Manhatan distance
|
||||||
|
let diff = *pos - to;
|
||||||
|
(diff.x.abs() + diff.y.abs() + diff.z().abs()) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
astar(&from, successors, heuristic, |pos| *pos == to).map(|(path, _)| path)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -236,7 +236,7 @@ mod test {
|
|||||||
let layout = HexLayout::default();
|
let layout = HexLayout::default();
|
||||||
let tile = Tile::new(Hex::new(1, 0));
|
let tile = Tile::new(Hex::new(1, 0));
|
||||||
let vec2 = tile.to_vec2(&layout);
|
let vec2 = tile.to_vec2(&layout);
|
||||||
assert_eq!(vec2, Vec2::new(1.5, -0.8660254));
|
assert_eq!(vec2, Vec2::new(1.5, -0.866_025_4));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -244,7 +244,7 @@ mod test {
|
|||||||
let layout = HexLayout::default();
|
let layout = HexLayout::default();
|
||||||
let tile = Tile::new(Hex::new(0, 1));
|
let tile = Tile::new(Hex::new(0, 1));
|
||||||
let vec3 = tile.to_vec3(&layout);
|
let vec3 = tile.to_vec3(&layout);
|
||||||
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.7320508));
|
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.732_050_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
94
src/walls.rs
94
src/walls.rs
@ -115,7 +115,7 @@ impl Walls {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert<T>(&mut self, direction: T) -> bool
|
pub fn insert<T>(&mut self, direction: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self>,
|
||||||
{
|
{
|
||||||
let mask = direction.into().0;
|
let mask = direction.into().0;
|
||||||
let was_present = self.0 & mask != 0;
|
let was_present = self.0 & mask != 0;
|
||||||
@ -152,7 +152,7 @@ impl Walls {
|
|||||||
#[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>,
|
||||||
{
|
{
|
||||||
let mask = direction.into().0;
|
let mask = direction.into().0;
|
||||||
let was_present = self.0 & mask != 0;
|
let was_present = self.0 & mask != 0;
|
||||||
@ -174,15 +174,15 @@ impl Walls {
|
|||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// walls.insert(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));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains<T>(&self, direction: &T) -> bool
|
pub fn contains<T>(&self, direction: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self>,
|
||||||
{
|
{
|
||||||
self.0 & (*direction).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
|
||||||
@ -319,7 +319,7 @@ 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);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -375,7 +375,7 @@ mod test {
|
|||||||
let walls = Walls::all_directions();
|
let walls = Walls::all_directions();
|
||||||
assert!(walls.is_enclosed());
|
assert!(walls.is_enclosed());
|
||||||
assert!(!walls.is_empty());
|
assert!(!walls.is_empty());
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// as_bits
|
// as_bits
|
||||||
@ -389,7 +389,7 @@ mod test {
|
|||||||
fn as_bits_single_wall() {
|
fn as_bits_single_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b01_0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -397,13 +397,13 @@ mod test {
|
|||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH);
|
walls.insert(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn as_bits_all_walls() {
|
fn as_bits_all_walls() {
|
||||||
let walls = Walls::new();
|
let walls = Walls::new();
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// new
|
// new
|
||||||
@ -411,7 +411,7 @@ mod test {
|
|||||||
fn new_created_closed_walls() {
|
fn new_created_closed_walls() {
|
||||||
let walls = Walls::new();
|
let walls = Walls::new();
|
||||||
assert!(walls.is_enclosed());
|
assert!(walls.is_enclosed());
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty
|
// empty
|
||||||
@ -427,7 +427,7 @@ mod test {
|
|||||||
fn insert_single_wall() {
|
fn insert_single_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +436,7 @@ 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]
|
||||||
@ -452,14 +452,14 @@ mod test {
|
|||||||
fn toggle_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
|
||||||
@ -467,8 +467,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,31 +477,31 @@ mod test {
|
|||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ mod test {
|
|||||||
fn default_creates_closed_walls() {
|
fn default_creates_closed_walls() {
|
||||||
let walls = Walls::default();
|
let walls = Walls::default();
|
||||||
assert!(walls.is_enclosed());
|
assert!(walls.is_enclosed());
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -532,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]
|
||||||
@ -542,53 +542,53 @@ mod test {
|
|||||||
|
|
||||||
// Test single bit operations
|
// Test single bit operations
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.insert(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b01_0000);
|
||||||
|
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH);
|
walls.insert(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
|
|
||||||
// Test removing middle bit
|
// Test removing middle bit
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
|
walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b010011);
|
assert_eq!(walls.as_bits(), 0b01_0011);
|
||||||
walls.remove(EdgeDirection::FLAT_SOUTH);
|
walls.remove(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010001);
|
assert_eq!(walls.as_bits(), 0b01_0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From<EdgeDirection> tests
|
// From<EdgeDirection> tests
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south_east() {
|
fn from_edge_direction_flat_south_east() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b000001);
|
assert_eq!(walls.as_bits(), 0b00_0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south() {
|
fn from_edge_direction_flat_south() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b000010);
|
assert_eq!(walls.as_bits(), 0b00_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south_west() {
|
fn from_edge_direction_flat_south_west() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST);
|
||||||
assert_eq!(walls.as_bits(), 0b000100);
|
assert_eq!(walls.as_bits(), 0b00_0100);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_north_west() {
|
fn from_edge_direction_flat_north_west() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST);
|
||||||
assert_eq!(walls.as_bits(), 0b001000);
|
assert_eq!(walls.as_bits(), 0b00_1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_north() {
|
fn from_edge_direction_flat_north() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b01_0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_east() {
|
fn from_edge_direction_flat_east() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b100000);
|
assert_eq!(walls.as_bits(), 0b10_0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromIterator tests
|
// FromIterator tests
|
||||||
@ -603,7 +603,7 @@ mod test {
|
|||||||
let walls = vec![EdgeDirection::FLAT_SOUTH]
|
let walls = vec![EdgeDirection::FLAT_SOUTH]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b000010);
|
assert_eq!(walls.as_bits(), 0b00_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -611,7 +611,7 @@ mod test {
|
|||||||
let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]
|
let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -623,13 +623,13 @@ mod test {
|
|||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_iterator_all_directions() {
|
fn from_iterator_all_directions() {
|
||||||
let walls = EdgeDirection::iter().collect::<Walls>();
|
let walls = EdgeDirection::iter().collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From<[EdgeDirection; N]> tests
|
// From<[EdgeDirection; N]> tests
|
||||||
@ -642,13 +642,13 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn from_array_single() {
|
fn from_array_single() {
|
||||||
let walls = Walls::from([EdgeDirection::FLAT_NORTH]);
|
let walls = Walls::from([EdgeDirection::FLAT_NORTH]);
|
||||||
assert_eq!(walls.as_bits(), 0b010000);
|
assert_eq!(walls.as_bits(), 0b01_0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_array_multiple() {
|
fn from_array_multiple() {
|
||||||
let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -658,7 +658,7 @@ mod test {
|
|||||||
EdgeDirection::FLAT_NORTH,
|
EdgeDirection::FLAT_NORTH,
|
||||||
EdgeDirection::FLAT_SOUTH,
|
EdgeDirection::FLAT_SOUTH,
|
||||||
]);
|
]);
|
||||||
assert_eq!(walls.as_bits(), 0b010010);
|
assert_eq!(walls.as_bits(), 0b01_0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -671,6 +671,6 @@ mod test {
|
|||||||
EdgeDirection::FLAT_SOUTH_WEST,
|
EdgeDirection::FLAT_SOUTH_WEST,
|
||||||
EdgeDirection::FLAT_NORTH_WEST,
|
EdgeDirection::FLAT_NORTH_WEST,
|
||||||
]);
|
]);
|
||||||
assert_eq!(walls.as_bits(), 0b111111);
|
assert_eq!(walls.as_bits(), 0b11_1111);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use rstest::rstest;
|
|||||||
#[case(5, 91)]
|
#[case(5, 91)]
|
||||||
fn maze_size(#[case] radius: u16, #[case] expected_size: usize) {
|
fn maze_size(#[case] radius: u16, #[case] expected_size: usize) {
|
||||||
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build());
|
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build());
|
||||||
assert_eq!(maze.len(), expected_size);
|
assert_eq!(maze.count(), expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -67,28 +67,24 @@ 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: &Maze, pos: Hex) -> usize {
|
let count_accessible_neighbors = |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) {
|
maze.get_walls(&pos)
|
||||||
!walls.contains(&dir) && maze.get(&neighbor).is_some()
|
.is_some_and(|walls| !walls.contains(dir) && maze.get(&neighbor).is_some())
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.count()
|
.count()
|
||||||
}
|
};
|
||||||
|
|
||||||
// Check that each tile has at least one connection
|
// Check that each tile has at least one connection
|
||||||
for &pos in maze.keys() {
|
for &pos in maze.keys() {
|
||||||
let accessible_neighbors = count_accessible_neighbors(&maze, pos);
|
let accessible_neighbors = count_accessible_neighbors(pos);
|
||||||
claims::assert_gt!(
|
assert_gt!(
|
||||||
accessible_neighbors,
|
accessible_neighbors,
|
||||||
0,
|
0,
|
||||||
"Tile at {:?} has no accessible neighbors",
|
"Tile at {pos:?} has no accessible neighbors",
|
||||||
pos
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,10 +92,9 @@ fn maze_connectivity() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn maze_boundaries() {
|
fn maze_boundaries() {
|
||||||
let radius = 3;
|
let radius = 3;
|
||||||
let maze = MazeBuilder::new()
|
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build());
|
||||||
.with_radius(radius as u16)
|
|
||||||
.build()
|
let radius = i32::from(radius);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Test that tiles exist within the radius
|
// Test that tiles exist within the radius
|
||||||
for q in -radius..=radius {
|
for q in -radius..=radius {
|
||||||
@ -108,8 +103,7 @@ fn maze_boundaries() {
|
|||||||
if q.abs() + r.abs() <= radius {
|
if q.abs() + r.abs() <= radius {
|
||||||
assert!(
|
assert!(
|
||||||
maze.get(&pos).is_some(),
|
maze.get(&pos).is_some(),
|
||||||
"Expected tile at {:?} to exist",
|
"Expected tile at {pos:?} to exist",
|
||||||
pos
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,5 +120,5 @@ fn generate_maze_with_different_types(#[case] generator: GeneratorType) {
|
|||||||
.with_generator(generator)
|
.with_generator(generator)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
assert_gt!(maze.len(), 0);
|
assert_gt!(maze.count(), 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use claims::assert_some;
|
||||||
use hexlab::prelude::*;
|
use hexlab::prelude::*;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@ -19,11 +20,11 @@ fn generator_type(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let initial_size = maze.len();
|
let initial_size = maze.count();
|
||||||
|
|
||||||
generator.generate(&mut maze, start_pos, seed);
|
generator.generate(&mut maze, start_pos, seed);
|
||||||
|
|
||||||
assert_eq!(maze.len(), initial_size, "Maze size should not change");
|
assert_eq!(maze.count(), initial_size, "Maze size should not change");
|
||||||
|
|
||||||
// Check maze connectivity
|
// Check maze connectivity
|
||||||
let start = start_pos.unwrap_or(Hex::ZERO);
|
let start = start_pos.unwrap_or(Hex::ZERO);
|
||||||
@ -36,21 +37,20 @@ 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(&neighbor).is_some() {
|
if !walls.contains(dir) && maze.get(&neighbor).is_some() {
|
||||||
to_visit.push(neighbor);
|
to_visit.push(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(visited.len(), maze.len(), "All tiles should be connected");
|
assert_eq!(visited.len(), maze.count(), "All tiles should be connected");
|
||||||
|
|
||||||
// Check that each tile has at least one open wall
|
// Check that each tile has at least one open wall
|
||||||
for &pos in maze.keys() {
|
for &pos in maze.keys() {
|
||||||
let walls = maze.get_walls(&pos).unwrap();
|
let walls = assert_some!(maze.get_walls(&pos));
|
||||||
assert!(
|
assert!(
|
||||||
walls.count() < 6,
|
walls.count() < 6,
|
||||||
"Tile at {:?} should have at least one open wall",
|
"Tile at {pos:?} should have at least one open wall",
|
||||||
pos
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use claims::assert_some;
|
||||||
use hexlab::prelude::*;
|
use hexlab::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -7,12 +8,11 @@ fn hex_maze_creation_and_basic_operations() {
|
|||||||
|
|
||||||
let center = Hex::ZERO;
|
let center = Hex::ZERO;
|
||||||
maze.insert(center);
|
maze.insert(center);
|
||||||
assert_eq!(maze.len(), 1);
|
assert_eq!(maze.count(), 1);
|
||||||
assert!(!maze.is_empty());
|
assert!(!maze.is_empty());
|
||||||
|
|
||||||
let tile = maze.get(¢er);
|
let tile = assert_some!(maze.get(¢er));
|
||||||
assert!(tile.is_some());
|
assert_eq!(tile.pos(), center);
|
||||||
assert_eq!(tile.unwrap().pos(), center);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -26,7 +26,7 @@ fn hex_maze_wall_operations() {
|
|||||||
let _ = maze.add_tile_wall(¢er, direction);
|
let _ = maze.add_tile_wall(¢er, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
let walls = maze.get_walls(¢er).unwrap();
|
let walls = assert_some!(maze.get_walls(¢er));
|
||||||
assert_eq!(walls.count(), 6);
|
assert_eq!(walls.count(), 6);
|
||||||
|
|
||||||
// Remove walls
|
// Remove walls
|
||||||
@ -34,7 +34,7 @@ fn hex_maze_wall_operations() {
|
|||||||
let _ = maze.remove_tile_wall(¢er, direction);
|
let _ = maze.remove_tile_wall(¢er, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
let walls = maze.get_walls(¢er).unwrap();
|
let walls = assert_some!(maze.get_walls(¢er));
|
||||||
assert_eq!(walls.count(), 0);
|
assert_eq!(walls.count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ fn hex_maze_multiple_tiles() {
|
|||||||
maze.insert(tile);
|
maze.insert(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(maze.len(), tiles.len());
|
assert_eq!(maze.count(), tiles.len());
|
||||||
|
|
||||||
for &tile in &tiles {
|
for &tile in &tiles {
|
||||||
assert!(maze.get(&tile).is_some());
|
assert!(maze.get(&tile).is_some());
|
||||||
|
|||||||
79
tests/pathfinding.rs
Normal file
79
tests/pathfinding.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use claims::*;
|
||||||
|
use hexlab::MazeBuilder;
|
||||||
|
use hexx::{hex, EdgeDirection, Hex};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_path() {
|
||||||
|
let maze = assert_ok!(MazeBuilder::new().with_seed(12345).with_radius(5).build());
|
||||||
|
|
||||||
|
let start = Hex::new(0, 0);
|
||||||
|
let goal = Hex::new(2, 0);
|
||||||
|
|
||||||
|
assert_some_eq!(
|
||||||
|
maze.find_path(start, goal),
|
||||||
|
vec![start, hex(1, 0), hex(1, 1), hex(2, 1), goal]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_with_walls() {
|
||||||
|
let mut maze = assert_ok!(MazeBuilder::new().with_seed(12345).with_radius(5).build());
|
||||||
|
let start = Hex::new(0, 0);
|
||||||
|
let goal = Hex::new(2, 0);
|
||||||
|
|
||||||
|
// Block direct path with wall
|
||||||
|
assert_ok!(maze.add_tile_wall(&start, EdgeDirection::FLAT_SOUTH));
|
||||||
|
|
||||||
|
// Should find alternative path or no path
|
||||||
|
let path = maze.find_path(start, goal);
|
||||||
|
if let Some(path) = path {
|
||||||
|
// If path exists, verify it's valid
|
||||||
|
assert!(path.len() > 3); // Should be longer than direct path
|
||||||
|
assert_eq!(path.first(), Some(&start));
|
||||||
|
assert_eq!(path.last(), Some(&goal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_to_self() {
|
||||||
|
let maze = assert_ok!(MazeBuilder::new().with_seed(12345).with_radius(5).build());
|
||||||
|
let pos = Hex::new(0, 0);
|
||||||
|
|
||||||
|
assert_some_eq!(maze.find_path(pos, pos), vec![pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_path_exists() {
|
||||||
|
let mut maze = assert_ok!(MazeBuilder::new().with_seed(12345).with_radius(5).build());
|
||||||
|
let start = Hex::new(0, 0);
|
||||||
|
let goal = Hex::new(2, 0);
|
||||||
|
|
||||||
|
// Surround start with walls
|
||||||
|
for dir in EdgeDirection::ALL_DIRECTIONS {
|
||||||
|
assert_ok!(maze.add_tile_wall(&start, dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_none!(maze.find_path(start, goal));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_in_larger_maze() {
|
||||||
|
let maze = assert_ok!(MazeBuilder::new().with_seed(12345).with_radius(10).build());
|
||||||
|
let start = Hex::new(-5, -5);
|
||||||
|
let goal = Hex::new(5, 5);
|
||||||
|
|
||||||
|
let path = assert_some!(maze.find_path(start, goal));
|
||||||
|
|
||||||
|
// Basic path properties
|
||||||
|
assert_eq!(path.first(), Some(&start));
|
||||||
|
assert_eq!(path.last(), Some(&goal));
|
||||||
|
|
||||||
|
// Path should be continuous
|
||||||
|
for window in path.windows(2) {
|
||||||
|
let current = window[0];
|
||||||
|
let next = window[1];
|
||||||
|
assert!(EdgeDirection::ALL_DIRECTIONS
|
||||||
|
.iter()
|
||||||
|
.any(|&dir| current.neighbor(dir) == next));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user