Compare commits
16 Commits
b5f6bcde11
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
c35270a6a2
|
|||
|
845138dd2d
|
|||
|
fd6b0c846b
|
|||
|
004591374c
|
|||
|
d0c14d004f
|
|||
|
8cb33180ad
|
|||
|
19426d1dc8
|
|||
|
292638ea1d
|
|||
|
ef4de807d4
|
|||
|
866544d416
|
|||
|
0a5e5c8798
|
|||
|
73b44b1377
|
|||
|
72d7dc1cbb
|
|||
|
33275b3918
|
|||
|
3f9f7afd27
|
|||
|
522e10a3ea
|
BIN
.aoc_tiles/tiles/2025/09.png
Normal file
BIN
.aoc_tiles/tiles/2025/09.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
BIN
.aoc_tiles/tiles/2025/10.png
Normal file
BIN
.aoc_tiles/tiles/2025/10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
.aoc_tiles/tiles/2025/11.png
Normal file
BIN
.aoc_tiles/tiles/2025/11.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
.aoc_tiles/tiles/2025/12.png
Normal file
BIN
.aoc_tiles/tiles/2025/12.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
697
Cargo.lock
generated
697
Cargo.lock
generated
@@ -8,7 +8,7 @@ version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.4",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@@ -66,23 +66,52 @@ dependencies = [
|
||||
"aoc-runner-derive",
|
||||
"cached",
|
||||
"grid",
|
||||
"indicatif",
|
||||
"itertools",
|
||||
"misc",
|
||||
"rayon",
|
||||
"regex",
|
||||
"rstest",
|
||||
"rustc_data_structures",
|
||||
"wide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cached"
|
||||
version = "0.56.0"
|
||||
@@ -92,7 +121,7 @@ dependencies = [
|
||||
"ahash",
|
||||
"cached_proc_macro",
|
||||
"cached_proc_macro_types",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.5",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"web-time",
|
||||
@@ -116,12 +145,56 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
@@ -163,12 +236,43 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -181,10 +285,77 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[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 2.0.111",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
@@ -196,12 +367,59 @@ dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
"rustc-rayon 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.18.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
|
||||
dependencies = [
|
||||
"console",
|
||||
"portable-atomic",
|
||||
"rayon",
|
||||
"unicode-width",
|
||||
"unit-prefix",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "intrusive-collections"
|
||||
version = "0.9.7"
|
||||
@@ -226,6 +444,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.83"
|
||||
@@ -236,12 +464,54 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
@@ -268,12 +538,72 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
|
||||
dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
@@ -292,6 +622,41 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
@@ -321,6 +686,136 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
|
||||
dependencies = [
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"rstest_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"glob",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn 2.0.111",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-rayon"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rustc-rayon-core 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-rayon"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cd9fb077db982d7ceb42a90471e5a69a990b58f71e06f0d8340bb2cf35eb751"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rustc-rayon-core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-rayon-core"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-rayon-core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_data_structures"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38bae9c6afa27015bcaa2869e03bb111ecf0d0e0edc2da559a91d4057174c9a"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 0.1.10",
|
||||
"ena",
|
||||
"indexmap 1.9.3",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"parking_lot",
|
||||
"rustc-hash",
|
||||
"rustc-rayon 0.3.2",
|
||||
"rustc-rayon-core 0.3.2",
|
||||
"stable_deref_trait",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
@@ -333,6 +828,27 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f7caad094bd561859bcd467734a720c3c1f5d1f338995351fefe2190c45efed"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -375,6 +891,24 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -403,6 +937,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
@@ -423,25 +970,107 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.23.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
|
||||
dependencies = [
|
||||
"indexmap 2.12.1",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unit-prefix"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.4",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -490,6 +1119,68 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wide"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbace5de6cfc4866f684318ad85761c89380cfb191982ae96aa65c295bf5897e"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"safe_arch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.31"
|
||||
|
||||
@@ -8,9 +8,14 @@ aoc-runner = "0.3.0"
|
||||
aoc-runner-derive = "0.3.0"
|
||||
cached = "0.56.0"
|
||||
grid = {version = "0.1.0", path = "utils/grid"}
|
||||
indicatif = { version = "0.18.3", features = ["rayon"] }
|
||||
itertools = "0.14.0"
|
||||
misc = {path = "utils/misc"}
|
||||
rayon = "1.11.0"
|
||||
regex = "1.11.1"
|
||||
rstest = "0.26.1"
|
||||
rustc_data_structures = "0.1.2"
|
||||
wide = "1.0.2"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
14
README.md
14
README.md
@@ -1,6 +1,6 @@
|
||||
<!-- AOC TILES BEGIN -->
|
||||
<h1 align="center">
|
||||
2025 - 16 ⭐ - Rust
|
||||
2025 - 21 ⭐ - Rust
|
||||
</h1>
|
||||
<a href="src/day1.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
|
||||
@@ -26,4 +26,16 @@
|
||||
<a href="src/day8.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/08.png" width="161px">
|
||||
</a>
|
||||
<a href="src/day9.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/09.png" width="161px">
|
||||
</a>
|
||||
<a href="src/day10.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/10.png" width="161px">
|
||||
</a>
|
||||
<a href="src/day11.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/11.png" width="161px">
|
||||
</a>
|
||||
<a href="src/day12.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/12.png" width="161px">
|
||||
</a>
|
||||
<!-- AOC TILES END -->
|
||||
|
||||
331
src/day10.rs
Normal file
331
src/day10.rs
Normal file
@@ -0,0 +1,331 @@
|
||||
use std::{
|
||||
collections::{BinaryHeap, VecDeque},
|
||||
hash::Hash,
|
||||
iter::{repeat, repeat_n},
|
||||
};
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
use wide::{CmpGt, i16x16};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct MachineDefinition {
|
||||
desired: Vec<bool>,
|
||||
buttons: Vec<Vec<usize>>,
|
||||
buttons2: Vec<i16x16>,
|
||||
buttons_max: i16x16,
|
||||
joltages: i16x16,
|
||||
}
|
||||
|
||||
impl MachineDefinition {
|
||||
fn create<'a>(&'a self) -> Machine<'a> {
|
||||
Machine {
|
||||
d: self,
|
||||
lights: Vec::from_iter(repeat_n(false, self.desired.len())),
|
||||
}
|
||||
}
|
||||
|
||||
fn create2<'a>(&'a self) -> JoltMachine<'a> {
|
||||
JoltMachine {
|
||||
d: self,
|
||||
joltages: i16x16::splat(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MachineDefinition {
|
||||
fn from(value: &str) -> Self {
|
||||
let parse_re = Regex::new(
|
||||
r##"\[(?<desired>[.#]+)\] (?<buttons>(\([0-9,]+\) ?)+) \{(?<joltages>[0-9,]+)\}"##,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let parts = parse_re.captures(value).unwrap();
|
||||
let joltages: [i16; 16] = parts["joltages"]
|
||||
.split(',')
|
||||
.map(|n| n.parse().unwrap())
|
||||
.chain(repeat(0))
|
||||
.take(16)
|
||||
.collect_array()
|
||||
.unwrap();
|
||||
|
||||
let buttons = parts["buttons"]
|
||||
.split_ascii_whitespace()
|
||||
.map(|s| {
|
||||
s[1..s.len() - 1]
|
||||
.split(',')
|
||||
.map(|n| n.parse().unwrap())
|
||||
.collect()
|
||||
})
|
||||
.sorted_unstable_by_key(|s: &Vec<usize>| s.len())
|
||||
.rev()
|
||||
.collect_vec();
|
||||
|
||||
let mut buttons2 = Vec::new();
|
||||
let mut buttons_max = [0i16; 16];
|
||||
|
||||
for (i, b) in buttons.iter().enumerate() {
|
||||
let mut but = [0i16; 16];
|
||||
for i in b {
|
||||
but[*i] = 1;
|
||||
}
|
||||
buttons2.push(i16x16::new(but));
|
||||
|
||||
// find the joltage this button affects with the lowest value
|
||||
// it is the max number of presses for this button
|
||||
buttons_max[i] = b.iter().map(|idx| joltages[*idx]).min().unwrap();
|
||||
}
|
||||
|
||||
MachineDefinition {
|
||||
desired: parts["desired"].chars().map(|c| c == '#').collect(),
|
||||
buttons: buttons,
|
||||
buttons2: buttons2,
|
||||
buttons_max: i16x16::new(buttons_max),
|
||||
joltages: i16x16::new(joltages),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Machine<'a> {
|
||||
d: &'a MachineDefinition,
|
||||
lights: Vec<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct JoltMachine<'a> {
|
||||
d: &'a MachineDefinition,
|
||||
joltages: i16x16,
|
||||
}
|
||||
|
||||
impl<'a> Machine<'a> {
|
||||
/// Get the state after pressing `button`, returns None if the state is as desired.
|
||||
fn press(&self, button: usize) -> Option<Self> {
|
||||
let mut new_state = self.lights.clone();
|
||||
for light in &self.d.buttons[button] {
|
||||
new_state[*light] = !new_state[*light];
|
||||
}
|
||||
if new_state == self.d.desired {
|
||||
None
|
||||
} else {
|
||||
Some(Machine {
|
||||
d: self.d,
|
||||
lights: new_state,
|
||||
})
|
||||
}
|
||||
}
|
||||
/// Get the possible states from the current position
|
||||
fn next_states(&self) -> Vec<(usize, Option<Self>)> {
|
||||
self.d
|
||||
.buttons
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _but)| (i, self.press(i)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> JoltMachine<'a> {
|
||||
fn press_jolts(&self, button: usize, presses: &i16x16) -> (i16x16, Option<Self>) {
|
||||
// let mut new_joltage = self.joltages.clone();
|
||||
// // for jolt in &self.d.buttons[button] {
|
||||
// // new_joltage[*jolt] += 1;
|
||||
// // }
|
||||
let new_joltage = self.joltages + self.d.buttons2[button];
|
||||
let mut new_presses = presses.clone();
|
||||
new_presses.as_mut_array()[button] += 1;
|
||||
if new_joltage == self.d.joltages {
|
||||
(new_presses, None)
|
||||
} else {
|
||||
(
|
||||
new_presses,
|
||||
Some(Self {
|
||||
d: self.d,
|
||||
joltages: new_joltage,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_states_jolt(&self, presses: &i16x16) -> Vec<(i16x16, Option<Self>)> {
|
||||
self.d
|
||||
.buttons
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _but)| self.press_jolts(i, &presses))
|
||||
// .inspect(|(p, o)| println!(" {p:?} {o:?}\n"))
|
||||
// joltages monotonically increase, so cull any where a joltage is higher than needed
|
||||
.filter(|(presses, candidate)| {
|
||||
!presses.simd_gt(self.d.buttons_max).any()
|
||||
&& candidate.as_ref().is_none_or(|c| {
|
||||
!c.joltages.simd_gt(self.d.joltages).any()
|
||||
// !c.joltages
|
||||
// .iter()
|
||||
// .zip(self.d.joltages.iter())
|
||||
// .any(|(candidate, expected)| candidate > expected)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PressSet<'a> {
|
||||
machine: Machine<'a>,
|
||||
presses: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PressSet2<'a> {
|
||||
machine: JoltMachine<'a>,
|
||||
presses: i16x16,
|
||||
}
|
||||
|
||||
// NOTE: All compares are reversed so our max heap becomes a min heap
|
||||
impl<'a> Eq for PressSet<'a> {}
|
||||
impl<'a> Eq for PressSet2<'a> {}
|
||||
|
||||
impl<'a> PartialEq for PressSet<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
other.presses.eq(&self.presses)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for PressSet2<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
other.presses.eq(&self.presses)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd for PressSet<'a> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd for PressSet2<'a> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Ord for PressSet<'a> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
other.presses.cmp(&self.presses)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Ord for PressSet2<'a> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
other.presses.reduce_add().cmp(&self.presses.reduce_add())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_best(md: &MachineDefinition) -> usize {
|
||||
let m = md.create();
|
||||
let mut to_check = BinaryHeap::new();
|
||||
to_check.push(PressSet {
|
||||
presses: 0,
|
||||
machine: m,
|
||||
});
|
||||
|
||||
while let Some(candidate) = to_check.pop() {
|
||||
let cm = candidate.machine.clone();
|
||||
for next in cm.next_states() {
|
||||
let presses = candidate.presses + 1;
|
||||
if let Some(new_m) = next.1 {
|
||||
to_check.push(PressSet {
|
||||
presses,
|
||||
machine: new_m.clone(),
|
||||
})
|
||||
} else {
|
||||
return presses;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn find_best_jolts(md: &MachineDefinition) -> usize {
|
||||
let m = md.create2();
|
||||
let mut to_check = VecDeque::new();
|
||||
to_check.push_back(PressSet2 {
|
||||
presses: i16x16::splat(0),
|
||||
machine: m,
|
||||
});
|
||||
|
||||
let mut pb = ProgressBar::no_length()
|
||||
.with_style(
|
||||
ProgressStyle::with_template(
|
||||
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.with_finish(indicatif::ProgressFinish::AndLeave);
|
||||
|
||||
while let Some(candidate) = to_check.pop_front() {
|
||||
pb.inc(1);
|
||||
pb.set_length(to_check.len() as u64);
|
||||
let cm = candidate.machine.clone();
|
||||
for (presses, next) in cm.next_states_jolt(&candidate.presses) {
|
||||
if let Some(new_m) = next {
|
||||
to_check.push_back(PressSet2 {
|
||||
presses,
|
||||
machine: new_m.clone(),
|
||||
})
|
||||
} else {
|
||||
return presses.reduce_add() as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[aoc_generator(day10)]
|
||||
fn parse(input: &str) -> Vec<MachineDefinition> {
|
||||
input.lines().map(|l| l.into()).collect()
|
||||
}
|
||||
|
||||
#[aoc(day10, part1)]
|
||||
fn part1(input: &[MachineDefinition]) -> u64 {
|
||||
input
|
||||
.iter()
|
||||
.map(find_best)
|
||||
// .inspect(|sol| println!(" [{sol:?}]"))
|
||||
.map(|sol| sol as u64)
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day10, part2)]
|
||||
fn part2(input: &[MachineDefinition]) -> u64 {
|
||||
input
|
||||
.iter()
|
||||
.map(find_best_jolts)
|
||||
.map(|sol| sol as u64)
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
const EXAMPLE: &str = "[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
|
||||
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
|
||||
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}";
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 7)]
|
||||
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part1(&parse(input)), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 33)]
|
||||
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part2(&parse(input)), expected);
|
||||
}
|
||||
}
|
||||
203
src/day11.rs
Normal file
203
src/day11.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
|
||||
type Edges = FxHashMap<String, Vec<String>>;
|
||||
|
||||
#[aoc_generator(day11)]
|
||||
fn parse(input: &str) -> Edges {
|
||||
let mut edges: Edges = FxHashMap::default();
|
||||
for l in input.lines() {
|
||||
let (k, rest) = l.split_once(": ").unwrap();
|
||||
for v in rest.split_ascii_whitespace() {
|
||||
edges.entry(k.to_string()).or_default().push(v.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
edges
|
||||
}
|
||||
|
||||
fn find_path(cur: &str, goal: &str, edges: &Edges) -> u64 {
|
||||
if cur == goal {
|
||||
return 1;
|
||||
}
|
||||
if let Some(nexts) = edges.get(cur) {
|
||||
nexts.iter().map(|n| find_path(n, goal, edges)).sum()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_paths<'a>(
|
||||
cur: &'a str,
|
||||
edges: &'a Edges,
|
||||
mut reachable: FxHashSet<&'a str>,
|
||||
) -> FxHashSet<&'a str> {
|
||||
reachable.insert(cur);
|
||||
if let Some(nexts) = edges.get(cur) {
|
||||
for n in nexts {
|
||||
if !reachable.contains(&n.as_str()) {
|
||||
reachable = mark_paths(n, edges, reachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
reachable
|
||||
}
|
||||
|
||||
fn count_paths_impl<'a>(
|
||||
cur: &'a str,
|
||||
goal: &str,
|
||||
edges: &'a Edges,
|
||||
memo: &mut FxHashMap<&'a str, u64>,
|
||||
) -> u64 {
|
||||
if cur == goal {
|
||||
return 1;
|
||||
}
|
||||
if let Some(v) = memo.get(cur) {
|
||||
return *v;
|
||||
}
|
||||
|
||||
if let Some(nexts) = edges.get(cur) {
|
||||
let ret = nexts
|
||||
.iter()
|
||||
.map(|n| count_paths_impl(n, goal, edges, memo))
|
||||
.sum();
|
||||
memo.insert(cur, ret);
|
||||
ret
|
||||
} else {
|
||||
memo.insert(cur, 0);
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn count_paths(cur: &str, goal: &str, edges: &Edges) -> u64 {
|
||||
let mut cache = FxHashMap::default();
|
||||
count_paths_impl(cur, goal, edges, &mut cache)
|
||||
}
|
||||
|
||||
#[aoc(day11, part1, NaiveDfs)]
|
||||
fn part1(fwd: &Edges) -> u64 {
|
||||
find_path("you", "out", fwd)
|
||||
}
|
||||
|
||||
#[aoc(day11, part1, MemoDfs)]
|
||||
fn part1_memo(fwd: &Edges) -> u64 {
|
||||
count_paths("you", "out", fwd)
|
||||
}
|
||||
|
||||
#[aoc(day11, part2, MemoDfs)]
|
||||
fn part2_memo(edges: &Edges) -> u64 {
|
||||
// we assume all valid paths reach svr->fft->dac->out in that order. this holds on the example and the input.
|
||||
// to handle both possible orderings, we need to sum paths fft->dac and dac->fft, and then partition the graph
|
||||
// (just remove the node) at dac & fft before computing the sum of dac->out and fft->out.
|
||||
//
|
||||
// this is our performance implementation though and it's faster to not do that work. we do the posterity for the
|
||||
// naive solution
|
||||
count_paths("svr", "fft", edges)
|
||||
* count_paths("fft", "dac", edges)
|
||||
* count_paths("dac", "out", edges)
|
||||
}
|
||||
|
||||
#[aoc(day11, part2, NaiveDfs)]
|
||||
fn part2(edges: &Edges) -> u64 {
|
||||
let mut rev = Edges::from_iter(edges.keys().map(|k| (k.to_owned(), vec![])));
|
||||
for (from, tos) in edges {
|
||||
for to in tos {
|
||||
rev.entry(to.to_owned()).or_default().push(from.to_owned());
|
||||
}
|
||||
}
|
||||
let mut reachable_dac = mark_paths("dac", edges, FxHashSet::default());
|
||||
reachable_dac = mark_paths("dac", &rev, reachable_dac);
|
||||
let mut reachable_fft = mark_paths("fft", edges, FxHashSet::default());
|
||||
reachable_fft = mark_paths("fft", &rev, reachable_fft);
|
||||
|
||||
let reachable: FxHashSet<&str> = reachable_dac
|
||||
.intersection(&reachable_fft)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let unreachable: FxHashSet<&str> = FxHashSet::from_iter(edges.keys().map(|k| k.as_str()))
|
||||
.difference(&reachable)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let mut reachable_edges = edges.clone();
|
||||
|
||||
for k in &unreachable {
|
||||
reachable_edges.remove(*k);
|
||||
}
|
||||
for (_k, v) in reachable_edges.iter_mut() {
|
||||
for ur in &unreachable {
|
||||
if let Some(idx) = v.iter().position(|s| s == ur) {
|
||||
v.remove(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A real graph structure would probably notice that the graphs get split here
|
||||
let mut reachable_edges_excl_fft = reachable_edges.clone();
|
||||
reachable_edges_excl_fft.remove("fft");
|
||||
let mut reachable_edges_excl_dac = reachable_edges.clone();
|
||||
reachable_edges_excl_dac.remove("dac");
|
||||
|
||||
find_path("svr", "fft", &reachable_edges)
|
||||
// only svr->fft->dac->out paths appear in my input and in the example, but include the dac->fft ordering
|
||||
// as well, for posterity. It ~doubles runtime.
|
||||
* (find_path("fft", "dac", &reachable_edges) + find_path("dac", "fft", &reachable_edges))
|
||||
* (find_path("dac", "out", &reachable_edges_excl_fft) + find_path("fft", "out", &reachable_edges_excl_dac))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
const EXAMPLE: &str = "aaa: you hhh
|
||||
you: bbb ccc
|
||||
bbb: ddd eee
|
||||
ccc: ddd eee fff
|
||||
ddd: ggg
|
||||
eee: out
|
||||
fff: out
|
||||
ggg: out
|
||||
hhh: ccc fff iii
|
||||
iii: out";
|
||||
|
||||
const EXAMPLE2: &str = "svr: aaa bbb
|
||||
aaa: fft
|
||||
fft: ccc
|
||||
bbb: tty
|
||||
tty: ccc
|
||||
ccc: ddd eee
|
||||
ddd: hub
|
||||
hub: fff
|
||||
eee: dac
|
||||
dac: fff
|
||||
fff: ggg hhh
|
||||
ggg: out
|
||||
hhh: out";
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 5)]
|
||||
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part1(&parse(input)), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 5)]
|
||||
fn part1_memo_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part1_memo(&parse(input)), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE2, 2)]
|
||||
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part2(&parse(input)), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE2, 2)]
|
||||
fn part2_memo_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part2_memo(&parse(input)), expected);
|
||||
}
|
||||
}
|
||||
249
src/day12.rs
Normal file
249
src/day12.rs
Normal file
@@ -0,0 +1,249 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use grid::{Coord2d, Grid, MirrorAxis};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
use std::iter::repeat_n;
|
||||
|
||||
type CellType = bool;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PresentShape {
|
||||
variants: Vec<Grid<CellType>>,
|
||||
}
|
||||
|
||||
impl From<&str> for PresentShape {
|
||||
fn from(value: &str) -> Self {
|
||||
let (_idx, rest) = value.split_once(":\n").unwrap();
|
||||
|
||||
let shape_raw: Grid<u8> = rest.parse().unwrap();
|
||||
let mut shape: Grid<CellType> = shape_raw.same_shape(false);
|
||||
for pos in shape_raw.find_all(&b'#') {
|
||||
shape.set(&pos, true);
|
||||
}
|
||||
|
||||
Self {
|
||||
variants: (0..4)
|
||||
.map(|rot| shape.rotated(rot))
|
||||
.chain([MirrorAxis::X, MirrorAxis::Y].map(|axis| shape.mirrored(axis)))
|
||||
.unique()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PresentShape {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}", self.variants[0]))
|
||||
}
|
||||
}
|
||||
|
||||
impl PresentShape {
|
||||
fn popcount(&self) -> u64 {
|
||||
self.variants[0].count(&true) as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Present {
|
||||
shape: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ChristmasTree {
|
||||
area: Grid<CellType>,
|
||||
presents: Vec<Present>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PresentsProblem {
|
||||
trees: Vec<ChristmasTree>,
|
||||
shapes: Vec<PresentShape>,
|
||||
}
|
||||
|
||||
#[aoc_generator(day12)]
|
||||
fn parse(input: &str) -> PresentsProblem {
|
||||
let mut parts = input.split("\n\n").collect_vec();
|
||||
let trees_s = parts.pop().unwrap();
|
||||
|
||||
let shapes: Vec<PresentShape> = parts.into_iter().map(|p| p.into()).collect();
|
||||
let mut trees = Vec::new();
|
||||
|
||||
for l in trees_s.lines() {
|
||||
let (d, el) = l.split_once(": ").unwrap();
|
||||
let (w, h) = d.split_once('x').unwrap();
|
||||
let present_counts: Vec<usize> = el
|
||||
.split_ascii_whitespace()
|
||||
.map(|count| count.parse().unwrap())
|
||||
.collect_vec();
|
||||
let presents = present_counts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, count)| repeat_n(Present { shape: i }, *count))
|
||||
.collect();
|
||||
trees.push(ChristmasTree {
|
||||
area: Grid::with_shape(w.parse().unwrap(), h.parse().unwrap(), false),
|
||||
presents,
|
||||
});
|
||||
}
|
||||
|
||||
PresentsProblem { trees, shapes }
|
||||
}
|
||||
|
||||
// place b on a and test if any # overlap any non-.
|
||||
// fn overlaps(a: &Grid<CellType>, b: &Grid<CellType>, p: &Coord2d) -> bool {
|
||||
// b.find_all(&true)
|
||||
// .any(|c| a.get(&(c + p)).is_some_and(|c| *c))
|
||||
// }
|
||||
|
||||
impl PresentsProblem {
|
||||
fn place_shape(
|
||||
&self,
|
||||
mut t: ChristmasTree,
|
||||
idx: usize,
|
||||
pos: Coord2d,
|
||||
variant: usize,
|
||||
) -> Option<ChristmasTree> {
|
||||
let variant = &self.shapes[t.presents[idx].shape].variants[variant];
|
||||
|
||||
for xy in variant.find_all(&true) {
|
||||
if let Some(was) = t.area.set(&(pos + &(xy)), true)
|
||||
&& was
|
||||
{
|
||||
// overlapped
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(t)
|
||||
}
|
||||
|
||||
fn place_next(
|
||||
&self,
|
||||
t: ChristmasTree,
|
||||
cur: usize,
|
||||
cache: &mut HashSet<(usize, Grid<CellType>)>, // impossible shapes
|
||||
) -> Option<ChristmasTree> {
|
||||
if cur == t.presents.len() {
|
||||
// done
|
||||
return Some(t);
|
||||
}
|
||||
if cache.contains(&(cur, t.area.clone())) {
|
||||
// doomed
|
||||
return None;
|
||||
}
|
||||
let variants = &self.shapes[t.presents[cur].shape].variants;
|
||||
for (i, _v) in variants.iter().enumerate() {
|
||||
for y in 0..&t.area.height() - 2 {
|
||||
for x in 0..&t.area.width() - 2 {
|
||||
if let Some(new_t) = self.place_shape(
|
||||
t.clone(),
|
||||
cur,
|
||||
Coord2d {
|
||||
x: x as i64,
|
||||
y: y as i64,
|
||||
},
|
||||
i,
|
||||
) {
|
||||
if let Some(solution) = self.place_next(new_t, cur + 1, cache) {
|
||||
return Some(solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cache.insert((cur, t.area.clone()));
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc(day12, part1)]
|
||||
fn part1(input: &PresentsProblem) -> u64 {
|
||||
let input = input.clone();
|
||||
let mut count = 0;
|
||||
|
||||
for t in input.trees.iter() {
|
||||
let area = (t.area.width() * t.area.height()) as u64;
|
||||
let (occupied_area, present_count) = t
|
||||
.presents
|
||||
.iter()
|
||||
.map(|p| (input.shapes[p.shape].popcount(), 1))
|
||||
.reduce(|(o_a, pc_a), (o_b, pc_b)| (o_a + o_b, pc_a + pc_b))
|
||||
.unwrap();
|
||||
if occupied_area > area {
|
||||
// definitely impossible
|
||||
continue;
|
||||
}
|
||||
let available_3x3s = (t.area.width() / 3) * (t.area.height() / 3);
|
||||
if available_3x3s >= present_count {
|
||||
// definitely_possible
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
if input
|
||||
.place_next(t.clone(), 0, &mut HashSet::new())
|
||||
.is_some()
|
||||
{
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
#[aoc(day12, part2)]
|
||||
fn part2(_input: &PresentsProblem) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
const EXAMPLE: &str = "0:
|
||||
###
|
||||
##.
|
||||
##.
|
||||
|
||||
1:
|
||||
###
|
||||
##.
|
||||
.##
|
||||
|
||||
2:
|
||||
.##
|
||||
###
|
||||
##.
|
||||
|
||||
3:
|
||||
##.
|
||||
###
|
||||
##.
|
||||
|
||||
4:
|
||||
###
|
||||
#..
|
||||
###
|
||||
|
||||
5:
|
||||
###
|
||||
.#.
|
||||
###
|
||||
|
||||
4x4: 0 0 0 0 2 0
|
||||
12x5: 1 0 1 0 2 2
|
||||
12x5: 1 0 1 0 3 2";
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 2)]
|
||||
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part1(&parse(input)), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(EXAMPLE, 0)]
|
||||
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
||||
assert_eq!(part2(&parse(input)), expected);
|
||||
}
|
||||
}
|
||||
94
src/day8.rs
94
src/day8.rs
@@ -1,4 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::{cmp::Reverse, collections::BinaryHeap, fmt::Display};
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use itertools::Itertools;
|
||||
@@ -9,13 +9,13 @@ struct Junction {
|
||||
}
|
||||
|
||||
fn squared_distance(a: &Junction, b: &Junction) -> u64 {
|
||||
if a.pos == b.pos {
|
||||
0
|
||||
} else {
|
||||
(a.pos.0 - b.pos.0).pow(2) as u64
|
||||
+ (a.pos.1 - b.pos.1).pow(2) as u64
|
||||
+ (a.pos.2 - b.pos.2).pow(2) as u64
|
||||
}
|
||||
// if a.pos == b.pos {
|
||||
// 0
|
||||
// } else {
|
||||
(a.pos.0 - b.pos.0).pow(2) as u64
|
||||
+ (a.pos.1 - b.pos.1).pow(2) as u64
|
||||
+ (a.pos.2 - b.pos.2).pow(2) as u64
|
||||
// }
|
||||
}
|
||||
|
||||
impl Junction {
|
||||
@@ -112,12 +112,12 @@ fn part1_impl(input: &Circuits, n: usize) -> u64 {
|
||||
.unwrap() as u64
|
||||
}
|
||||
|
||||
#[aoc(day8, part1)]
|
||||
#[aoc(day8, part1, Sorted)]
|
||||
fn part1(input: &Circuits) -> u64 {
|
||||
part1_impl(input, 1000)
|
||||
}
|
||||
|
||||
#[aoc(day8, part2)]
|
||||
#[aoc(day8, part2, Sorted)]
|
||||
fn part2(input: &Circuits) -> u64 {
|
||||
let mut circuits = input.clone();
|
||||
|
||||
@@ -138,6 +138,70 @@ fn part2(input: &Circuits) -> u64 {
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct JunctionPair {
|
||||
d: u64,
|
||||
a: usize,
|
||||
b: usize,
|
||||
}
|
||||
|
||||
fn make_heap(circuits: &Circuits) -> BinaryHeap<Reverse<JunctionPair>> {
|
||||
BinaryHeap::from_iter(
|
||||
circuits
|
||||
.junctions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.tuple_combinations()
|
||||
.map(|((a_pos, a), (b_pos, b))| {
|
||||
Reverse(JunctionPair {
|
||||
a: a_pos,
|
||||
b: b_pos,
|
||||
d: a.squared_distance(b),
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn part1_heaped_impl(input: &Circuits, n: usize) -> u64 {
|
||||
let mut circuits = input.clone();
|
||||
let mut distances = make_heap(&circuits);
|
||||
|
||||
for _ in 0..n {
|
||||
let pair = distances.pop().unwrap().0;
|
||||
circuits.connect(pair.a, pair.b);
|
||||
}
|
||||
|
||||
circuits
|
||||
.circuits
|
||||
.iter()
|
||||
.map(|c| c.len())
|
||||
.sorted_unstable()
|
||||
.rev()
|
||||
.take(3)
|
||||
.reduce(|a, b| a * b)
|
||||
.unwrap() as u64
|
||||
}
|
||||
|
||||
#[aoc(day8, part1, Heaped)]
|
||||
fn part1_heaped(input: &Circuits) -> u64 {
|
||||
part1_heaped_impl(input, 1000)
|
||||
}
|
||||
|
||||
#[aoc(day8, part2, Heaped)]
|
||||
fn part2_heaped(input: &Circuits) -> u64 {
|
||||
let mut circuits = input.clone();
|
||||
let mut distances = make_heap(&circuits);
|
||||
|
||||
while let Some(Reverse(jp)) = distances.pop() {
|
||||
circuits.connect(jp.a, jp.b);
|
||||
if circuits.circuits.len() == 1 {
|
||||
return (circuits.junctions[jp.a].pos.0 * circuits.junctions[jp.b].pos.0) as u64;
|
||||
}
|
||||
}
|
||||
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -168,8 +232,18 @@ mod tests {
|
||||
assert_eq!(part1_impl(&parse(EXAMPLE), 10), 40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_heaped_example() {
|
||||
assert_eq!(part1_heaped_impl(&parse(EXAMPLE), 10), 40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse(EXAMPLE)), 25272);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_heaped_example() {
|
||||
assert_eq!(part2_heaped(&parse(EXAMPLE)), 25272);
|
||||
}
|
||||
}
|
||||
|
||||
459
src/day9.rs
Normal file
459
src/day9.rs
Normal file
@@ -0,0 +1,459 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use grid::{AsCoord2d, Coord2d, Grid};
|
||||
use indicatif::{ParallelProgressIterator, ProgressIterator, ProgressStyle};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
cmp::{max, min},
|
||||
fmt::Display,
|
||||
};
|
||||
|
||||
#[aoc_generator(day9)]
|
||||
fn parse(input: &str) -> Vec<Coord2d> {
|
||||
input
|
||||
.lines()
|
||||
.map(|l| l.parse::<Coord2d>().unwrap())
|
||||
.map(|c| Coord2d {
|
||||
x: c.x + 1, // allow a buffer zone
|
||||
y: c.y + 1,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[aoc(day9, part1)]
|
||||
fn part1(input: &Vec<Coord2d>) -> u64 {
|
||||
input
|
||||
.iter()
|
||||
.tuple_combinations()
|
||||
.inspect(|(a, b)| {
|
||||
println!(
|
||||
"{a} vs {b} = {}",
|
||||
a.x().abs_diff(b.x()) * a.y().abs_diff(b.y())
|
||||
)
|
||||
})
|
||||
.map(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1))
|
||||
.sorted_unstable()
|
||||
.next_back()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn build_grid(reds: &Vec<Coord2d>) -> Grid<u8> {
|
||||
let width = reds.iter().map(|c| c.x()).max().unwrap() + 3;
|
||||
let height = reds.iter().map(|c| c.y()).max().unwrap() + 3;
|
||||
|
||||
let mut grid = Grid::with_shape(width as usize, height as usize, b'.');
|
||||
let mut prev = reds.last().unwrap();
|
||||
for c in reds {
|
||||
// mark c filled
|
||||
grid.set(&c, b'#');
|
||||
// build a line of green between it and the previous
|
||||
if c.x() == prev.x() {
|
||||
// vertical
|
||||
for y in (min(c.y(), prev.y()) + 1)..max(c.y(), prev.y()) {
|
||||
grid.set(&(c.x(), y), b'X');
|
||||
}
|
||||
} else if c.y() == prev.y() {
|
||||
// horiztonal
|
||||
for x in (min(c.x(), prev.x()) + 1)..max(c.x(), prev.x()) {
|
||||
grid.set(&(x, c.y()), b'X');
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
prev = c
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
|
||||
fn build_hashgrid(reds: &Vec<Coord2d>) -> HashMap<Coord2d, u8> {
|
||||
let mut grid = HashMap::new();
|
||||
let mut prev = reds.last().unwrap();
|
||||
for c in reds {
|
||||
// mark c filled
|
||||
grid.insert(*c, b'#');
|
||||
// build a line of green between it and the previous
|
||||
if c.x() == prev.x() {
|
||||
// vertical
|
||||
for y in (min(c.y(), prev.y()) + 1)..max(c.y(), prev.y()) {
|
||||
grid.insert(Coord2d { x: c.x(), y }, b'X');
|
||||
}
|
||||
} else if c.y() == prev.y() {
|
||||
// horiztonal
|
||||
for x in (min(c.x(), prev.x()) + 1)..max(c.x(), prev.x()) {
|
||||
grid.insert(Coord2d { x, y: c.y() }, b'X');
|
||||
}
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
prev = c
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
|
||||
// FIXME: TOTALLY BROKEN
|
||||
fn flood_fill(grid: &mut Grid<u8>) {
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum State {
|
||||
OffLine(bool), // Off a line(true=inside)
|
||||
OnLine(bool), // On a line(previous state)
|
||||
}
|
||||
for y in 0..grid.height() {
|
||||
let mut state = State::OffLine(false);
|
||||
for x in 0..grid.width() {
|
||||
match grid.get(&(x, y)) {
|
||||
Some(b'.') => {
|
||||
if state == State::OffLine(true) || state == State::OnLine(false) {
|
||||
grid.set(&(x, y), b'X');
|
||||
state = State::OffLine(true)
|
||||
} else {
|
||||
state = State::OffLine(false)
|
||||
}
|
||||
}
|
||||
Some(b'#') | Some(b'X') => {
|
||||
state = State::OnLine(match state {
|
||||
State::OnLine(s) => s,
|
||||
State::OffLine(s) => s,
|
||||
})
|
||||
}
|
||||
None => panic!("overran the grid"),
|
||||
Some(c) => panic!("unexpected value {c}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[aoc(day9, part2, Brute)]
|
||||
fn part2(reds: &Vec<Coord2d>) -> u64 {
|
||||
let mut grid = build_grid(reds);
|
||||
flood_fill(&mut grid);
|
||||
println!("{grid}");
|
||||
|
||||
let pairs = reds
|
||||
.iter()
|
||||
.tuple_combinations()
|
||||
.sorted_unstable_by_key(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1))
|
||||
.rev()
|
||||
.progress()
|
||||
.collect_vec();
|
||||
|
||||
let (a, b) = pairs
|
||||
.par_iter()
|
||||
.progress_with_style(
|
||||
ProgressStyle::with_template(
|
||||
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.filter(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1) < 2972065369) // FIXME: PROGRESS CAPTURE
|
||||
.map(|(a, b)| {
|
||||
for y in (min(a.y(), b.y()))..=max(a.y(), b.y()) {
|
||||
for x in (min(a.x(), b.x()))..=max(a.x(), b.y()) {
|
||||
if *grid.get(&(x, y)).unwrap() == b'.' {
|
||||
return (false, a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
(true, a, b)
|
||||
})
|
||||
.find_map_first(|(good, a, b)| if good { Some((a, b)) } else { None })
|
||||
.unwrap();
|
||||
println!("win: {a} {b}");
|
||||
(a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1)
|
||||
}
|
||||
|
||||
fn line_square_intersection<LT: AsCoord2d, ST: AsCoord2d>(
|
||||
&(l1, l2): &(<, <),
|
||||
&(sq1, sq2): &(&ST, &ST),
|
||||
) -> bool
|
||||
where
|
||||
LT: Clone + Display,
|
||||
ST: Clone + Display,
|
||||
{
|
||||
let min_x = min(sq1.x(), sq2.x());
|
||||
let max_x = max(sq1.x(), sq2.x());
|
||||
let min_y = min(sq1.y(), sq2.y());
|
||||
let max_y = max(sq1.y(), sq2.y());
|
||||
|
||||
// println!("({},{}) X [{},{}]", l1, l2, sq1, sq2);
|
||||
// line is horizontal
|
||||
if l1.y() == l2.y() {
|
||||
// println!(" horizontal at y={}", l1.y());
|
||||
// above, below, or touching square
|
||||
if l1.y() <= min_y || l1.y() >= max_y {
|
||||
// println!(" y out of range or touching");
|
||||
false
|
||||
// start inside
|
||||
} else if min(l1.x(), l2.x()) >= min_x && min(l1.x(), l2.x()) <= max_x {
|
||||
// println!(" start inside");
|
||||
true
|
||||
//end inside
|
||||
} else if max(l1.x(), l2.x()) >= min_x && max(l1.x(), l2.x()) <= max_x {
|
||||
// println!(" end inside");
|
||||
true
|
||||
} else {
|
||||
// println!(" no overlap on x");
|
||||
false
|
||||
}
|
||||
// line is vertical
|
||||
} else {
|
||||
// println!(" vertical at x={}", l1.x());
|
||||
if l1.x() <= min_x || l1.x() >= max_x {
|
||||
// println!(" x out of range or touching");
|
||||
false
|
||||
// start inside
|
||||
} else if min(l1.y(), l2.y()) >= min_y && min(l1.y(), l2.y()) <= max_y {
|
||||
// println!(" start inside");
|
||||
true
|
||||
//end inside
|
||||
} else if max(l1.y(), l2.y()) >= min_y && max(l1.y(), l2.y()) <= max_y {
|
||||
// println!(" end inside");
|
||||
true
|
||||
} else {
|
||||
// println!(" no overlap on y");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Rectangle {
|
||||
fn top_left(&self) -> Coord2d;
|
||||
fn bottom_right(&self) -> Coord2d;
|
||||
fn in_bounds<T: AsCoord2d>(&self, b: &T) -> bool {
|
||||
b.x() >= self.top_left().x
|
||||
&& b.x() <= self.bottom_right().x()
|
||||
&& b.y() >= self.top_left().y()
|
||||
&& b.y() <= self.bottom_right().y()
|
||||
}
|
||||
fn area(&self) -> u64 {
|
||||
let a = self.top_left();
|
||||
let b = self.bottom_right();
|
||||
(a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle for (&Coord2d, &Coord2d) {
|
||||
fn top_left(&self) -> Coord2d {
|
||||
Coord2d {
|
||||
x: min(self.0.x, self.1.x),
|
||||
y: min(self.0.y, self.1.y),
|
||||
}
|
||||
}
|
||||
fn bottom_right(&self) -> Coord2d {
|
||||
Coord2d {
|
||||
x: max(self.0.x, self.1.x),
|
||||
y: max(self.0.y, self.1.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// true = clockwise
|
||||
fn outside_points(prev: &Coord2d, cur: &Coord2d, next: &Coord2d) -> Vec<Coord2d> {
|
||||
let l1_diff = ((cur.x - prev.x).signum(), (cur.y - prev.y).signum());
|
||||
let l2_diff = ((next.x - cur.x).signum(), (next.y - cur.y).signum());
|
||||
|
||||
match (l1_diff, l2_diff) {
|
||||
// CW
|
||||
((1, 0), (0, 1)) => vec![cur + (0, -1), cur + (1, -1), cur + (1, 0)], // x^ y^
|
||||
((0, 1), (-1, 0)) => vec![cur + (1, 0), cur + (1, 1), cur + (0, -1)], // y^ xv
|
||||
((-1, 0), (0, -1)) => vec![cur + (0, 1), cur + (-1, 1), cur + (-1, 0)], // xv yv
|
||||
((0, -1), (1, 0)) => vec![cur + (-1, 0), cur + (-1, -1), cur + (0, -1)], // yv x^
|
||||
// CCW
|
||||
((0, 1), (1, 0)) => vec![cur + (1, -1)],
|
||||
((1, 0), (0, -1)) => vec![cur + (-1, -1)],
|
||||
((0, -1), (-1, 0)) => vec![cur + (-1, 1)],
|
||||
((-1, 0), (0, 1)) => vec![cur + (1, 1)],
|
||||
((0, _), (0, _)) | ((_, 0), (_, 0)) => vec![], // colinear
|
||||
_ => panic!(
|
||||
"unexpected line arrangement {:?} {:?} @ [({},{}), ({},{}), ({},{})",
|
||||
l1_diff, l2_diff, prev.x, prev.y, cur.x, cur.y, next.x, next.y
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc(day9, part2, Lines)]
|
||||
fn part2_lines(reds: &Vec<Coord2d>) -> u64 {
|
||||
// let mut grid = build_hashgrid(reds);
|
||||
let mut grid = build_grid(reds);
|
||||
println!("{grid}");
|
||||
let outside_points: HashMap<&Coord2d, Vec<Coord2d>> = reds
|
||||
.iter()
|
||||
.circular_tuple_windows()
|
||||
.map(|(a, b, c)| (b, outside_points(a, b, c)))
|
||||
.collect();
|
||||
// for corner in reds {
|
||||
// for p in &outside_points[corner] {
|
||||
// if *grid.get(p).unwrap() == b'.' {
|
||||
// grid.set(p, b'O');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// println!("{grid}");
|
||||
|
||||
// still iterate over the possible rectangles in size order
|
||||
for rect in reds
|
||||
.iter()
|
||||
.tuple_combinations()
|
||||
.sorted_unstable_by_key(|rect: &(&Coord2d, &Coord2d)| rect.area())
|
||||
.rev()
|
||||
.progress_with_style(
|
||||
ProgressStyle::with_template(
|
||||
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
{
|
||||
println!(
|
||||
"[{},{}] = {}",
|
||||
rect.top_left(),
|
||||
rect.bottom_right(),
|
||||
rect.area()
|
||||
);
|
||||
// then for each corner, check if any of its outside edges are inside and not part of another line
|
||||
if reds
|
||||
.iter()
|
||||
.filter(|corner| rect.in_bounds(corner))
|
||||
.inspect(|c| println!(" corner: {c}"))
|
||||
.any(|corner| {
|
||||
outside_points[corner]
|
||||
.iter()
|
||||
.inspect(|p| println!(" {} = {}", p, *grid.get(p).unwrap() as char))
|
||||
.any(|p| rect.in_bounds(p) && *grid.get(p).unwrap() == b'.')
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
println!(
|
||||
"win! {},{} = {}",
|
||||
rect.top_left(),
|
||||
rect.bottom_right(),
|
||||
rect.area()
|
||||
);
|
||||
grid.set(&rect.top_left(), b'*');
|
||||
grid.set(&rect.bottom_right(), b'*');
|
||||
|
||||
println!("{grid}");
|
||||
return rect.area();
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
const EXAMPLE: &str = "7,1
|
||||
11,1
|
||||
11,7
|
||||
9,7
|
||||
9,5
|
||||
2,5
|
||||
2,3
|
||||
7,3";
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse(EXAMPLE)), 50);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse(EXAMPLE)), 24);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"7,1
|
||||
11,1
|
||||
11,7
|
||||
9,7
|
||||
9,5
|
||||
2,5
|
||||
2,3
|
||||
7,3",
|
||||
24
|
||||
)]
|
||||
#[case(
|
||||
"4,2
|
||||
13,2
|
||||
13,4
|
||||
8,4
|
||||
8,6
|
||||
11,6
|
||||
11,10
|
||||
4,10",
|
||||
40
|
||||
)]
|
||||
#[case(
|
||||
"3,2
|
||||
17,2
|
||||
17,13
|
||||
13,13
|
||||
13,11
|
||||
15,11
|
||||
15,8
|
||||
11,8
|
||||
11,15
|
||||
18,15
|
||||
18,17
|
||||
4,17
|
||||
4,12
|
||||
6,12
|
||||
6,5
|
||||
3,5",
|
||||
66
|
||||
)]
|
||||
#[case(
|
||||
"2,2
|
||||
8,2
|
||||
8,6
|
||||
5,6
|
||||
5,4
|
||||
2,4",
|
||||
21
|
||||
)]
|
||||
#[case(
|
||||
"3,1
|
||||
12,1
|
||||
12,4
|
||||
9,4
|
||||
9,8
|
||||
3,8",
|
||||
56
|
||||
)]
|
||||
#[case(
|
||||
"7,1
|
||||
11,1
|
||||
11,3
|
||||
13,3
|
||||
13,5
|
||||
11,5
|
||||
11,7
|
||||
9,7
|
||||
9,5
|
||||
2,5
|
||||
2,3
|
||||
7,3",
|
||||
36
|
||||
)]
|
||||
#[case(
|
||||
"1,2
|
||||
8,2
|
||||
8,4
|
||||
6,4
|
||||
6,5
|
||||
9,5
|
||||
9,8
|
||||
1,8",
|
||||
56
|
||||
)]
|
||||
|
||||
fn part2_lines_example(#[case] input: &str, #[case] answer: u64) {
|
||||
assert_eq!(part2_lines(&parse(input)), answer);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
mod day1;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
mod day2;
|
||||
mod day3;
|
||||
mod day4;
|
||||
@@ -6,6 +9,7 @@ mod day5;
|
||||
mod day6;
|
||||
mod day7;
|
||||
mod day8;
|
||||
mod day9;
|
||||
|
||||
use aoc_runner_derive::aoc_lib;
|
||||
|
||||
|
||||
@@ -22,22 +22,42 @@ pub const ADJACENT_OFFSETS: [&(i64, i64); 8] = [
|
||||
/// NESW
|
||||
pub const CARDINAL_OFFSETS: [&(i64, i64); 4] = [&(0, -1), &(-1, 0), &(1, 0), &(0, 1)];
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum MirrorAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub struct Coord2d {
|
||||
pub x: i64,
|
||||
pub y: i64,
|
||||
}
|
||||
|
||||
impl Debug for Coord2d {
|
||||
impl Display for Coord2d {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("({}, {})", self.x, self.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Coord2d {
|
||||
type Err = Box<dyn std::error::Error>;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (l, r) = s.split_once(',').ok_or("Can't split on ,")?;
|
||||
Ok(Coord2d {
|
||||
x: l.parse()?,
|
||||
y: r.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsCoord2d {
|
||||
fn to_coord(self) -> Coord2d;
|
||||
fn x(&self) -> i64;
|
||||
fn y(&self) -> i64;
|
||||
|
||||
fn manhattan<T: AsCoord2d>(&self, other: &T) -> u64 {
|
||||
self.x().abs_diff(other.x()) + self.y().abs_diff(other.y())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsCoord2d> Sub<T> for &Coord2d {
|
||||
@@ -266,7 +286,7 @@ impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for OffsetsIter<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
||||
pub struct Grid<T> {
|
||||
pub data: Vec<T>,
|
||||
width: i64,
|
||||
@@ -471,6 +491,79 @@ impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rotated(&self, mut rot: u8) -> Self {
|
||||
rot %= 4;
|
||||
match rot {
|
||||
0 => self.clone(),
|
||||
1 => {
|
||||
let mut n = Grid::with_shape(self.height(), self.width(), self.data[0].clone()); // fill will be overwritten anyway
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
n.set(
|
||||
&(self.height() - y - 1, x),
|
||||
self.get(&(x as u64, y as u64)).unwrap().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
2 => {
|
||||
let mut n = Grid::with_shape(self.height(), self.width(), self.data[0].clone()); // fill will be overwritten anyway
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
n.set(
|
||||
&(self.width() - x - 1, self.height() - y - 1),
|
||||
self.get(&(x as u64, y as u64)).unwrap().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
3 => {
|
||||
let mut n = Grid::with_shape(self.height(), self.width(), self.data[0].clone()); // fill will be overwritten anyway
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
n.set(
|
||||
&(self.height() - y - 1, self.width() - x - 1),
|
||||
self.get(&(x as u64, y as u64)).unwrap().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
_ => unreachable!("invalid rotation"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mirrored(&self, axis: MirrorAxis) -> Self {
|
||||
match axis {
|
||||
MirrorAxis::X => {
|
||||
let mut n = Grid::with_shape(self.height(), self.width(), self.data[0].clone()); // fill will be overwritten anyway
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
n.set(
|
||||
&(self.width() - x - 1, y),
|
||||
self.get(&(x as u64, y as u64)).unwrap().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
MirrorAxis::Y => {
|
||||
let mut n = Grid::with_shape(self.height(), self.width(), self.data[0].clone()); // fill will be overwritten anyway
|
||||
for y in 0..self.height() {
|
||||
for x in 0..self.width() {
|
||||
n.set(
|
||||
&(x, &self.height() - y - 1),
|
||||
self.get(&(x as u64, y as u64)).unwrap().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn window_compare_impl<const REV: bool>(&self, needle: &[T]) -> Vec<(i64, i64)> {
|
||||
// if (self.width as usize) < needle.len() {
|
||||
// return Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user