mirror of
https://github.com/kristoferssolo/hexlab.git
synced 2025-10-21 19:40:34 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62cba0c5b9 |
90
.github/workflows/ci.yml
vendored
90
.github/workflows/ci.yml
vendored
@ -9,30 +9,76 @@ env:
|
|||||||
RUSTFLAGS: --deny warnings
|
RUSTFLAGS: --deny warnings
|
||||||
RUSTDOCFLAGS: --deny warnings
|
RUSTDOCFLAGS: --deny warnings
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
# Run tests.
|
||||||
|
test:
|
||||||
|
name: Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
timeout-minutes: 30
|
||||||
SCCACHE_GHA_ENABLED: "true"
|
|
||||||
RUSTC_WRAPPER: "sccache"
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Install Rust
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||||
|
- name: Populate target directory from cache
|
||||||
|
uses: Leafwing-Studios/cargo-cache@v2
|
||||||
|
with:
|
||||||
|
sweep-cache: true
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cargo test --locked --workspace --all-features --all-targets
|
||||||
|
# Workaround for https://github.com/rust-lang/cargo/issues/6669
|
||||||
|
cargo test --locked --workspace --all-features --doc
|
||||||
|
# Run clippy lints.
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
components: clippy
|
||||||
components: clippy, rustfmt
|
- name: Install dependencies
|
||||||
- name: Run sccache-cache
|
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||||
uses: mozilla-actions/sccache-action@v0.0.9
|
- name: Populate target directory from cache
|
||||||
- name: Install cargo-nextest
|
uses: Leafwing-Studios/cargo-cache@v2
|
||||||
uses: taiki-e/install-action@cargo-nextest
|
with:
|
||||||
- name: Run Clippy
|
sweep-cache: true
|
||||||
run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
|
- name: Run clippy lints
|
||||||
- name: Run formatting
|
run: cargo clippy --locked --workspace --all-features -- --deny warnings
|
||||||
run: cargo fmt --all --check
|
# Check formatting.
|
||||||
- name: Run Tests
|
format:
|
||||||
run: |
|
name: Format
|
||||||
cargo nextest run --all-features --all-targets
|
runs-on: ubuntu-latest
|
||||||
cargo test --locked --workspace --all-features --doc
|
timeout-minutes: 30
|
||||||
- name: Check Documentation
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- name: Run cargo fmt
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
# Check documentation.
|
||||||
|
doc:
|
||||||
|
name: Docs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
|
||||||
|
- name: Populate target directory from cache
|
||||||
|
uses: Leafwing-Studios/cargo-cache@v2
|
||||||
|
with:
|
||||||
|
sweep-cache: true
|
||||||
|
- name: Check documentation
|
||||||
run: cargo doc --locked --workspace --all-features --document-private-items --no-deps
|
run: cargo doc --locked --workspace --all-features --document-private-items --no-deps
|
||||||
|
|||||||
395
Cargo.lock
generated
395
Cargo.lock
generated
@ -136,7 +136,7 @@ dependencies = [
|
|||||||
"ndk-context",
|
"ndk-context",
|
||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys 0.6.0+11769913",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1248,7 +1248,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
@ -1300,9 +1300,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake3"
|
name = "blake3"
|
||||||
version = "1.5.5"
|
version = "1.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
|
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
@ -1347,18 +1347,18 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.21.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck_derive",
|
"bytemuck_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck_derive"
|
name = "bytemuck_derive"
|
||||||
version = "1.8.1"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
|
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1379,9 +1379,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.9.0"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calloop"
|
name = "calloop"
|
||||||
@ -1394,14 +1394,14 @@ dependencies = [
|
|||||||
"polling",
|
"polling",
|
||||||
"rustix",
|
"rustix",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.5"
|
version = "1.1.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1441,12 +1441,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "claims"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@ -1525,9 +1519,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_panic"
|
name = "const_panic"
|
||||||
version = "0.2.11"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53857514f72ee4a2b583de67401e3ff63a5472ca4acf289d09a9ea7636dfec17"
|
checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_soft_float"
|
name = "const_soft_float"
|
||||||
@ -1631,7 +1625,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rangemap",
|
"rangemap",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"self_cell",
|
"self_cell",
|
||||||
"swash",
|
"swash",
|
||||||
@ -1677,18 +1671,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.14"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
|
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@ -1705,9 +1699,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
@ -1743,18 +1737,6 @@ 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"
|
||||||
@ -1833,7 +1815,7 @@ dependencies = [
|
|||||||
"const_panic",
|
"const_panic",
|
||||||
"encase_derive",
|
"encase_derive",
|
||||||
"glam",
|
"glam",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1874,12 +1856,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.10"
|
version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1910,9 +1892,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event-listener-strategy"
|
name = "event-listener-strategy"
|
||||||
version = "0.5.3"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
|
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"event-listener 5.3.1",
|
"event-listener 5.3.1",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@ -1920,15 +1902,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.3.0"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdeflate"
|
name = "fdeflate"
|
||||||
version = "0.3.7"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
@ -1947,9 +1929,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.35"
|
version = "1.0.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
@ -1963,9 +1945,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.4"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "font-types"
|
name = "font-types"
|
||||||
@ -2049,9 +2031,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
version = "2.5.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
|
checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -2060,43 +2042,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-macro"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-timer"
|
|
||||||
version = "3.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-macro",
|
|
||||||
"futures-task",
|
|
||||||
"pin-project-lite",
|
|
||||||
"pin-utils",
|
|
||||||
"slab",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gethostname"
|
name = "gethostname"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -2266,19 +2211,19 @@ checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"presser",
|
"presser",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpu-descriptor"
|
name = "gpu-descriptor"
|
||||||
version = "0.3.1"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca"
|
checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"gpu-descriptor-types",
|
"gpu-descriptor-types",
|
||||||
"hashbrown 0.15.1",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2350,26 +2295,21 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
version = "0.6.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_reflect",
|
|
||||||
"bevy_utils",
|
|
||||||
"claims",
|
|
||||||
"glam",
|
|
||||||
"hexx",
|
"hexx",
|
||||||
"pathfinding",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rstest",
|
"rand_chacha",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.3",
|
"thiserror 2.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexx"
|
name = "hexx"
|
||||||
version = "0.19.1"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b450e02a24a4a981c895be4cd2752e2401996c545971309730c4e812b984691"
|
checksum = "34b47b6f7ee46bba869534a92306b7e7f549bec38114a4ba0288b9321617db22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"glam",
|
"glam",
|
||||||
@ -2433,15 +2373,6 @@ 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"
|
||||||
@ -2463,9 +2394,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.14"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
@ -2478,7 +2409,7 @@ dependencies = [
|
|||||||
"combine",
|
"combine",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
@ -2558,9 +2489,9 @@ checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.6"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
@ -2580,7 +2511,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.8",
|
"redox_syscall 0.5.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2686,9 +2617,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.2"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
@ -2696,9 +2627,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "23.1.0"
|
version = "23.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
|
checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-set 0.8.0",
|
"bit-set 0.8.0",
|
||||||
@ -2709,10 +2640,10 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"pp-rs",
|
"pp-rs",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"spirv",
|
"spirv",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2730,8 +2661,8 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2747,7 +2678,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"ndk-sys 0.5.0+25.2.9519653",
|
"ndk-sys 0.5.0+25.2.9519653",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2762,7 +2693,7 @@ dependencies = [
|
|||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys 0.6.0+11769913",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3177,7 +3108,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.8",
|
"redox_syscall 0.5.7",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
@ -3188,20 +3119,6 @@ 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"
|
||||||
@ -3246,12 +3163,6 @@ version = "0.2.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "piper"
|
name = "piper"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -3271,9 +3182,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.16"
|
version = "0.17.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
@ -3284,9 +3195,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "3.7.4"
|
version = "3.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
|
checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
@ -3389,6 +3300,7 @@ checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3450,9 +3362,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "read-fonts"
|
name = "read-fonts"
|
||||||
version = "0.22.7"
|
version = "0.22.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f"
|
checksum = "4a04b892cb6f91951f144c33321843790c8574c825aafdb16d815fd7183b5229"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"font-types",
|
"font-types",
|
||||||
@ -3475,9 +3387,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.8"
|
version = "0.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
@ -3490,7 +3402,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.9",
|
"regex-automata 0.4.8",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3505,9 +3417,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.9"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -3526,12 +3438,6 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "relative-path"
|
|
||||||
version = "1.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "renderdoc-sys"
|
name = "renderdoc-sys"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -3546,7 +3452,7 @@ checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cpal",
|
"cpal",
|
||||||
"lewton",
|
"lewton",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3567,68 +3473,23 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rstest"
|
|
||||||
version = "0.24.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89"
|
|
||||||
dependencies = [
|
|
||||||
"futures-timer",
|
|
||||||
"futures-util",
|
|
||||||
"rstest_macros",
|
|
||||||
"rustc_version",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rstest_macros"
|
|
||||||
version = "0.24.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"glob",
|
|
||||||
"proc-macro-crate",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"regex",
|
|
||||||
"relative-path",
|
|
||||||
"rustc_version",
|
|
||||||
"syn",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
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]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
|
||||||
dependencies = [
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.42"
|
version = "0.38.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3684,12 +3545,6 @@ 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 = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
|
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "1.0.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "send_wrapper"
|
name = "send_wrapper"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -3718,9 +3573,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.134"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -3860,7 +3715,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"windows 0.57.0",
|
"windows 0.54.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3887,11 +3742,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 1.0.69",
|
"thiserror-impl 1.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3905,9 +3760,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.69"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3946,9 +3801,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.1"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
@ -3989,9 +3844,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.28"
|
version = "0.1.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4093,9 +3948,9 @@ checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.18"
|
version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi-mirroring"
|
name = "unicode-bidi-mirroring"
|
||||||
@ -4325,9 +4180,9 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
]
|
]
|
||||||
@ -4367,9 +4222,9 @@ dependencies = [
|
|||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.68",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
@ -4429,16 +4284,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.57.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@ -4459,42 +4304,19 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement 0.57.0",
|
|
||||||
"windows-interface 0.57.0",
|
|
||||||
"windows-result 0.1.2",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement 0.58.0",
|
"windows-implement",
|
||||||
"windows-interface 0.58.0",
|
"windows-interface",
|
||||||
"windows-result 0.2.0",
|
"windows-result 0.2.0",
|
||||||
"windows-strings",
|
"windows-strings",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@ -4506,17 +4328,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@ -4763,9 +4574,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.30.7"
|
version = "0.30.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dba50bc8ef4b6f1a75c9274fb95aa9a8f63fbc66c56f391bd85cf68d51e7b1a3"
|
checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-activity",
|
"android-activity",
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
@ -4867,9 +4678,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.24"
|
version = "0.8.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432"
|
checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yazi"
|
name = "yazi"
|
||||||
|
|||||||
45
Cargo.toml
45
Cargo.toml
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||||
version = "0.6.1"
|
version = "0.2.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"
|
||||||
@ -16,41 +16,23 @@ categories = [
|
|||||||
"data-structures",
|
"data-structures",
|
||||||
]
|
]
|
||||||
exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"]
|
exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"]
|
||||||
readme = "README.md"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bevy = { version = "0.15", optional = true }
|
||||||
hexx = { version = "0.19" }
|
hexx = { version = "0.19" }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
rand_chacha = "0.3"
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
bevy = { version = "0.15", optional = true }
|
|
||||||
bevy_utils = { version = "0.15", optional = true }
|
|
||||||
glam = { version = "0.29", optional = true }
|
|
||||||
pathfinding = { version = "4.13", optional = true }
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies.bevy_reflect]
|
|
||||||
version = "0.15"
|
|
||||||
optional = true
|
|
||||||
default-features = false
|
|
||||||
features = ["glam"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
claims = "0.8"
|
|
||||||
rstest = "0.24"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
serde = ["dep:serde", "hexx/serde"]
|
serde = ["dep:serde", "hexx/serde", "rand_chacha/serde"]
|
||||||
bevy = ["dep:bevy", "bevy_reflect"]
|
bevy = ["dep:bevy", "hexx/bevy_reflect"]
|
||||||
bevy_reflect = [
|
full = ["serde", "bevy"]
|
||||||
"dep:bevy_reflect",
|
|
||||||
"dep:bevy_utils",
|
|
||||||
"hexx/bevy_reflect",
|
|
||||||
"dep:glam",
|
|
||||||
]
|
|
||||||
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
|
||||||
@ -65,23 +47,8 @@ panic = "abort" # Smaller binary size
|
|||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
# Override some settings for native builds.
|
|
||||||
[profile.release-native]
|
|
||||||
# Default to release profile values.
|
|
||||||
inherits = "release"
|
|
||||||
# Optimize with performance in mind.
|
|
||||||
opt-level = 3
|
|
||||||
# Keep debug information in the binary.
|
|
||||||
strip = "none"
|
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
pedantic = "warn"
|
pedantic = "warn"
|
||||||
nursery = "warn"
|
nursery = "warn"
|
||||||
unwrap_used = "warn"
|
unwrap_used = "warn"
|
||||||
expect_used = "warn"
|
expect_used = "warn"
|
||||||
|
|
||||||
[package.metadata.nextest]
|
|
||||||
slow-timeout = { period = "120s", terminate-after = 3 }
|
|
||||||
|
|||||||
89
README.md
89
README.md
@ -1,89 +0,0 @@
|
|||||||
# Hexlab
|
|
||||||
|
|
||||||
<!-- toc -->
|
|
||||||
|
|
||||||
- [Features](#features)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [Acknowledgements](#acknowledgements)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
<!-- tocstop -->
|
|
||||||
|
|
||||||
Hexlab is a Rust library for generating and manipulating hexagonal mazes.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Create hexagonal mazes of configurable size
|
|
||||||
- Customizable maze properties (radius, start position, seed)
|
|
||||||
- Efficient bit-flag representation of walls for optimized memory usage
|
|
||||||
- Multiple maze generation algorithms (WIP)
|
|
||||||
- Maze builder pattern for easy and flexible maze creation
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Add `hexlab` as a dependency:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo add hexlab
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use hexlab::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Create a new maze with radius 5
|
|
||||||
let maze = MazeBuilder::new()
|
|
||||||
.with_radius(5)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create maze");
|
|
||||||
println!("Maze size: {}", maze.count());
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use hexlab::prelude::*;
|
|
||||||
|
|
||||||
// Create a new maze
|
|
||||||
let maze = MazeBuilder::new()
|
|
||||||
.with_radius(5)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create maze");
|
|
||||||
|
|
||||||
// Get a specific tile
|
|
||||||
let tile = maze.get_tile(&Hex::new(1, -1)).unwrap();
|
|
||||||
|
|
||||||
// Check if a wall exists
|
|
||||||
let has_wall = tile.walls().contains(EdgeDirection::FLAT_NORTH);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Full documentation is available at [docs.rs](https://docs.rs/hexlab).
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
||||||
|
|
||||||
## Acknowledgements
|
|
||||||
|
|
||||||
Hexlab relies on the excellent [hexx](https://github.com/ManevilleF/hexx)
|
|
||||||
library for handling hexagonal grid mathematics, coordinates, and related
|
|
||||||
operations. We're grateful for the robust foundation it provides for working
|
|
||||||
with hexagonal grids.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is dual-licensed under either:
|
|
||||||
|
|
||||||
- MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
317
src/builder.rs
317
src/builder.rs
@ -1,16 +1,38 @@
|
|||||||
use crate::{errors::MazeBuilderError, GeneratorType, Maze};
|
use crate::{
|
||||||
|
generator::{generate_backtracking, GeneratorType},
|
||||||
|
HexMaze,
|
||||||
|
};
|
||||||
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.
|
||||||
///
|
///
|
||||||
/// This struct provides a fluent interface for configuring and building hexagonal mazes.
|
/// This struct provides a fluent interface for configuring and building hexagonal mazes.
|
||||||
/// It offers flexibility in specifying the maze size, random seed, generation algorithm,
|
/// It offers flexibility in specifying the maze size, random seed, and generation algorithm.
|
||||||
/// and starting position.
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let maze = MazeBuilder::new()
|
/// let maze = MazeBuilder::new()
|
||||||
@ -20,11 +42,11 @@ use hexx::Hex;
|
|||||||
///
|
///
|
||||||
/// // A radius of 5 creates 61 hexagonal tiles
|
/// // A radius of 5 creates 61 hexagonal tiles
|
||||||
/// assert!(!maze.is_empty());
|
/// assert!(!maze.is_empty());
|
||||||
/// assert_eq!(maze.count(), 91);
|
/// assert_eq!(maze.len(), 91);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Using a seed for reproducible results:
|
/// Using a seed for reproducible results:
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let maze1 = MazeBuilder::new()
|
/// let maze1 = MazeBuilder::new()
|
||||||
@ -40,12 +62,12 @@ use hexx::Hex;
|
|||||||
/// .expect("Failed to create maze");
|
/// .expect("Failed to create maze");
|
||||||
///
|
///
|
||||||
/// // Same seed should produce identical mazes
|
/// // Same seed should produce identical mazes
|
||||||
/// assert_eq!(maze1.count(), maze2.count());
|
/// assert_eq!(maze1.len(), maze2.len());
|
||||||
/// assert_eq!(maze1, maze2);
|
/// assert_eq!(maze1, maze2);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Specifying a custom generator:
|
/// Specifying a custom generator:
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let maze = MazeBuilder::new()
|
/// let maze = MazeBuilder::new()
|
||||||
@ -57,14 +79,14 @@ use hexx::Hex;
|
|||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MazeBuilder {
|
pub struct MazeBuilder {
|
||||||
radius: Option<u16>,
|
radius: Option<u32>,
|
||||||
seed: Option<u64>,
|
seed: Option<u64>,
|
||||||
generator_type: GeneratorType,
|
generator_type: GeneratorType,
|
||||||
start_position: Option<Hex>,
|
start_position: Option<Hex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MazeBuilder {
|
impl MazeBuilder {
|
||||||
/// Creates a new [`MazeBuilder`] instance with default settings.
|
/// Creates a new [`MazeBuilder`] instance.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -73,28 +95,21 @@ impl MazeBuilder {
|
|||||||
|
|
||||||
/// Sets the radius for the hexagonal maze.
|
/// Sets the radius for the hexagonal maze.
|
||||||
///
|
///
|
||||||
/// The radius determines the size of the maze, specifically the number of tiles
|
|
||||||
/// from the center (0,0) to the edge of the hexagon, not including the center tile.
|
|
||||||
/// For example, a radius of 3 would create a maze with 3 tiles from center to edge,
|
|
||||||
/// resulting in a total diameter of 7 tiles (3 + 1 + 3).
|
|
||||||
///
|
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `radius` - The number of tiles from the center to the edge of the hexagon.
|
/// * `radius` - The size of the maze (number of tiles along one edge).
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_radius(mut self, radius: u16) -> Self {
|
pub const fn with_radius(mut self, radius: u32) -> Self {
|
||||||
self.radius = Some(radius);
|
self.radius = Some(radius);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the random seed for maze generation.
|
/// Sets the random seed for maze generation.
|
||||||
///
|
///
|
||||||
/// Using the same seed will produce identical mazes, allowing for reproducible results.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `seed` - The random seed value.
|
/// * `seed` - The random seed value.
|
||||||
#[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 {
|
||||||
@ -108,18 +123,14 @@ impl MazeBuilder {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - `generator_type` - The maze generation algorithm to use.
|
/// * `generator_type` - The maze generation algorithm to use.
|
||||||
#[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;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Sets the starting position for maze generation.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `pos` - The hexagonal coordinates for the starting position.
|
|
||||||
#[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 {
|
||||||
@ -136,7 +147,7 @@ impl MazeBuilder {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// // Should fail without radius
|
/// // Should fail without radius
|
||||||
@ -152,35 +163,41 @@ impl MazeBuilder {
|
|||||||
/// let maze = result.unwrap();
|
/// let maze = result.unwrap();
|
||||||
/// assert!(!maze.is_empty());
|
/// assert!(!maze.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn build(self) -> Result<Maze, MazeBuilderError> {
|
pub fn build(self) -> Result<HexMaze, 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(&start_pos).is_none() {
|
if maze.get_tile(&start_pos).is_none() {
|
||||||
return Err(MazeBuilderError::InvalidStartPosition(start_pos));
|
return Err(MazeBuilderError::InvalidStartPosition(start_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !maze.is_empty() {
|
if !maze.is_empty() {
|
||||||
self.generator_type
|
self.generate_maze(&mut maze);
|
||||||
.generate(&mut maze, self.start_position, self.seed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(maze)
|
Ok(maze)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_hex_maze(radius: u16) -> Maze {
|
fn generate_maze(&self, maze: &mut HexMaze) {
|
||||||
let mut maze = Maze::new();
|
match self.generator_type {
|
||||||
let radius = i32::from(radius);
|
GeneratorType::RecursiveBacktracking => {
|
||||||
|
generate_backtracking(maze, self.start_position, self.seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn create_hex_maze(radius: u32) -> HexMaze {
|
||||||
|
let mut maze = HexMaze::new();
|
||||||
|
let radius = i32::try_from(radius).unwrap_or(5);
|
||||||
|
|
||||||
for q in -radius..=radius {
|
for q in -radius..=radius {
|
||||||
let r1 = (-radius).max(-q - radius);
|
let r1 = (-radius).max(-q - radius);
|
||||||
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.insert(pos);
|
maze.add_tile(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,68 +206,194 @@ pub fn create_hex_maze(radius: u16) -> Maze {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use hexx::EdgeDirection;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use claims::{assert_gt, assert_some};
|
|
||||||
use rstest::rstest;
|
/// Helper function to count the number of tiles for a given radius
|
||||||
|
fn calculate_hex_tiles(radius: u32) -> usize {
|
||||||
|
let r = radius as i32;
|
||||||
|
(3 * r * r + 3 * r + 1) as usize
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn maze_builder_new() {
|
fn new_builder() {
|
||||||
let builder = MazeBuilder::new();
|
let builder = MazeBuilder::new();
|
||||||
assert_eq!(builder.radius, None);
|
assert!(builder.radius.is_none());
|
||||||
assert_eq!(builder.seed, None);
|
assert!(builder.seed.is_none());
|
||||||
assert_eq!(builder.generator_type, GeneratorType::default());
|
assert!(builder.start_position.is_none());
|
||||||
assert_eq!(builder.start_position, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(0, 1)] // Minimum size is 1 tile
|
|
||||||
#[case(1, 7)]
|
|
||||||
#[case(2, 19)]
|
|
||||||
#[case(3, 37)]
|
|
||||||
#[case(10, 331)]
|
|
||||||
#[case(100, 30301)]
|
|
||||||
fn create_hex_maze_various_radii(#[case] radius: u16, #[case] expected_size: usize) {
|
|
||||||
let maze = create_hex_maze(radius);
|
|
||||||
assert_eq!(maze.count(), expected_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_hex_maze_large_radius() {
|
fn builder_with_radius() {
|
||||||
let large_radius = 1000;
|
let radius = 5;
|
||||||
let maze = create_hex_maze(large_radius);
|
let maze = MazeBuilder::new().with_radius(radius).build().unwrap();
|
||||||
assert_gt!(maze.count(), 0);
|
|
||||||
|
|
||||||
// Calculate expected size for this radius
|
assert_eq!(maze.len(), calculate_hex_tiles(radius));
|
||||||
let expected_size = 3 * (large_radius as usize).pow(2) + 3 * large_radius as usize + 1;
|
assert!(maze.get_tile(&Hex::ZERO).is_some());
|
||||||
assert_eq!(maze.count(), expected_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_hex_maze_tile_positions() {
|
fn builder_without_radius() {
|
||||||
let maze = create_hex_maze(2);
|
let maze = MazeBuilder::new().build();
|
||||||
let expected_positions = [
|
assert!(matches!(maze, Err(MazeBuilderError::NoRadius)));
|
||||||
Hex::new(0, 0),
|
}
|
||||||
Hex::new(1, -1),
|
|
||||||
Hex::new(1, 0),
|
#[test]
|
||||||
Hex::new(0, 1),
|
fn builder_with_seed() {
|
||||||
Hex::new(-1, 1),
|
let radius = 3;
|
||||||
Hex::new(-1, 0),
|
let seed = 12345;
|
||||||
Hex::new(0, -1),
|
|
||||||
Hex::new(2, -2),
|
let maze1 = MazeBuilder::new()
|
||||||
Hex::new(2, -1),
|
.with_radius(radius)
|
||||||
Hex::new(2, 0),
|
.with_seed(seed)
|
||||||
Hex::new(1, 1),
|
.build()
|
||||||
Hex::new(0, 2),
|
.unwrap();
|
||||||
Hex::new(-1, 2),
|
|
||||||
Hex::new(-2, 2),
|
let maze2 = MazeBuilder::new()
|
||||||
Hex::new(-2, 1),
|
.with_radius(radius)
|
||||||
Hex::new(-2, 0),
|
.with_seed(seed)
|
||||||
Hex::new(-1, -1),
|
.build()
|
||||||
Hex::new(0, -2),
|
.unwrap();
|
||||||
Hex::new(1, -2),
|
|
||||||
];
|
// Same seed should produce identical mazes
|
||||||
for pos in &expected_positions {
|
assert_eq!(maze1, maze2);
|
||||||
assert_some!(maze.get(pos), "Expected tile at {pos:?}");
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_seeds_produce_different_mazes() {
|
||||||
|
let radius = 3;
|
||||||
|
|
||||||
|
let maze1 = MazeBuilder::new()
|
||||||
|
.with_radius(radius)
|
||||||
|
.with_seed(12345)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let maze2 = MazeBuilder::new()
|
||||||
|
.with_radius(radius)
|
||||||
|
.with_seed(54321)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Different seeds should produce different mazes
|
||||||
|
assert_ne!(maze1, maze2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maze_connectivity() {
|
||||||
|
let radius = 3;
|
||||||
|
let maze = MazeBuilder::new().with_radius(radius).build().unwrap();
|
||||||
|
|
||||||
|
// Helper function to count accessible neighbors
|
||||||
|
fn count_accessible_neighbors(maze: &HexMaze, pos: Hex) -> usize {
|
||||||
|
EdgeDirection::ALL_DIRECTIONS
|
||||||
|
.iter()
|
||||||
|
.filter(|&&dir| {
|
||||||
|
let neighbor = pos + dir;
|
||||||
|
if let Some(walls) = maze.get_walls(&pos) {
|
||||||
|
!walls.contains(dir) && maze.get_tile(&neighbor).is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that each tile has at least one connection
|
||||||
|
for &pos in maze.keys() {
|
||||||
|
let accessible_neighbors = count_accessible_neighbors(&maze, pos);
|
||||||
|
assert!(
|
||||||
|
accessible_neighbors > 0,
|
||||||
|
"Tile at {:?} has no accessible neighbors",
|
||||||
|
pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn start_position() {
|
||||||
|
let radius = 3;
|
||||||
|
let start_pos = Hex::new(1, 1);
|
||||||
|
|
||||||
|
let maze = MazeBuilder::new()
|
||||||
|
.with_radius(radius)
|
||||||
|
.with_start_position(start_pos)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(maze.get_tile(&start_pos).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_start_position() {
|
||||||
|
let maze = MazeBuilder::new()
|
||||||
|
.with_radius(3)
|
||||||
|
.with_start_position(Hex::new(10, 10))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
maze,
|
||||||
|
Err(MazeBuilderError::InvalidStartPosition(_))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maze_boundaries() {
|
||||||
|
let radius = 3;
|
||||||
|
let maze = MazeBuilder::new().with_radius(radius).build().unwrap();
|
||||||
|
|
||||||
|
// Test that tiles exist within the radius
|
||||||
|
for q in -(radius as i32)..=(radius as i32) {
|
||||||
|
for r in -(radius as i32)..=(radius as i32) {
|
||||||
|
let pos = Hex::new(q, r);
|
||||||
|
if q.abs() + r.abs() <= radius as i32 {
|
||||||
|
assert!(
|
||||||
|
maze.get_tile(&pos).is_some(),
|
||||||
|
"Expected tile at {:?} to exist",
|
||||||
|
pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_radii() {
|
||||||
|
for radius in 1..=5 {
|
||||||
|
let maze = MazeBuilder::new().with_radius(radius).build().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
maze.len(),
|
||||||
|
calculate_hex_tiles(radius),
|
||||||
|
"Incorrect number of tiles for radius {}",
|
||||||
|
radius
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wall_consistency() {
|
||||||
|
let radius = 3;
|
||||||
|
let maze = MazeBuilder::new().with_radius(radius).build().unwrap();
|
||||||
|
|
||||||
|
// Check that if tile A has no wall to tile B,
|
||||||
|
// then tile B has no wall to tile A
|
||||||
|
for &pos in maze.keys() {
|
||||||
|
for &dir in &EdgeDirection::ALL_DIRECTIONS {
|
||||||
|
let neighbor = pos + dir;
|
||||||
|
if let (Some(walls), Some(neighbor_walls)) =
|
||||||
|
(maze.get_walls(&pos), maze.get_walls(&neighbor))
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
walls.contains(dir),
|
||||||
|
neighbor_walls.contains(dir.const_neg()),
|
||||||
|
"Wall inconsistency between {:?} and {:?}",
|
||||||
|
pos,
|
||||||
|
neighbor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
use hexx::{EdgeDirection, Hex};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::Tile;
|
|
||||||
|
|
||||||
#[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 when attempting to access or modify a tile at a non-existent coordinate.
|
|
||||||
#[error("Invalid coordinate: {0:?}")]
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
50
src/generator.rs
Normal file
50
src/generator.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use hexx::{EdgeDirection, Hex};
|
||||||
|
use rand::{seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng};
|
||||||
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
|
||||||
|
use crate::HexMaze;
|
||||||
|
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub enum GeneratorType {
|
||||||
|
#[default]
|
||||||
|
RecursiveBacktracking,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_backtracking(maze: &mut HexMaze, start_pos: Option<Hex>, seed: Option<u64>) {
|
||||||
|
if maze.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = start_pos.unwrap_or(Hex::ZERO);
|
||||||
|
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
|
||||||
|
let mut rng: Box<dyn RngCore> = seed.map_or_else(
|
||||||
|
|| Box::new(thread_rng()) as Box<dyn RngCore>,
|
||||||
|
|seed| Box::new(ChaCha8Rng::seed_from_u64(seed)) as Box<dyn RngCore>,
|
||||||
|
);
|
||||||
|
recursive_backtrack(maze, start, &mut visited, &mut rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recursive_backtrack<R: Rng>(
|
||||||
|
maze: &mut HexMaze,
|
||||||
|
current: Hex,
|
||||||
|
visited: &mut HashSet<Hex>,
|
||||||
|
rng: &mut R,
|
||||||
|
) {
|
||||||
|
visited.insert(current);
|
||||||
|
let mut directions = EdgeDirection::ALL_DIRECTIONS;
|
||||||
|
directions.shuffle(rng);
|
||||||
|
|
||||||
|
for direction in directions {
|
||||||
|
let neighbor = current + direction;
|
||||||
|
if maze.get_tile(&neighbor).is_some() && !visited.contains(&neighbor) {
|
||||||
|
maze.remove_tile_wall(¤t, direction);
|
||||||
|
maze.remove_tile_wall(&neighbor, direction.const_neg());
|
||||||
|
recursive_backtrack(maze, neighbor, visited, rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,116 +0,0 @@
|
|||||||
use crate::Maze;
|
|
||||||
use hexx::{EdgeDirection, Hex};
|
|
||||||
use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
pub(super) fn generate_backtracking(maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
|
|
||||||
if maze.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = start_pos.unwrap_or(Hex::ZERO);
|
|
||||||
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
|
|
||||||
let mut rng: Box<dyn RngCore> = seed.map_or_else(
|
|
||||||
|| Box::new(thread_rng()) as Box<dyn RngCore>,
|
|
||||||
|seed| Box::new(StdRng::seed_from_u64(seed)) as Box<dyn RngCore>,
|
|
||||||
);
|
|
||||||
|
|
||||||
recursive_backtrack(maze, start, &mut visited, &mut rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recursive_backtrack<R: Rng>(
|
|
||||||
maze: &mut Maze,
|
|
||||||
current: Hex,
|
|
||||||
visited: &mut HashSet<Hex>,
|
|
||||||
rng: &mut R,
|
|
||||||
) {
|
|
||||||
visited.insert(current);
|
|
||||||
let mut directions = EdgeDirection::ALL_DIRECTIONS;
|
|
||||||
directions.shuffle(rng);
|
|
||||||
|
|
||||||
for direction in directions {
|
|
||||||
let neighbor = current + direction;
|
|
||||||
if maze.get(&neighbor).is_some() && !visited.contains(&neighbor) {
|
|
||||||
let _ = maze.remove_tile_wall(¤t, direction);
|
|
||||||
let _ = maze.remove_tile_wall(&neighbor, direction.const_neg());
|
|
||||||
recursive_backtrack(maze, neighbor, visited, rng);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::builder::create_hex_maze;
|
|
||||||
use claims::assert_some;
|
|
||||||
use rstest::rstest;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(Hex::ZERO)]
|
|
||||||
#[case(Hex::new(1, -1))]
|
|
||||||
#[case(Hex::new(-2, 2))]
|
|
||||||
fn recursive_backtrack_start_visited(#[case] start: Hex) {
|
|
||||||
let mut maze = create_hex_maze(3);
|
|
||||||
let mut rng = StdRng::seed_from_u64(12345);
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
|
|
||||||
recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
|
|
||||||
|
|
||||||
assert!(visited.contains(&start), "Start position should be visited");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(Hex::ZERO)]
|
|
||||||
#[case(Hex::new(1, -1))]
|
|
||||||
#[case(Hex::new(-2, 2))]
|
|
||||||
fn recursive_backtrack_walls_removed(#[case] start: Hex) {
|
|
||||||
let mut maze = create_hex_maze(3);
|
|
||||||
let mut rng = StdRng::seed_from_u64(12345);
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
|
|
||||||
recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
|
|
||||||
|
|
||||||
for &pos in maze.keys() {
|
|
||||||
let walls = assert_some!(maze.get_walls(&pos));
|
|
||||||
assert!(
|
|
||||||
walls.count() < 6,
|
|
||||||
"At least one wall should be removed for each tile"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(Hex::ZERO)]
|
|
||||||
#[case(Hex::new(1, -1))]
|
|
||||||
#[case(Hex::new(-2, 2))]
|
|
||||||
fn recursive_backtrack_connectivity(#[case] start: Hex) {
|
|
||||||
let mut maze = create_hex_maze(3);
|
|
||||||
let mut rng = StdRng::seed_from_u64(12345);
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
|
|
||||||
recursive_backtrack(&mut maze, start, &mut visited, &mut rng);
|
|
||||||
|
|
||||||
let mut to_visit = vec![start];
|
|
||||||
let mut connected = HashSet::new();
|
|
||||||
while let Some(current) = to_visit.pop() {
|
|
||||||
if !connected.insert(current) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for dir in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
let neighbor = current + dir;
|
|
||||||
if let Some(walls) = maze.get_walls(¤t) {
|
|
||||||
if !walls.contains(dir) && maze.get(&neighbor).is_some() {
|
|
||||||
to_visit.push(neighbor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
connected.len(),
|
|
||||||
maze.count(),
|
|
||||||
"All tiles should be connected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
mod backtrack;
|
|
||||||
use crate::Maze;
|
|
||||||
use backtrack::generate_backtracking;
|
|
||||||
#[cfg(feature = "bevy")]
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use hexx::Hex;
|
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
|
||||||
#[cfg_attr(feature = "bevy", derive(Component))]
|
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
|
||||||
pub enum GeneratorType {
|
|
||||||
#[default]
|
|
||||||
RecursiveBacktracking,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GeneratorType {
|
|
||||||
pub fn generate(&self, maze: &mut Maze, start_pos: Option<Hex>, seed: Option<u64>) {
|
|
||||||
match self {
|
|
||||||
Self::RecursiveBacktracking => generate_backtracking(maze, start_pos, seed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
250
src/hex_maze.rs
Normal file
250
src/hex_maze.rs
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
|
use hexx::{EdgeDirection, Hex};
|
||||||
|
|
||||||
|
use super::{HexTile, Walls};
|
||||||
|
|
||||||
|
/// Represents a hexagonal maze with tiles and walls
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
|
pub struct HexMaze(HashMap<Hex, HexTile>);
|
||||||
|
|
||||||
|
impl HexMaze {
|
||||||
|
/// Creates a new empty maze
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new tile at the specified coordinates
|
||||||
|
pub fn add_tile(&mut self, coords: Hex) {
|
||||||
|
let tile = HexTile::new(coords);
|
||||||
|
self.0.insert(coords, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a wall in the specified direction at the given coordinates
|
||||||
|
pub fn add_wall(&mut self, coord: Hex, direction: EdgeDirection) {
|
||||||
|
if let Some(tile) = self.0.get_mut(&coord) {
|
||||||
|
tile.walls.add(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the tile at the specified coordinates
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_tile(&self, coord: &Hex) -> Option<&HexTile> {
|
||||||
|
self.0.get(coord)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the walls at the specified coordinates
|
||||||
|
pub fn get_walls(&self, coord: &Hex) -> Option<&Walls> {
|
||||||
|
self.0.get(coord).map(HexTile::walls)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of tiles in the maze
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the maze is empty
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_tile_wall(&mut self, coord: &Hex, direction: EdgeDirection) {
|
||||||
|
if let Some(tile) = self.0.get_mut(coord) {
|
||||||
|
tile.walls.remove(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for HexMaze {
|
||||||
|
type Target = HashMap<Hex, HexTile>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for HexMaze {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_maze() {
|
||||||
|
let maze = HexMaze::default();
|
||||||
|
assert!(maze.is_empty(), "New maze should be empty");
|
||||||
|
assert_eq!(maze.len(), 0, "New maze should have zero tiles");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_tile() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coords = [Hex::ZERO, Hex::new(1, -1), Hex::new(-1, 1)];
|
||||||
|
|
||||||
|
// Add tiles
|
||||||
|
for &coord in &coords {
|
||||||
|
maze.add_tile(coord);
|
||||||
|
assert!(
|
||||||
|
maze.get_tile(&coord).is_some(),
|
||||||
|
"Tile should exist after adding"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
maze.len(),
|
||||||
|
coords.len(),
|
||||||
|
"Maze should contain all added tiles"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wall_operations() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coord = Hex::ZERO;
|
||||||
|
maze.add_tile(coord);
|
||||||
|
|
||||||
|
// Test adding walls
|
||||||
|
let directions = [
|
||||||
|
EdgeDirection::FLAT_TOP,
|
||||||
|
EdgeDirection::FLAT_BOTTOM,
|
||||||
|
EdgeDirection::POINTY_TOP_RIGHT,
|
||||||
|
];
|
||||||
|
|
||||||
|
for &direction in &directions {
|
||||||
|
maze.add_wall(coord, direction);
|
||||||
|
assert!(
|
||||||
|
maze.get_walls(&coord).unwrap().contains(direction),
|
||||||
|
"Wall should exist after adding"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_iteration() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coords = [Hex::ZERO, Hex::new(1, 0), Hex::new(0, 1)];
|
||||||
|
|
||||||
|
// Add tiles
|
||||||
|
for &coord in &coords {
|
||||||
|
maze.add_tile(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test iterator
|
||||||
|
let collected = maze.iter().map(|(_, tile)| tile).collect::<Vec<_>>();
|
||||||
|
assert_eq!(
|
||||||
|
collected.len(),
|
||||||
|
coords.len(),
|
||||||
|
"Iterator should yield all tiles"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maze_clone() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coord = Hex::ZERO;
|
||||||
|
maze.add_tile(coord);
|
||||||
|
maze.add_wall(coord, EdgeDirection::FLAT_TOP);
|
||||||
|
|
||||||
|
// Test cloning
|
||||||
|
let cloned_maze = maze.clone();
|
||||||
|
assert_eq!(
|
||||||
|
maze.len(),
|
||||||
|
cloned_maze.len(),
|
||||||
|
"Cloned maze should have same size"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
cloned_maze
|
||||||
|
.get_walls(&coord)
|
||||||
|
.unwrap()
|
||||||
|
.contains(EdgeDirection::FLAT_TOP),
|
||||||
|
"Cloned maze should preserve wall state"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_tile_operations() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coord = Hex::ZERO;
|
||||||
|
|
||||||
|
// Operations on non-existent tile
|
||||||
|
assert!(
|
||||||
|
maze.get_tile(&coord).is_none(),
|
||||||
|
"Should return None for non-existent tile"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
maze.get_walls(&coord).is_none(),
|
||||||
|
"Should return None for non-existent walls"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Adding wall to non-existent tile should not panic
|
||||||
|
maze.add_wall(coord, EdgeDirection::FLAT_TOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maze_boundaries() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let extreme_coords = [
|
||||||
|
Hex::new(i32::MAX, i32::MIN),
|
||||||
|
Hex::new(i32::MIN, i32::MAX),
|
||||||
|
Hex::new(0, i32::MAX),
|
||||||
|
Hex::new(0, i32::MIN),
|
||||||
|
Hex::new(i32::MAX, 0),
|
||||||
|
Hex::new(i32::MIN, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test with extreme coordinates
|
||||||
|
for &coord in &extreme_coords {
|
||||||
|
maze.add_tile(coord);
|
||||||
|
assert!(
|
||||||
|
maze.get_tile(&coord).is_some(),
|
||||||
|
"Should handle extreme coordinates"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator_consistency() {
|
||||||
|
let mut maze = HexMaze::default();
|
||||||
|
let coords = [Hex::ZERO, Hex::new(1, -1), Hex::new(-1, 1)];
|
||||||
|
|
||||||
|
// Add tiles
|
||||||
|
for &coord in &coords {
|
||||||
|
maze.add_tile(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify iterator
|
||||||
|
let iter_coords = maze.iter().map(|(coord, _)| *coord).collect::<Vec<_>>();
|
||||||
|
assert_eq!(
|
||||||
|
iter_coords.len(),
|
||||||
|
coords.len(),
|
||||||
|
"Iterator should yield all coordinates"
|
||||||
|
);
|
||||||
|
|
||||||
|
for coord in coords {
|
||||||
|
assert!(
|
||||||
|
iter_coords.contains(&coord),
|
||||||
|
"Iterator should contain all added coordinates"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_maze() {
|
||||||
|
let maze = HexMaze::default();
|
||||||
|
assert!(maze.is_empty(), "New maze should be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
213
src/hex_tile.rs
Normal file
213
src/hex_tile.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use hexx::Hex;
|
||||||
|
|
||||||
|
#[cfg(feature = "bevy")]
|
||||||
|
use hexx::HexLayout;
|
||||||
|
|
||||||
|
use super::Walls;
|
||||||
|
#[cfg(feature = "bevy")]
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/// Represents a single hexagonal tile in the maze
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
|
||||||
|
#[cfg_attr(feature = "bevy", reflect(Component))]
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
|
pub struct HexTile {
|
||||||
|
pub(crate) pos: Hex,
|
||||||
|
pub(crate) walls: Walls,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HexTile {
|
||||||
|
/// Creates a new tile with pos and default walls
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(pos: Hex) -> Self {
|
||||||
|
Self {
|
||||||
|
pos,
|
||||||
|
walls: Walls::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the tile's walls
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn walls(&self) -> &Walls {
|
||||||
|
&self.walls
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns position of the tile
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn pos(&self) -> Hex {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bevy")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_vec2(&self, layout: &HexLayout) -> Vec2 {
|
||||||
|
layout.hex_to_world_pos(self.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bevy")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_vec3(&self, layout: &HexLayout) -> Vec3 {
|
||||||
|
let pos = self.to_vec2(layout);
|
||||||
|
Vec3::new(pos.x, 0., pos.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hex> for HexTile {
|
||||||
|
fn from(value: Hex) -> Self {
|
||||||
|
Self {
|
||||||
|
pos: value,
|
||||||
|
walls: Walls::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HexTile {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({},{})", self.pos.x, self.pos.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use hexx::EdgeDirection;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_tile() {
|
||||||
|
let pos = Hex::ZERO;
|
||||||
|
let tile = HexTile::new(pos);
|
||||||
|
|
||||||
|
assert_eq!(tile.pos, pos, "Position should match constructor argument");
|
||||||
|
assert_eq!(
|
||||||
|
tile.walls,
|
||||||
|
Walls::default(),
|
||||||
|
"Walls should be initialized to default"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_walls_accessor() {
|
||||||
|
let pos = Hex::new(1, -1);
|
||||||
|
let tile = HexTile::new(pos);
|
||||||
|
|
||||||
|
// Test walls accessor method
|
||||||
|
let walls_ref = tile.walls();
|
||||||
|
assert_eq!(
|
||||||
|
walls_ref, &tile.walls,
|
||||||
|
"Walls accessor should return reference to walls"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_modification() {
|
||||||
|
let pos = Hex::new(2, 3);
|
||||||
|
let mut tile = HexTile::new(pos);
|
||||||
|
|
||||||
|
// Modify walls
|
||||||
|
tile.walls.remove(EdgeDirection::FLAT_TOP);
|
||||||
|
assert!(
|
||||||
|
!tile.walls.contains(EdgeDirection::FLAT_TOP),
|
||||||
|
"Wall should be removed"
|
||||||
|
);
|
||||||
|
|
||||||
|
tile.walls.add(EdgeDirection::FLAT_TOP);
|
||||||
|
assert!(
|
||||||
|
tile.walls.contains(EdgeDirection::FLAT_TOP),
|
||||||
|
"Wall should be added back"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_clone() {
|
||||||
|
let pos = Hex::new(0, -2);
|
||||||
|
let tile = HexTile::new(pos);
|
||||||
|
|
||||||
|
// Test Clone trait
|
||||||
|
let cloned_tile = tile.clone();
|
||||||
|
assert_eq!(tile, cloned_tile, "Cloned tile should equal original");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_debug() {
|
||||||
|
let pos = Hex::ZERO;
|
||||||
|
let tile = HexTile::new(pos);
|
||||||
|
|
||||||
|
// Test Debug trait
|
||||||
|
let debug_string = format!("{:?}", tile);
|
||||||
|
assert!(
|
||||||
|
debug_string.contains("HexTile"),
|
||||||
|
"Debug output should contain struct name"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_positions() {
|
||||||
|
let positions = [Hex::ZERO, Hex::new(1, 0), Hex::new(-1, 1), Hex::new(2, -2)];
|
||||||
|
|
||||||
|
// Create tiles at different positions
|
||||||
|
let tiles = positions
|
||||||
|
.iter()
|
||||||
|
.map(|&pos| HexTile::new(pos))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Verify each tile has correct position
|
||||||
|
for (tile, &pos) in tiles.iter().zip(positions.iter()) {
|
||||||
|
assert_eq!(
|
||||||
|
tile.pos, pos,
|
||||||
|
"Tile position should match constructor argument"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tile_equality() {
|
||||||
|
let pos1 = Hex::new(1, 1);
|
||||||
|
let pos2 = Hex::new(1, 1);
|
||||||
|
let pos3 = Hex::new(2, 1);
|
||||||
|
|
||||||
|
let tile1 = HexTile::new(pos1);
|
||||||
|
let tile2 = HexTile::new(pos2);
|
||||||
|
let tile3 = HexTile::new(pos3);
|
||||||
|
|
||||||
|
assert_eq!(tile1, tile2, "Tiles with same position should be equal");
|
||||||
|
assert_ne!(
|
||||||
|
tile1, tile3,
|
||||||
|
"Tiles with different positions should not be equal"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with modified walls
|
||||||
|
let mut tile4 = HexTile::new(pos1);
|
||||||
|
tile4.walls.remove(EdgeDirection::FLAT_TOP);
|
||||||
|
assert_ne!(
|
||||||
|
tile1, tile4,
|
||||||
|
"Tiles with different walls should not be equal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_boundaries() {
|
||||||
|
// Test with extreme coordinate values
|
||||||
|
let extreme_positions = [
|
||||||
|
Hex::new(i32::MAX, i32::MIN),
|
||||||
|
Hex::new(i32::MIN, i32::MAX),
|
||||||
|
Hex::new(0, i32::MAX),
|
||||||
|
Hex::new(i32::MIN, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
for pos in extreme_positions {
|
||||||
|
let tile = HexTile::new(pos);
|
||||||
|
assert_eq!(
|
||||||
|
tile.pos, pos,
|
||||||
|
"Tile should handle extreme coordinate values"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/lib.rs
69
src/lib.rs
@ -1,73 +1,16 @@
|
|||||||
//! Hexlab is a library for generating and manipulating hexagonal mazes.
|
|
||||||
//!
|
|
||||||
//! # Features
|
|
||||||
//!
|
|
||||||
//! - Create hexagonal mazes of configurable size
|
|
||||||
//! - Customizable maze properties (radius, start position, seed)
|
|
||||||
//! - Efficient bit-flag representation of walls
|
|
||||||
//! - Multiple maze generation algorithms
|
|
||||||
//! - Maze builder pattern for easy maze creation
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! Here's a quick example to create a simple hexagonal maze:
|
|
||||||
//!
|
|
||||||
//!```
|
|
||||||
//! use hexlab::prelude::*;
|
|
||||||
//!
|
|
||||||
//! let maze = MazeBuilder::new()
|
|
||||||
//! .with_radius(3)
|
|
||||||
//! .build()
|
|
||||||
//! .expect("Failed to create maze");
|
|
||||||
//!
|
|
||||||
//! assert_eq!(maze.count(), 37); // A radius of 3 should create 37 tiles
|
|
||||||
//!```
|
|
||||||
//!
|
|
||||||
//! Customizing maze generation:
|
|
||||||
//!
|
|
||||||
//!```
|
|
||||||
//! use hexlab::prelude::*;
|
|
||||||
//!
|
|
||||||
//! let maze = MazeBuilder::new()
|
|
||||||
//! .with_radius(2)
|
|
||||||
//! .with_seed(12345)
|
|
||||||
//! .with_start_position(Hex::new(1, -1))
|
|
||||||
//! .build()
|
|
||||||
//! .expect("Failed to create maze");
|
|
||||||
//!
|
|
||||||
//! assert!(maze.get(&Hex::new(1, -1)).is_some());
|
|
||||||
//!```
|
|
||||||
//!
|
|
||||||
//! Manipulating walls:
|
|
||||||
//!
|
|
||||||
//!```
|
|
||||||
//! use hexlab::prelude::*;
|
|
||||||
//!
|
|
||||||
//! let mut walls = Walls::empty();
|
|
||||||
//! assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
|
|
||||||
//! assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
|
||||||
//! assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
|
|
||||||
//!```
|
|
||||||
mod builder;
|
mod builder;
|
||||||
pub mod errors;
|
|
||||||
mod generator;
|
mod generator;
|
||||||
mod maze;
|
mod hex_maze;
|
||||||
#[cfg(feature = "pathfinding")]
|
mod hex_tile;
|
||||||
mod pathfinding;
|
|
||||||
mod tile;
|
|
||||||
pub mod traits;
|
|
||||||
mod walls;
|
mod walls;
|
||||||
|
|
||||||
pub use builder::MazeBuilder;
|
pub use builder::{MazeBuilder, MazeBuilderError};
|
||||||
pub use errors::*;
|
|
||||||
pub use generator::GeneratorType;
|
pub use generator::GeneratorType;
|
||||||
pub use maze::Maze;
|
pub use hex_maze::HexMaze;
|
||||||
pub use tile::Tile;
|
pub use hex_tile::HexTile;
|
||||||
pub use traits::*;
|
|
||||||
pub use walls::Walls;
|
pub use walls::Walls;
|
||||||
|
|
||||||
/// Prelude module containing commonly used types
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{errors::*, traits::*, GeneratorType, Maze, MazeBuilder, Tile, Walls};
|
pub use super::{GeneratorType, HexMaze, HexTile, MazeBuilder, MazeBuilderError, Walls};
|
||||||
pub use hexx::{EdgeDirection, Hex, HexLayout};
|
pub use hexx::{EdgeDirection, Hex, HexLayout};
|
||||||
}
|
}
|
||||||
|
|||||||
380
src/maze.rs
380
src/maze.rs
@ -1,380 +0,0 @@
|
|||||||
use super::{Tile, Walls};
|
|
||||||
use crate::{
|
|
||||||
errors::MazeError,
|
|
||||||
traits::{TilePosition, WallStorage},
|
|
||||||
};
|
|
||||||
#[cfg(feature = "bevy")]
|
|
||||||
use bevy::prelude::*;
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
use bevy_utils::HashMap;
|
|
||||||
use hexx::{EdgeDirection, Hex};
|
|
||||||
#[cfg(not(feature = "bevy_reflect"))]
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
/// Represents a hexagonal maze with tiles and walls.
|
|
||||||
///
|
|
||||||
/// This struct stores the layout of a hexagonal maze, including the positions
|
|
||||||
/// of tiles and their associated walls.
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
|
||||||
#[cfg_attr(feature = "bevy", derive(Component))]
|
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
||||||
pub struct Maze(HashMap<Hex, Tile>);
|
|
||||||
|
|
||||||
impl Maze {
|
|
||||||
/// Creates a new empty maze
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let maze = Maze::new();
|
|
||||||
///
|
|
||||||
/// assert!(maze.is_empty());
|
|
||||||
/// assert_eq!(maze.count(), 0);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
///
|
|
||||||
/// - `coords` - The hexagonal coordinates where the tile should be added.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
/// let coord = Hex::ZERO;
|
|
||||||
///
|
|
||||||
/// assert_eq!(maze.insert(coord), None);
|
|
||||||
/// assert_eq!(maze.insert(coord), Some(Tile::new(coord)));
|
|
||||||
/// ```
|
|
||||||
pub fn insert(&mut self, coords: Hex) -> Option<Tile> {
|
|
||||||
let tile = Tile::new(coords);
|
|
||||||
self.0.insert(coords, tile)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new tile at the specified coordinates. It is recommended to use [`insert`].
|
|
||||||
///
|
|
||||||
/// [`insert`]: Maze::insert
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `coords` - The hexagonal coordinates where the tile should be added.
|
|
||||||
/// - `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
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// 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) -> Result<Tile, MazeError> {
|
|
||||||
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.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `coord` - The hexagonal coordinates of the tile to retrieve.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
/// let coord = Hex::ZERO;
|
|
||||||
/// maze.insert(coord);
|
|
||||||
///
|
|
||||||
/// assert!(maze.get(&coord).is_some());
|
|
||||||
/// assert!(maze.get(&Hex::new(1, 1)).is_none());
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn get(&self, coord: &Hex) -> Option<&Tile> {
|
|
||||||
self.0.get(coord)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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
|
|
||||||
///
|
|
||||||
/// - `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 walls = maze.get_walls(&coord).unwrap();
|
|
||||||
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_walls(&self, coord: &Hex) -> Option<&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.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
/// assert_eq!(maze.count(), 0);
|
|
||||||
///
|
|
||||||
/// maze.insert(Hex::new(0, 0));
|
|
||||||
/// assert_eq!(maze.count(), 1);
|
|
||||||
///
|
|
||||||
/// maze.insert(Hex::new(1, -1));
|
|
||||||
/// assert_eq!(maze.count(), 2);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the maze contains no tiles.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
/// assert!(maze.is_empty());
|
|
||||||
///
|
|
||||||
/// maze.insert(Hex::ZERO);
|
|
||||||
/// assert!(!maze.is_empty());
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a wall from a tile in the specified direction.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `coord` - The hexagonal coordinates of the tile.
|
|
||||||
/// - `direction` - The direction of the wall to remove.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns `MazeError::InvalidCoordinate` if the specified coordinate does not exist in the maze.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// // Create a maze with a single tile at the origin
|
|
||||||
/// let mut tile = Tile::new(Hex::ZERO);
|
|
||||||
/// tile.walls_mut().toggle(Walls::all_directions());
|
|
||||||
/// let mut maze = Maze::from([tile]);
|
|
||||||
///
|
|
||||||
/// // Initially, the tile should have no walls
|
|
||||||
/// assert!(maze.get_walls(&Hex::ZERO).unwrap().is_empty());
|
|
||||||
///
|
|
||||||
/// // Add a wall to the north
|
|
||||||
/// assert!(maze.add_tile_wall(&Hex::ZERO, EdgeDirection::FLAT_NORTH).is_ok());
|
|
||||||
///
|
|
||||||
/// // Check that the wall was added
|
|
||||||
/// let walls = maze.get_walls(&Hex::ZERO).unwrap();
|
|
||||||
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
|
||||||
/// assert_eq!(walls.count(), 1);
|
|
||||||
///
|
|
||||||
/// // Adding the same wall again should return true (no change)
|
|
||||||
/// assert_eq!(maze.add_tile_wall(&Hex::ZERO, EdgeDirection::FLAT_NORTH), Ok(true));
|
|
||||||
///
|
|
||||||
/// // Adding a wall to a non-existent tile should return an error
|
|
||||||
/// let invalid_coord = Hex::new(1, 1);
|
|
||||||
/// assert_eq!(
|
|
||||||
/// maze.add_tile_wall(&invalid_coord, EdgeDirection::FLAT_NORTH),
|
|
||||||
/// Err(MazeError::InvalidCoordinate(invalid_coord))
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn add_tile_wall(
|
|
||||||
&mut self,
|
|
||||||
coord: &Hex,
|
|
||||||
direction: EdgeDirection,
|
|
||||||
) -> Result<bool, MazeError> {
|
|
||||||
self.0
|
|
||||||
.get_mut(coord)
|
|
||||||
.map(|tile| tile.walls.insert(direction))
|
|
||||||
.ok_or(MazeError::InvalidCoordinate(*coord))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a wall from a tile in the specified direction.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `coord` - The hexagonal coordinates of the tile.
|
|
||||||
/// - `direction` - The direction of the wall to remove.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns `MazeError::InvalidCoordinate` if the specified coordinate does not exist in the maze.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut maze = Maze::new();
|
|
||||||
/// let coord = Hex::ZERO;
|
|
||||||
/// maze.insert(coord);
|
|
||||||
///
|
|
||||||
/// maze.add_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
|
|
||||||
/// maze.remove_tile_wall(&coord, EdgeDirection::FLAT_NORTH);
|
|
||||||
///
|
|
||||||
/// let walls = maze.get_walls(&coord).unwrap();
|
|
||||||
/// assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
|
|
||||||
/// ```
|
|
||||||
pub fn remove_tile_wall(
|
|
||||||
&mut self,
|
|
||||||
coord: &Hex,
|
|
||||||
direction: EdgeDirection,
|
|
||||||
) -> Result<bool, MazeError> {
|
|
||||||
self.0
|
|
||||||
.get_mut(coord)
|
|
||||||
.map(|tile| tile.walls.remove(direction))
|
|
||||||
.ok_or(MazeError::InvalidCoordinate(*coord))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<Hex> for Maze {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Hex>>(iter: T) -> Self {
|
|
||||||
Self(iter.into_iter().map(|hex| (hex, Tile::new(hex))).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<Tile> for Maze {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Tile>>(iter: T) -> Self {
|
|
||||||
Self(iter.into_iter().map(|tile| (tile.pos(), tile)).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<(Hex, Tile)> for Maze {
|
|
||||||
fn from_iter<T: IntoIterator<Item = (Hex, Tile)>>(iter: T) -> Self {
|
|
||||||
Self(iter.into_iter().collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> From<[Hex; N]> for Maze {
|
|
||||||
fn from(value: [Hex; N]) -> Self {
|
|
||||||
value.into_iter().collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> From<[Tile; N]> for Maze {
|
|
||||||
fn from(value: [Tile; N]) -> Self {
|
|
||||||
value.into_iter().collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Maze {
|
|
||||||
type Target = HashMap<Hex, Tile>;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Maze {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
//! 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
250
src/tile.rs
250
src/tile.rs
@ -1,250 +0,0 @@
|
|||||||
use super::Walls;
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
use crate::traits::WorldPositionable;
|
|
||||||
use crate::traits::{TilePosition, WallStorage};
|
|
||||||
#[cfg(feature = "bevy")]
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use hexx::Hex;
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
use hexx::HexLayout;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
/// Represents a single hexagonal tile in the maze
|
|
||||||
///
|
|
||||||
/// Each tile has a position and a set of walls defining its boundaries.
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
|
||||||
#[cfg_attr(feature = "bevy", derive(Component))]
|
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
||||||
pub struct Tile {
|
|
||||||
pub(crate) pos: Hex,
|
|
||||||
pub(crate) walls: Walls,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TilePosition for Tile {
|
|
||||||
/// Returns position of the tile
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let tile = Tile::new(Hex::new(2, -2));
|
|
||||||
/// assert_eq!(tile.pos(), Hex::new(2, -2));
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn pos(&self) -> Hex {
|
|
||||||
self.pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WallStorage for Tile {
|
|
||||||
/// Returns an immutable reference to the tile's walls
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let tile = Tile::new(Hex::ZERO);
|
|
||||||
/// assert_eq!(*tile.walls(), Walls::default());
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn walls(&self) -> &Walls {
|
|
||||||
&self.walls
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the tile's walls
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let tile = Tile::new(Hex::ZERO);
|
|
||||||
/// assert_eq!(*tile.walls(), Walls::default());
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
fn walls_mut(&mut self) -> &mut Walls {
|
|
||||||
&mut self.walls
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
impl WorldPositionable for Tile {
|
|
||||||
/// Converts the tile's position to a 2D vector based on the given layout.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `layout` - The hexagonal layout used for conversion.
|
|
||||||
#[inline]
|
|
||||||
fn to_vec2(&self, layout: &HexLayout) -> glam::Vec2 {
|
|
||||||
layout.hex_to_world_pos(self.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the tile's position to a 3D vector based on the given layout.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `layout` - The hexagonal layout used for conversion.
|
|
||||||
#[inline]
|
|
||||||
fn to_vec3(&self, layout: &HexLayout) -> glam::Vec3 {
|
|
||||||
let pos = self.to_vec2(layout);
|
|
||||||
glam::Vec3::new(pos.x, 0., pos.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tile {
|
|
||||||
/// Creates a new tile with the given position and default walls.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `pos` - The hexagonal coordinates of the tile.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
|
||||||
///
|
|
||||||
/// let tile = Tile::new(Hex::new(1, -1));
|
|
||||||
/// assert_eq!(tile.pos(), Hex::new(1, -1));
|
|
||||||
/// assert_eq!(*tile.walls(), Walls::default());
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(pos: Hex) -> Self {
|
|
||||||
Self {
|
|
||||||
pos,
|
|
||||||
walls: Walls::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Hex> for Tile {
|
|
||||||
fn from(value: Hex) -> Self {
|
|
||||||
Self {
|
|
||||||
pos: value,
|
|
||||||
walls: Walls::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Tile {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "({},{})", self.pos.x, self.pos.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use hexx::EdgeDirection;
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
|
|
||||||
fn random_hex() -> Hex {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
Hex::new(rng.gen(), rng.gen())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn different_positions() {
|
|
||||||
let positions = [Hex::ZERO, Hex::new(1, 0), Hex::new(-1, 1), Hex::new(2, -2)];
|
|
||||||
|
|
||||||
// Create tiles at different positions
|
|
||||||
let tiles = positions
|
|
||||||
.iter()
|
|
||||||
.map(|&pos| Tile::new(pos))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Verify each tile has correct position
|
|
||||||
for (tile, &pos) in tiles.iter().zip(positions.iter()) {
|
|
||||||
assert_eq!(tile.pos, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_boundaries() {
|
|
||||||
// Test with extreme coordinate values
|
|
||||||
let extreme_positions = [
|
|
||||||
Hex::new(i32::MAX, i32::MIN),
|
|
||||||
Hex::new(i32::MIN, i32::MAX),
|
|
||||||
Hex::new(0, i32::MAX),
|
|
||||||
Hex::new(i32::MIN, 0),
|
|
||||||
];
|
|
||||||
|
|
||||||
for pos in extreme_positions {
|
|
||||||
let tile = Tile::new(pos);
|
|
||||||
assert_eq!(tile.pos, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_creation_and_properties() {
|
|
||||||
let hex = random_hex();
|
|
||||||
let tile = Tile::new(hex);
|
|
||||||
|
|
||||||
assert_eq!(tile.pos(), hex);
|
|
||||||
assert!(tile.walls().is_enclosed());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_from_hex() {
|
|
||||||
let hex = random_hex();
|
|
||||||
let tile = Tile::from(hex);
|
|
||||||
|
|
||||||
assert_eq!(tile.pos, hex);
|
|
||||||
assert_eq!(tile.walls, Walls::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_hex_into_tile() {
|
|
||||||
let hex = random_hex();
|
|
||||||
let tile: Tile = hex.into();
|
|
||||||
|
|
||||||
assert_eq!(tile.pos, hex);
|
|
||||||
assert_eq!(tile.walls, Walls::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_display() {
|
|
||||||
let tile = Tile::new(Hex::new(3, -3));
|
|
||||||
assert_eq!(format!("{tile}"), "(3,-3)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_wall_modifications() {
|
|
||||||
let mut tile = Tile::new(Hex::ZERO);
|
|
||||||
|
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
tile.walls.insert(direction);
|
|
||||||
}
|
|
||||||
assert_eq!(tile.walls.count(), 6);
|
|
||||||
|
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
tile.walls.remove(direction);
|
|
||||||
}
|
|
||||||
assert_eq!(tile.walls.count(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
mod bevy_tests {
|
|
||||||
use super::*;
|
|
||||||
use glam::{Vec2, Vec3};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_to_vec2() {
|
|
||||||
let layout = HexLayout::default();
|
|
||||||
let tile = Tile::new(Hex::new(1, 0));
|
|
||||||
let vec2 = tile.to_vec2(&layout);
|
|
||||||
assert_eq!(vec2, Vec2::new(1.5, -0.866_025_4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_tile_to_vec3() {
|
|
||||||
let layout = HexLayout::default();
|
|
||||||
let tile = Tile::new(Hex::new(0, 1));
|
|
||||||
let vec3 = tile.to_vec3(&layout);
|
|
||||||
assert_eq!(vec3, Vec3::new(0.0, 0.0, -1.732_050_8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
321
src/walls.rs
321
src/walls.rs
@ -1,5 +1,5 @@
|
|||||||
#[cfg(feature = "bevy")]
|
#[cfg(feature = "bevy")]
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::{Component, Reflect, ReflectComponent};
|
||||||
use hexx::EdgeDirection;
|
use hexx::EdgeDirection;
|
||||||
|
|
||||||
/// A bit-flag representation of walls in a hexagonal tile.
|
/// A bit-flag representation of walls in a hexagonal tile.
|
||||||
@ -11,27 +11,41 @@ use hexx::EdgeDirection;
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Creating and manipulating walls:
|
/// Creating and manipulating walls:
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// // Create a hexagon with all walls
|
/// // Create a hexagon with all walls
|
||||||
/// let walls = Walls::new();
|
/// let walls = Walls::new();
|
||||||
/// assert!(walls.is_enclosed());
|
/// assert!(walls.is_closed());
|
||||||
///
|
///
|
||||||
/// // 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.insert(EdgeDirection::FLAT_NORTH);
|
/// walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
/// walls.insert(EdgeDirection::FLAT_SOUTH);
|
/// walls.add(EdgeDirection::FLAT_SOUTH);
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// Using walls in game logic:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use hexlab::prelude::*;
|
||||||
|
/// let mut walls = Walls::empty();
|
||||||
|
///
|
||||||
|
/// // Add walls to create a corner
|
||||||
|
/// walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
|
/// walls.add(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
|
///
|
||||||
|
/// // Check if a specific direction has a wall
|
||||||
|
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
||||||
|
/// assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
|
||||||
|
/// ```
|
||||||
#[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", derive(Component))]
|
|
||||||
#[cfg_attr(feature = "bevy", reflect(Component))]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
|
||||||
|
#[cfg_attr(feature = "bevy", reflect(Component))]
|
||||||
pub struct Walls(u8);
|
pub struct Walls(u8);
|
||||||
|
|
||||||
impl Walls {
|
impl Walls {
|
||||||
@ -41,11 +55,12 @@ impl Walls {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let walls = Walls::new();
|
/// let walls = Walls::new();
|
||||||
/// assert!(walls.is_enclosed());
|
/// assert!(walls.is_closed());
|
||||||
|
/// assert_eq!(walls.count(), 6);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -57,11 +72,12 @@ impl Walls {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let walls = Walls::empty();
|
/// let walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
|
/// assert_eq!(walls.count(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -69,15 +85,19 @@ impl Walls {
|
|||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the walls are currently empty (no walls present).
|
/// Checks if the walls are currently empty
|
||||||
///
|
///
|
||||||
|
/// Returns `true` if all directions have no walls set.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let walls = Walls::empty();
|
/// let walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
/// assert!(walls.is_empty());
|
||||||
|
///
|
||||||
|
/// let walls = Walls::new();
|
||||||
|
/// assert!(!walls.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -85,118 +105,98 @@ impl Walls {
|
|||||||
self.0 == 0
|
self.0 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a wall in the specified direction.
|
/// Adds a wall in the specified direction
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// This method uses bitwise operations to efficiently set the wall flag
|
||||||
///
|
/// for the given direction. Multiple walls can be added to the same hexagon.
|
||||||
/// - `direction` - The direction in which to insert the wall.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns `true` if a wall was present, `false` otherwise.
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// assert_eq!(walls.count(), 0);
|
/// walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
|
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH));
|
||||||
|
/// assert!(!walls.contains(EdgeDirection::FLAT_SOUTH));
|
||||||
///
|
///
|
||||||
/// assert!(!walls.insert(1));
|
/// walls.add(EdgeDirection::FLAT_SOUTH);
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert!(walls.contains(EdgeDirection::FLAT_SOUTH));
|
||||||
///
|
|
||||||
/// assert!(walls.insert(1));
|
|
||||||
/// assert_eq!(walls.count(), 1);
|
|
||||||
///
|
|
||||||
/// assert!(!walls.insert(EdgeDirection::FLAT_NORTH));
|
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert<T>(&mut self, direction: T) -> bool
|
pub fn add<T>(&mut self, direction: T)
|
||||||
where
|
where
|
||||||
T: Into<Self>,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
let mask = direction.into().0;
|
self.0 |= 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
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Returns `true` if a wall was actually removed, `false` if there was no wall
|
||||||
|
/// in the specified direction.
|
||||||
///
|
///
|
||||||
/// - `direction` - The direction from which to remove the wall.
|
/// # Exmaples
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// ```rust
|
||||||
///
|
|
||||||
/// Returns `true` if a wall was present and removed, `false` otherwise.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::new();
|
/// let mut walls = Walls::new();
|
||||||
///
|
|
||||||
/// assert!(walls.remove(1));
|
|
||||||
/// assert_eq!(walls.count(), 5);
|
|
||||||
///
|
|
||||||
/// assert!(!walls.remove(1));
|
|
||||||
/// assert_eq!(walls.count(), 5);
|
|
||||||
///
|
|
||||||
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
/// assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
/// assert_eq!(walls.count(), 4);
|
/// assert!(!walls.contains(EdgeDirection::FLAT_NORTH));
|
||||||
|
///
|
||||||
|
/// // Removing a non-existent wall returns false
|
||||||
|
/// assert!(!walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
/// ```
|
/// ```
|
||||||
#[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>,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
let mask = direction.into().0;
|
let was_removed = self.contains(direction);
|
||||||
let was_present = self.0 & mask != 0;
|
if was_removed {
|
||||||
self.0 &= !mask;
|
self.0 &= !direction.into().0;
|
||||||
was_present
|
}
|
||||||
|
was_removed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if there is a wall in the specified direction.
|
/// Returns true if there is a wall in the specified direction
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Uses efficient bitwise operations to check for the presence of a wall.
|
||||||
///
|
///
|
||||||
/// - `other` - The direction to check for a wall.
|
/// # Exmaples
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// ```rust
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// walls.insert(EdgeDirection::FLAT_NORTH);
|
|
||||||
///
|
///
|
||||||
|
/// walls.add(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, other: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self>,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
self.0 & direction.into().0 != 0
|
self.0 & other.into().0 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw bit representation of the walls
|
/// Returns the raw bit representation of the walls
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// This method provides access to the underlying bit flags for advanced usage.
|
||||||
|
/// The bits are ordered according to the `EdgeDirection` indices.
|
||||||
///
|
///
|
||||||
/// ```
|
/// # Exmaples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let walls = Walls::new();
|
/// let mut walls = Walls::new();
|
||||||
/// assert_eq!(walls.as_bits(), 0b11_1111);
|
|
||||||
///
|
///
|
||||||
/// let walls = Walls::empty();
|
/// assert_eq!(walls.as_bits(), 0b111111);
|
||||||
/// assert_eq!(walls.as_bits(), 0);
|
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -206,18 +206,19 @@ impl Walls {
|
|||||||
|
|
||||||
/// Returns the total number of walls present
|
/// Returns the total number of walls present
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// Efficiently counts the number of set bits in the internal representation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// # Exmaples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// assert!(walls.is_empty());
|
|
||||||
///
|
///
|
||||||
/// walls.insert(0);
|
/// assert_eq!(walls.count(), 0);
|
||||||
/// assert_eq!(walls.count(), 1);
|
|
||||||
///
|
///
|
||||||
/// walls.insert(1);
|
/// walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
|
/// walls.add(EdgeDirection::FLAT_SOUTH);
|
||||||
/// assert_eq!(walls.count(), 2);
|
/// assert_eq!(walls.count(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -226,14 +227,19 @@ impl Walls {
|
|||||||
u8::try_from(self.0.count_ones()).unwrap_or_default()
|
u8::try_from(self.0.count_ones()).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `Walls` value representing all possible directions.
|
/// Returns all possible directions as a `Walls` value
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// This represents a hexagon with walls in all six directions.
|
||||||
///
|
///
|
||||||
/// ```
|
/// # Exmaples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// assert_eq!(Walls::all_directions().as_bits(), 0b11_1111);
|
/// let all_walls = Walls::all_directions();
|
||||||
|
///
|
||||||
|
/// assert_eq!(all_walls.count(), 6);
|
||||||
|
/// assert!(all_walls.is_closed());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -245,58 +251,59 @@ impl Walls {
|
|||||||
///
|
///
|
||||||
/// If a wall exists in the given direction, it will be removed.
|
/// If a wall exists in the given direction, it will be removed.
|
||||||
/// If no wall exists, one will be added.
|
/// If no wall exists, one will be added.
|
||||||
///
|
/// Returns the previous state (`true` if a wall was present).
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `direction` - The direction in which to toggle the wall.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The previous state (`true` if a wall was present before toggling, `false` otherwise).
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
///
|
///
|
||||||
/// assert!(!walls.toggle(0));
|
/// assert!(!walls.toggle(EdgeDirection::FLAT_NORTH)); // Returns false, wall was not present
|
||||||
/// assert_eq!(walls.count(), 1);
|
/// assert!(walls.contains(EdgeDirection::FLAT_NORTH)); // Wall is now present
|
||||||
///
|
///
|
||||||
/// assert!(walls.toggle(0));
|
/// let mut walls = Walls::new();
|
||||||
/// assert_eq!(walls.count(), 0);
|
///
|
||||||
|
/// assert!(walls.toggle(EdgeDirection::FLAT_NORTH)); // Returns true, wall was present
|
||||||
|
/// assert!(!walls.contains(EdgeDirection::FLAT_NORTH)); // Wall is now removed
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle<T>(&mut self, direction: T) -> bool
|
pub fn toggle<T>(&mut self, direction: T) -> bool
|
||||||
where
|
where
|
||||||
T: Into<Self> + Copy,
|
T: Into<Self> + Copy,
|
||||||
{
|
{
|
||||||
let mask = direction.into().0;
|
let is_present = self.contains(direction);
|
||||||
let was_present = self.0 & mask != 0;
|
if is_present {
|
||||||
self.0 ^= mask;
|
self.remove(direction);
|
||||||
was_present
|
} else {
|
||||||
|
self.add(direction);
|
||||||
|
}
|
||||||
|
is_present
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if walls are present in all six directions.
|
/// Checks if walls are present in all six directions.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// Returns `true` if the hexagon has all possible walls, making it completely enclosed.
|
||||||
///
|
|
||||||
/// `true` if the hexagon has all possible walls, making it completely enclosed.
|
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::new();
|
/// let walls = Walls::new();
|
||||||
/// assert!(walls.is_enclosed());
|
/// assert!(walls.is_closed());
|
||||||
///
|
///
|
||||||
/// walls.remove(0);
|
/// let mut walls = Walls::empty();
|
||||||
/// assert!(!walls.is_enclosed());
|
/// assert!(!walls.is_closed());
|
||||||
|
/// // Add all walls manually
|
||||||
|
/// for direction in EdgeDirection::iter() {
|
||||||
|
/// walls.add(direction);
|
||||||
|
/// }
|
||||||
|
/// assert!(walls.is_closed());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_enclosed(&self) -> bool {
|
pub fn is_closed(&self) -> bool {
|
||||||
self.count() == 6
|
self.count() == 6
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,19 +312,15 @@ impl Walls {
|
|||||||
/// This method efficiently adds multiple walls in a single operation while
|
/// This method efficiently adds multiple walls in a single operation while
|
||||||
/// preserving any existing walls not specified in the input.
|
/// preserving any existing walls not specified in the input.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// - `other` - The walls to insert, specified as a `Walls` instance or any type
|
|
||||||
/// that can be converted into `Walls`.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use hexlab::prelude::*;
|
/// use hexlab::prelude::*;
|
||||||
///
|
///
|
||||||
/// let mut walls = Walls::empty();
|
/// let mut walls = Walls::empty();
|
||||||
/// walls.fill([EdgeDirection::FLAT_NORTH ,EdgeDirection::FLAT_SOUTH, EdgeDirection::FLAT_SOUTH_EAST]);
|
/// walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
|
///
|
||||||
|
/// walls.fill([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);
|
||||||
@ -366,16 +369,16 @@ impl Default for Walls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// all_directions
|
// all_directions
|
||||||
#[test]
|
#[test]
|
||||||
fn all_directions_creates_closed_walls() {
|
fn all_directions_creates_closed_walls() {
|
||||||
let walls = Walls::all_directions();
|
let walls = Walls::all_directions();
|
||||||
assert!(walls.is_enclosed());
|
assert!(walls.is_closed());
|
||||||
assert!(!walls.is_empty());
|
assert!(!walls.is_empty());
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// as_bits
|
// as_bits
|
||||||
@ -388,30 +391,30 @@ 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.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0000);
|
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.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH);
|
walls.add(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn as_bits_all_walls() {
|
fn as_bits_all_walls() {
|
||||||
let walls = Walls::new();
|
let walls = Walls::new();
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// new
|
// new
|
||||||
#[test]
|
#[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_closed());
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty
|
// empty
|
||||||
@ -422,11 +425,11 @@ mod test {
|
|||||||
assert_eq!(walls.as_bits(), 0);
|
assert_eq!(walls.as_bits(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert
|
// add
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_single_wall() {
|
fn add_single_wall() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(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);
|
||||||
}
|
}
|
||||||
@ -443,13 +446,13 @@ mod 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.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
assert!(walls.remove(EdgeDirection::FLAT_NORTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggle
|
// toggle
|
||||||
#[test]
|
#[test]
|
||||||
fn toggle_wall() {
|
fn toggle_adds_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));
|
||||||
@ -475,7 +478,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fill_preserves_existing_walls() {
|
fn fill_preserves_existing_walls() {
|
||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(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));
|
||||||
@ -519,8 +522,8 @@ mod test {
|
|||||||
#[test]
|
#[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_closed());
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -541,54 +544,54 @@ mod test {
|
|||||||
let mut walls = Walls::empty();
|
let mut walls = Walls::empty();
|
||||||
|
|
||||||
// Test single bit operations
|
// Test single bit operations
|
||||||
walls.insert(EdgeDirection::FLAT_NORTH);
|
walls.add(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0000);
|
assert_eq!(walls.as_bits(), 0b010000);
|
||||||
|
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH);
|
walls.add(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
|
|
||||||
// Test removing middle bit
|
// Test removing middle bit
|
||||||
walls.insert(EdgeDirection::FLAT_SOUTH_EAST);
|
walls.add(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0011);
|
assert_eq!(walls.as_bits(), 0b010011);
|
||||||
walls.remove(EdgeDirection::FLAT_SOUTH);
|
walls.remove(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0001);
|
assert_eq!(walls.as_bits(), 0b010001);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From<EdgeDirection> tests
|
// From<EdgeDirection> tests
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south_east() {
|
fn from_edge_direction_flat_south_east() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b00_0001);
|
assert_eq!(walls.as_bits(), 0b000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south() {
|
fn from_edge_direction_flat_south() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH);
|
||||||
assert_eq!(walls.as_bits(), 0b00_0010);
|
assert_eq!(walls.as_bits(), 0b000010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_south_west() {
|
fn from_edge_direction_flat_south_west() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST);
|
let walls = Walls::from(EdgeDirection::FLAT_SOUTH_WEST);
|
||||||
assert_eq!(walls.as_bits(), 0b00_0100);
|
assert_eq!(walls.as_bits(), 0b000100);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_north_west() {
|
fn from_edge_direction_flat_north_west() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH_WEST);
|
||||||
assert_eq!(walls.as_bits(), 0b00_1000);
|
assert_eq!(walls.as_bits(), 0b001000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_north() {
|
fn from_edge_direction_flat_north() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0000);
|
assert_eq!(walls.as_bits(), 0b010000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_edge_direction_flat_east() {
|
fn from_edge_direction_flat_east() {
|
||||||
let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST);
|
let walls = Walls::from(EdgeDirection::FLAT_NORTH_EAST);
|
||||||
assert_eq!(walls.as_bits(), 0b10_0000);
|
assert_eq!(walls.as_bits(), 0b100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromIterator tests
|
// FromIterator tests
|
||||||
@ -603,7 +606,7 @@ mod test {
|
|||||||
let walls = vec![EdgeDirection::FLAT_SOUTH]
|
let walls = vec![EdgeDirection::FLAT_SOUTH]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b00_0010);
|
assert_eq!(walls.as_bits(), 0b000010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -611,7 +614,7 @@ mod test {
|
|||||||
let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]
|
let walls = vec![EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -623,13 +626,13 @@ mod test {
|
|||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Walls>();
|
.collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_iterator_all_directions() {
|
fn from_iterator_all_directions() {
|
||||||
let walls = EdgeDirection::iter().collect::<Walls>();
|
let walls = EdgeDirection::iter().collect::<Walls>();
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From<[EdgeDirection; N]> tests
|
// From<[EdgeDirection; N]> tests
|
||||||
@ -642,13 +645,13 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn from_array_single() {
|
fn from_array_single() {
|
||||||
let walls = Walls::from([EdgeDirection::FLAT_NORTH]);
|
let walls = Walls::from([EdgeDirection::FLAT_NORTH]);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0000);
|
assert_eq!(walls.as_bits(), 0b010000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_array_multiple() {
|
fn from_array_multiple() {
|
||||||
let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
let walls = Walls::from([EdgeDirection::FLAT_NORTH, EdgeDirection::FLAT_SOUTH]);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -658,7 +661,7 @@ mod test {
|
|||||||
EdgeDirection::FLAT_NORTH,
|
EdgeDirection::FLAT_NORTH,
|
||||||
EdgeDirection::FLAT_SOUTH,
|
EdgeDirection::FLAT_SOUTH,
|
||||||
]);
|
]);
|
||||||
assert_eq!(walls.as_bits(), 0b01_0010);
|
assert_eq!(walls.as_bits(), 0b010010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -671,6 +674,6 @@ mod test {
|
|||||||
EdgeDirection::FLAT_SOUTH_WEST,
|
EdgeDirection::FLAT_SOUTH_WEST,
|
||||||
EdgeDirection::FLAT_NORTH_WEST,
|
EdgeDirection::FLAT_NORTH_WEST,
|
||||||
]);
|
]);
|
||||||
assert_eq!(walls.as_bits(), 0b11_1111);
|
assert_eq!(walls.as_bits(), 0b111111);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
tests/builder.rs
124
tests/builder.rs
@ -1,124 +0,0 @@
|
|||||||
use claims::{assert_err, assert_gt, assert_matches, assert_ok, assert_some};
|
|
||||||
use hexlab::prelude::*;
|
|
||||||
use rstest::rstest;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(1, 7)]
|
|
||||||
#[case(2, 19)]
|
|
||||||
#[case(3, 37)]
|
|
||||||
#[case(4, 61)]
|
|
||||||
#[case(5, 91)]
|
|
||||||
fn maze_size(#[case] radius: u16, #[case] expected_size: usize) {
|
|
||||||
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build());
|
|
||||||
assert_eq!(maze.count(), expected_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn builder_without_radius() {
|
|
||||||
let result = MazeBuilder::new().build();
|
|
||||||
assert_err!(&result);
|
|
||||||
assert_matches!(result, Err(MazeBuilderError::NoRadius));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(Hex::ZERO)]
|
|
||||||
#[case(Hex::new(1,-1))]
|
|
||||||
#[case(Hex::new(-2,1))]
|
|
||||||
fn valid_start_position(#[case] start_pos: Hex) {
|
|
||||||
let maze = assert_ok!(MazeBuilder::new()
|
|
||||||
.with_radius(3)
|
|
||||||
.with_start_position(start_pos)
|
|
||||||
.build());
|
|
||||||
assert_some!(maze.get(&start_pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_start_position() {
|
|
||||||
let maze = MazeBuilder::new()
|
|
||||||
.with_radius(3)
|
|
||||||
.with_start_position(Hex::new(10, 10))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
assert_err!(&maze);
|
|
||||||
assert_matches!(maze, Err(MazeBuilderError::InvalidStartPosition(_)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maze_with_seed() {
|
|
||||||
let maze1 = assert_ok!(MazeBuilder::new().with_radius(3).with_seed(12345).build());
|
|
||||||
let maze2 = assert_ok!(MazeBuilder::new().with_radius(3).with_seed(12345).build());
|
|
||||||
|
|
||||||
assert_eq!(maze1, maze2, "Mazes with the same seed should be identical");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn different_seeds_produce_different_mazes() {
|
|
||||||
let maze1 = assert_ok!(MazeBuilder::new().with_radius(3).with_seed(12345).build());
|
|
||||||
let maze2 = assert_ok!(MazeBuilder::new().with_radius(3).with_seed(54321).build());
|
|
||||||
|
|
||||||
assert_ne!(
|
|
||||||
maze1, maze2,
|
|
||||||
"Mazes with different seeds should be different"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maze_connectivity() {
|
|
||||||
let maze = assert_ok!(MazeBuilder::new().with_radius(3).build());
|
|
||||||
|
|
||||||
// Helper function to count accessible neighbors
|
|
||||||
let count_accessible_neighbors = |pos: Hex| -> usize {
|
|
||||||
hexx::EdgeDirection::ALL_DIRECTIONS
|
|
||||||
.iter()
|
|
||||||
.filter(|&&dir| {
|
|
||||||
let neighbor = pos + dir;
|
|
||||||
maze.get_walls(&pos)
|
|
||||||
.is_some_and(|walls| !walls.contains(dir) && maze.get(&neighbor).is_some())
|
|
||||||
})
|
|
||||||
.count()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check that each tile has at least one connection
|
|
||||||
for &pos in maze.keys() {
|
|
||||||
let accessible_neighbors = count_accessible_neighbors(pos);
|
|
||||||
assert_gt!(
|
|
||||||
accessible_neighbors,
|
|
||||||
0,
|
|
||||||
"Tile at {pos:?} has no accessible neighbors",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn maze_boundaries() {
|
|
||||||
let radius = 3;
|
|
||||||
let maze = assert_ok!(MazeBuilder::new().with_radius(radius).build());
|
|
||||||
|
|
||||||
let radius = i32::from(radius);
|
|
||||||
|
|
||||||
// Test that tiles exist within the radius
|
|
||||||
for q in -radius..=radius {
|
|
||||||
for r in -radius..=radius {
|
|
||||||
let pos = Hex::new(q, r);
|
|
||||||
if q.abs() + r.abs() <= radius {
|
|
||||||
assert!(
|
|
||||||
maze.get(&pos).is_some(),
|
|
||||||
"Expected tile at {pos:?} to exist",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(GeneratorType::RecursiveBacktracking)]
|
|
||||||
fn generate_maze_with_different_types(#[case] generator: GeneratorType) {
|
|
||||||
// TODO: Add more generator types when they become available
|
|
||||||
|
|
||||||
let maze = assert_ok!(MazeBuilder::new()
|
|
||||||
.with_radius(3)
|
|
||||||
.with_generator(generator)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
assert_gt!(maze.count(), 0);
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
use claims::assert_some;
|
|
||||||
use hexlab::prelude::*;
|
|
||||||
use rstest::rstest;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case(GeneratorType::RecursiveBacktracking, None, None)]
|
|
||||||
#[case(GeneratorType::RecursiveBacktracking, Some(Hex::new(1, -1)), None)]
|
|
||||||
#[case(GeneratorType::RecursiveBacktracking, None, Some(12345))]
|
|
||||||
fn generator_type(
|
|
||||||
#[case] generator: GeneratorType,
|
|
||||||
#[case] start_pos: Option<Hex>,
|
|
||||||
#[case] seed: Option<u64>,
|
|
||||||
) {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
for q in -3..=3 {
|
|
||||||
for r in -3..=3 {
|
|
||||||
let hex = Hex::new(q, r);
|
|
||||||
if hex.length() <= 3 {
|
|
||||||
maze.insert(hex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let initial_size = maze.count();
|
|
||||||
|
|
||||||
generator.generate(&mut maze, start_pos, seed);
|
|
||||||
|
|
||||||
assert_eq!(maze.count(), initial_size, "Maze size should not change");
|
|
||||||
|
|
||||||
// Check maze connectivity
|
|
||||||
let start = start_pos.unwrap_or(Hex::ZERO);
|
|
||||||
let mut to_visit = vec![start];
|
|
||||||
let mut visited = std::collections::HashSet::new();
|
|
||||||
while let Some(current) = to_visit.pop() {
|
|
||||||
if !visited.insert(current) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for dir in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
let neighbor = current + dir;
|
|
||||||
if let Some(walls) = maze.get_walls(¤t) {
|
|
||||||
if !walls.contains(dir) && maze.get(&neighbor).is_some() {
|
|
||||||
to_visit.push(neighbor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(visited.len(), maze.count(), "All tiles should be connected");
|
|
||||||
|
|
||||||
// Check that each tile has at least one open wall
|
|
||||||
for &pos in maze.keys() {
|
|
||||||
let walls = assert_some!(maze.get_walls(&pos));
|
|
||||||
assert!(
|
|
||||||
walls.count() < 6,
|
|
||||||
"Tile at {pos:?} should have at least one open wall",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty_maze() {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
GeneratorType::RecursiveBacktracking.generate(&mut maze, None, None);
|
|
||||||
assert!(
|
|
||||||
maze.is_empty(),
|
|
||||||
"Empty maze should remain empty after generation"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
use claims::assert_some;
|
|
||||||
use hexlab::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_maze_creation_and_basic_operations() {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
assert!(maze.is_empty());
|
|
||||||
|
|
||||||
let center = Hex::ZERO;
|
|
||||||
maze.insert(center);
|
|
||||||
assert_eq!(maze.count(), 1);
|
|
||||||
assert!(!maze.is_empty());
|
|
||||||
|
|
||||||
let tile = assert_some!(maze.get(¢er));
|
|
||||||
assert_eq!(tile.pos(), center);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_maze_wall_operations() {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
let center = Hex::ZERO;
|
|
||||||
maze.insert(center);
|
|
||||||
|
|
||||||
// Add walls
|
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
let _ = maze.add_tile_wall(¢er, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
let walls = assert_some!(maze.get_walls(¢er));
|
|
||||||
assert_eq!(walls.count(), 6);
|
|
||||||
|
|
||||||
// Remove walls
|
|
||||||
for direction in EdgeDirection::ALL_DIRECTIONS {
|
|
||||||
let _ = maze.remove_tile_wall(¢er, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
let walls = assert_some!(maze.get_walls(¢er));
|
|
||||||
assert_eq!(walls.count(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_maze_multiple_tiles() {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
let tiles = [Hex::ZERO, Hex::new(1, -1), Hex::new(0, 1), Hex::new(-1, 1)];
|
|
||||||
|
|
||||||
for &tile in &tiles {
|
|
||||||
maze.insert(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(maze.count(), tiles.len());
|
|
||||||
|
|
||||||
for &tile in &tiles {
|
|
||||||
assert!(maze.get(&tile).is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hex_maze_edge_cases() {
|
|
||||||
let mut maze = Maze::new();
|
|
||||||
let non_existent = Hex::new(10, 10);
|
|
||||||
|
|
||||||
// Operations on non-existent tiles should not panic
|
|
||||||
let _ = maze.add_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
|
|
||||||
let _ = maze.remove_tile_wall(&non_existent, EdgeDirection::FLAT_NORTH);
|
|
||||||
|
|
||||||
assert!(maze.get(&non_existent).is_none());
|
|
||||||
assert!(maze.get_walls(&non_existent).is_none());
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
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