Solving
This commit is contained in:
parent
be5ff660dd
commit
23ac043d8e
340
Cargo.lock
generated
340
Cargo.lock
generated
|
@ -2,12 +2,55 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrays"
|
name = "arrays"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dbdbb2877d26e0647c6b8125802b2ecf2dc2a28d864dde41e6f9b9a54da08fe"
|
checksum = "2dbdbb2877d26e0647c6b8125802b2ecf2dc2a28d864dde41e6f9b9a54da08fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.59"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async_once"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beef"
|
name = "beef"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -20,6 +63,58 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec_simd"
|
||||||
|
version = "0.20.5"
|
||||||
|
source = "git+https://github.com/Imberflur/bitvec_simd?branch=master#b2c1df36d8fc015b0c2494305f3be70451e4f763"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
"wide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cached"
|
||||||
|
version = "0.40.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b4147cd94d5fbdc2ab71b11d50a2f45493625576b3bb70257f59eedea69f3d"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"async_once",
|
||||||
|
"cached_proc_macro",
|
||||||
|
"cached_proc_macro_types",
|
||||||
|
"futures",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
"instant",
|
||||||
|
"lazy_static",
|
||||||
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cached_proc_macro"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "751f7f4e7a091545e7f6c65bacc404eaee7e87bfb1f9ece234a1caa173dc16f2"
|
||||||
|
dependencies = [
|
||||||
|
"cached_proc_macro_types",
|
||||||
|
"darling",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cached_proc_macro_types"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.77"
|
version = "1.0.77"
|
||||||
|
@ -76,6 +171,41 @@ version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "882e392738ed515520f38708166e9efec85ee154f80bf1fc64510e2fc54bf481"
|
checksum = "882e392738ed515520f38708166e9efec85ee154f80bf1fc64510e2fc54bf481"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day01"
|
name = "day01"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -175,14 +305,27 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day14"
|
name = "day14"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec_simd",
|
||||||
|
"hashbrown 0.13.1",
|
||||||
|
"termion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day15"
|
name = "day15"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day16"
|
name = "day16"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec_simd",
|
||||||
|
"cached",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day17"
|
name = "day17"
|
||||||
|
@ -243,6 +386,67 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
@ -254,6 +458,21 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -269,6 +488,21 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -361,6 +595,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numtoa"
|
name = "numtoa"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -379,6 +619,18 @@ version = "6.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -475,6 +727,17 @@ dependencies = [
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.28"
|
version = "0.6.28"
|
||||||
|
@ -495,6 +758,30 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "safe_arch"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.150"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -543,6 +830,49 @@ dependencies = [
|
||||||
"redox_termios",
|
"redox_termios",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -561,6 +891,16 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wide"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae41ecad2489a1655c8ef8489444b0b113c0a0c795944a3572a0931cf7d2525c"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"safe_arch",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -8,3 +8,6 @@ build = true
|
||||||
app = true
|
app = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitvec_simd = { git = "https://github.com/Imberflur/bitvec_simd", branch = "master" }
|
||||||
|
hashbrown = { version = "0.13.1", features = ["nightly"] }
|
||||||
|
termion = "2.0.1"
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
use std::ops::{Index, IndexMut, Sub};
|
use std::ops::Sub;
|
||||||
|
|
||||||
|
use bitvec_simd::BitVec;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use termion::color;
|
||||||
|
|
||||||
const SOURCE: Point = Point::new(500, 0);
|
const SOURCE: Point = Point::new(500, 0);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub enum Material {
|
pub enum Material {
|
||||||
|
#[default]
|
||||||
Air,
|
Air,
|
||||||
Stone,
|
Stone,
|
||||||
Sand,
|
Sand,
|
||||||
|
@ -12,40 +17,24 @@ pub enum Material {
|
||||||
pub enum InsertResult {
|
pub enum InsertResult {
|
||||||
Ok,
|
Ok,
|
||||||
Overflow,
|
Overflow,
|
||||||
|
ReachedTop,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum GravityApplyResult {
|
pub enum GravityApplyResult {
|
||||||
Moved,
|
Moved,
|
||||||
Fixed,
|
Fixed,
|
||||||
|
Outside,
|
||||||
|
ReachedTop,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Point([u16; 2]);
|
pub struct Point([u16; 2]);
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
const MAX: Self = Point::new(u16::MAX, u16::MAX);
|
|
||||||
const MIN: Self = Point::new(u16::MIN, u16::MIN);
|
|
||||||
|
|
||||||
pub const fn new(x: u16, y: u16) -> Self {
|
pub const fn new(x: u16, y: u16) -> Self {
|
||||||
Self::from([x, y])
|
Self::from([x, y])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn maximize(&self, other: &Self) -> Self {
|
|
||||||
let sx = self.0[0];
|
|
||||||
let sy = self.0[1];
|
|
||||||
let ox = other.0[0];
|
|
||||||
let oy = other.0[1];
|
|
||||||
Point::new(sx.max(ox), sy.max(oy))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn minimize(&self, other: &Self) -> Self {
|
|
||||||
let sx = self.0[0];
|
|
||||||
let sy = self.0[1];
|
|
||||||
let ox = other.0[0];
|
|
||||||
let oy = other.0[1];
|
|
||||||
Point::new(sx.min(ox), sy.min(oy))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn x(&self) -> u16 {
|
pub const fn x(&self) -> u16 {
|
||||||
self.0[0]
|
self.0[0]
|
||||||
}
|
}
|
||||||
|
@ -54,14 +43,6 @@ impl Point {
|
||||||
self.0[1]
|
self.0[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x_mut(&mut self) -> &mut u16 {
|
|
||||||
&mut self.0[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn y_mut(&mut self) -> &mut u16 {
|
|
||||||
&mut self.0[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_to(&self, other: &Point) -> Box<dyn Iterator<Item = Self>> {
|
fn line_to(&self, other: &Point) -> Box<dyn Iterator<Item = Self>> {
|
||||||
let sx = self.0[0];
|
let sx = self.0[0];
|
||||||
let sy = self.0[1];
|
let sy = self.0[1];
|
||||||
|
@ -73,136 +54,143 @@ impl Point {
|
||||||
Box::from((sy.min(oy)..=sy.max(oy)).map(move |new_y| Point::new(sx, new_y)))
|
Box::from((sy.min(oy)..=sy.max(oy)).map(move |new_y| Point::new(sx, new_y)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn down(&self) -> Point {
|
||||||
|
Point::new(self.x(), self.y() + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn down_left(&self) -> Point {
|
||||||
|
Point::new(self.x() - 1, self.y() + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn down_right(&self) -> Point {
|
||||||
|
Point::new(self.x() + 1, self.y() + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cave {
|
pub struct Cave<const HAS_FLOOR: bool> {
|
||||||
min: Point,
|
grid: HashMap<Point, Material>,
|
||||||
max: Point,
|
max_stone_y: u16,
|
||||||
grid: Vec<Material>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cave {
|
impl<const HAS_FLOOR: bool> Cave<HAS_FLOOR> {
|
||||||
pub fn from_span(min: Point, max: Point) -> Self {
|
pub fn get(&self, index: &Point) -> Option<&Material> {
|
||||||
let width = (max.x() - min.x()) as usize + 1;
|
self.grid.get(index)
|
||||||
let height = (max.y() - min.y()) as usize + 1;
|
|
||||||
let grid: Vec<Material> = ::std::iter::repeat(Material::Air)
|
|
||||||
.take(width * height)
|
|
||||||
.collect();
|
|
||||||
Cave { min, max, grid }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, index: Point) -> &Material {
|
|
||||||
let normalized = index - self.min;
|
|
||||||
let idx = normalized.x() as usize + normalized.y() as usize * self.width() as usize;
|
|
||||||
&self.grid[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mut(&mut self, index: Point) -> &mut Material {
|
|
||||||
let normalized = index - self.min;
|
|
||||||
let idx = normalized.x() as usize + normalized.y() as usize * self.width() as usize;
|
|
||||||
&mut self.grid[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn width(&self) -> u16 {
|
|
||||||
self.max.x() - self.min.x() + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_sand(&mut self) -> InsertResult {
|
pub fn insert_sand(&mut self) -> InsertResult {
|
||||||
let mut pos = SOURCE;
|
let mut pos = SOURCE;
|
||||||
while self.is_in_bounds(pos) {
|
loop {
|
||||||
match self.apply_gravity(&mut pos) {
|
match self.apply_gravity(&mut pos) {
|
||||||
GravityApplyResult::Moved => {}
|
GravityApplyResult::Moved => {}
|
||||||
GravityApplyResult::Fixed => {
|
GravityApplyResult::Fixed => {
|
||||||
self[pos] = Material::Sand;
|
self.insert(pos, Material::Sand);
|
||||||
return InsertResult::Ok;
|
break InsertResult::Ok;
|
||||||
}
|
}
|
||||||
|
GravityApplyResult::Outside => break InsertResult::Overflow,
|
||||||
|
GravityApplyResult::ReachedTop => break InsertResult::ReachedTop,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertResult::Overflow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_bounds(&self, pos: Point) -> bool {
|
pub fn count_sand_using_scanline(&mut self) -> usize {
|
||||||
self.min.x() <= pos.x()
|
let mut y = SOURCE.y();
|
||||||
&& self.min.y() <= pos.y()
|
let mut min_x = SOURCE.x();
|
||||||
&& self.max.x() >= pos.x()
|
let mut max_x = SOURCE.x();
|
||||||
&& self.max.y() >= pos.y()
|
let mut bits = BitVec::zeros(SOURCE.x() as usize);
|
||||||
|
let mut total = 0;
|
||||||
|
bits.set(SOURCE.x() as usize, true);
|
||||||
|
while !bits.is_empty() {
|
||||||
|
if y == self.max_stone_y + 2 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut last_left_bit = false;
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
let mat = self.get(&Point::new(x, y)).copied().unwrap_or_default();
|
||||||
|
let set_bit = bits.get(x as usize).unwrap_or_default()
|
||||||
|
|| last_left_bit
|
||||||
|
|| bits.get(x as usize + 1).unwrap_or_default();
|
||||||
|
last_left_bit = bits.get(x as usize).unwrap_or_default();
|
||||||
|
if mat == Material::Stone {
|
||||||
|
bits.set(x as usize, false);
|
||||||
|
} else if set_bit && mat == Material::Air {
|
||||||
|
bits.set(x as usize, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min_x -= 1;
|
||||||
|
max_x += 1;
|
||||||
|
y += 1;
|
||||||
|
total += bits.count_ones();
|
||||||
|
}
|
||||||
|
total
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_gravity(&self, pos: &mut Point) -> GravityApplyResult {
|
fn apply_gravity(&self, pos: &mut Point) -> GravityApplyResult {
|
||||||
if pos.y() == self.max.y() {
|
if HAS_FLOOR && pos.y() == self.max_stone_y + 1 {
|
||||||
*pos.y_mut() += 1;
|
GravityApplyResult::Fixed
|
||||||
|
} else if !HAS_FLOOR && pos.y() >= self.max_stone_y {
|
||||||
|
*pos = pos.down();
|
||||||
|
GravityApplyResult::Outside
|
||||||
|
} else if self.is_air(&pos.down()) {
|
||||||
|
*pos = pos.down();
|
||||||
GravityApplyResult::Moved
|
GravityApplyResult::Moved
|
||||||
} else if let Material::Air = &self[Point::new(pos.x(), pos.y().saturating_add(1))] {
|
} else if self.is_air(&pos.down_left()) {
|
||||||
*pos.y_mut() += 1;
|
*pos = pos.down_left();
|
||||||
GravityApplyResult::Moved
|
GravityApplyResult::Moved
|
||||||
} else if let Material::Air =
|
} else if self.is_air(&pos.down_right()) {
|
||||||
&self[Point::new(pos.x().saturating_sub(1), pos.y().saturating_add(1))]
|
*pos = pos.down_right();
|
||||||
{
|
|
||||||
*pos.y_mut() += 1;
|
|
||||||
*pos.x_mut() -= 1;
|
|
||||||
GravityApplyResult::Moved
|
|
||||||
} else if let Material::Air =
|
|
||||||
&self[Point::new(pos.x().saturating_add(1), pos.y().saturating_add(1))]
|
|
||||||
{
|
|
||||||
*pos.y_mut() += 1;
|
|
||||||
*pos.x_mut() += 1;
|
|
||||||
GravityApplyResult::Moved
|
GravityApplyResult::Moved
|
||||||
|
} else if *pos == SOURCE {
|
||||||
|
GravityApplyResult::ReachedTop
|
||||||
} else {
|
} else {
|
||||||
GravityApplyResult::Fixed
|
GravityApplyResult::Fixed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(&self) -> u16 {
|
fn is_air(&self, pos: &Point) -> bool {
|
||||||
self.max.y() - self.min.y() + 1
|
match self.get(pos) {
|
||||||
}
|
Some(Material::Air) | None => true,
|
||||||
}
|
_ => false,
|
||||||
|
}
|
||||||
impl FromIterator<Vec<Point>> for Cave {
|
}
|
||||||
fn from_iter<T: IntoIterator<Item = Vec<Point>>>(iter: T) -> Self {
|
|
||||||
let (stone_points, min, max) = iter.into_iter().fold(
|
pub fn new() -> Self {
|
||||||
(vec![], Point::MAX, Point::MIN),
|
Self {
|
||||||
|(mut stone_points, mut min, mut max), points| {
|
grid: HashMap::new(),
|
||||||
let mut last: Option<Point> = None;
|
max_stone_y: 0,
|
||||||
for point in points {
|
}
|
||||||
min = point.minimize(&min);
|
}
|
||||||
max = point.maximize(&max);
|
|
||||||
match last {
|
fn insert(&mut self, pos: Point, material: Material) -> Option<Material> {
|
||||||
Some(last) => stone_points.extend(last.line_to(&point)),
|
if material == Material::Stone {
|
||||||
// Push the first point manually
|
self.max_stone_y = self.max_stone_y.max(pos.y());
|
||||||
None => stone_points.push(point.clone()),
|
}
|
||||||
}
|
self.grid.insert(pos, material)
|
||||||
last = Some(point);
|
}
|
||||||
}
|
}
|
||||||
(stone_points, min, max)
|
|
||||||
},
|
impl<const HAS_FLOOR: bool> FromIterator<Vec<Point>> for Cave<HAS_FLOOR> {
|
||||||
);
|
fn from_iter<T: IntoIterator<Item = Vec<Point>>>(iter: T) -> Self {
|
||||||
let min = min.minimize(&SOURCE);
|
let stone_points = iter.into_iter().fold(vec![], |mut stone_points, points| {
|
||||||
let max = max.maximize(&SOURCE);
|
let mut last: Option<Point> = None;
|
||||||
let min = Point::new(min.x().saturating_sub(1), min.y().saturating_sub(1));
|
for point in points {
|
||||||
let max = Point::new(max.x().saturating_add(1), max.y().saturating_add(1));
|
match last {
|
||||||
let mut cave = Cave::from_span(min, max);
|
Some(last) => stone_points.extend(last.line_to(&point)),
|
||||||
for point in stone_points {
|
// Push the first point manually
|
||||||
cave[point] = Material::Stone;
|
None => stone_points.push(point.clone()),
|
||||||
|
}
|
||||||
|
last = Some(point);
|
||||||
|
}
|
||||||
|
stone_points
|
||||||
|
});
|
||||||
|
let mut cave = Cave::new();
|
||||||
|
for point in stone_points {
|
||||||
|
cave.insert(point, Material::Stone);
|
||||||
}
|
}
|
||||||
println!("{cave}");
|
|
||||||
cave
|
cave
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<Point> for Cave {
|
|
||||||
type Output = Material;
|
|
||||||
|
|
||||||
fn index(&self, index: Point) -> &Self::Output {
|
|
||||||
self.get(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<Point> for Cave {
|
|
||||||
fn index_mut(&mut self, index: Point) -> &mut Self::Output {
|
|
||||||
self.get_mut(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl const From<[u16; 2]> for Point {
|
impl const From<[u16; 2]> for Point {
|
||||||
fn from(raw: [u16; 2]) -> Self {
|
fn from(raw: [u16; 2]) -> Self {
|
||||||
Self(raw)
|
Self(raw)
|
||||||
|
@ -217,26 +205,54 @@ impl const Sub for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Cave {
|
impl<const HAS_FLOOR: bool> std::fmt::Display for Cave<HAS_FLOOR> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for y in self.min.y()..=self.max.y() {
|
let (min_x, max_x, min_y, max_y) = self.grid.keys().fold(
|
||||||
for x in self.min.x()..=self.max.x() {
|
(u16::MAX, u16::MIN, u16::MAX, u16::MIN),
|
||||||
if Point::new(x, y) == SOURCE {
|
|(min_x, max_x, min_y, max_y), pos| {
|
||||||
write!(f, "@")?;
|
(
|
||||||
|
min_x.min(pos.x()),
|
||||||
|
max_x.max(pos.x()),
|
||||||
|
min_y.min(pos.y()),
|
||||||
|
max_y.max(pos.y()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let min_x = min_x.min(SOURCE.x());
|
||||||
|
let max_x = max_x.max(SOURCE.x());
|
||||||
|
let min_y = min_y.min(SOURCE.y());
|
||||||
|
let max_y = max_y.max(SOURCE.y());
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
let pos = Point::new(x, y);
|
||||||
|
let material = self.get(&pos);
|
||||||
|
if pos == SOURCE {
|
||||||
|
write!(f, "{}", color::Fg(color::Green))?
|
||||||
} else {
|
} else {
|
||||||
write!(
|
match material {
|
||||||
f,
|
Some(Material::Air) | None => {
|
||||||
"{}",
|
write!(f, "{}", color::Fg(color::Rgb(128, 128, 128)))
|
||||||
match self[Point::new(x, y)] {
|
|
||||||
Material::Air => ".",
|
|
||||||
Material::Sand => "o",
|
|
||||||
Material::Stone => "#",
|
|
||||||
}
|
}
|
||||||
)?;
|
Some(Material::Stone) => write!(f, "{}", color::Fg(color::LightRed)),
|
||||||
}
|
Some(Material::Sand) => write!(f, "{}", color::Fg(color::Yellow)),
|
||||||
|
}?
|
||||||
|
};
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
if pos == SOURCE {
|
||||||
|
"@"
|
||||||
|
} else {
|
||||||
|
match material {
|
||||||
|
Some(Material::Air) | None => ".",
|
||||||
|
Some(Material::Stone) => "#",
|
||||||
|
Some(Material::Sand) => "o",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
writeln!(f)
|
writeln!(f, "{}", color::Fg(color::Reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,21 +30,27 @@ fn parse_line<S: AsRef<str>>(line: S) -> Vec<Point> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
|
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
|
||||||
let mut cave: Cave = reader
|
let mut cave: Cave<false> = reader
|
||||||
.lines()
|
.lines()
|
||||||
.map(Result::unwrap)
|
.map(Result::unwrap)
|
||||||
.map(|line| parse_line(&line))
|
.map(|line| parse_line(&line))
|
||||||
.collect();
|
.collect();
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
while let InsertResult::Ok = cave.insert_sand() {
|
while let InsertResult::Ok = cave.insert_sand() {
|
||||||
println!("{cave}");
|
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
println!("{cave}");
|
||||||
counter
|
counter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
|
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
|
||||||
todo!()
|
let mut cave: Cave<true> = reader
|
||||||
|
.lines()
|
||||||
|
.map(Result::unwrap)
|
||||||
|
.map(|line| parse_line(&line))
|
||||||
|
.collect();
|
||||||
|
println!("{cave}");
|
||||||
|
cave.count_sand_using_scanline()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -81,6 +87,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn example_on_second() {
|
fn example_on_second() {
|
||||||
let reader = Cursor::new(include_str!("../input.test"));
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
assert_eq!(solve_second_puzzle(reader), 140);
|
assert_eq!(solve_second_puzzle(reader), 93);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,4 @@ build = true
|
||||||
app = true
|
app = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
regex = "1.7.0"
|
||||||
|
|
23
day15/input
Normal file
23
day15/input
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Sensor at x=325337, y=2568863: closest beacon is at x=-518661, y=2000000
|
||||||
|
Sensor at x=3988825, y=837820: closest beacon is at x=4305648, y=2127118
|
||||||
|
Sensor at x=1611311, y=2053174: closest beacon is at x=2827226, y=1579510
|
||||||
|
Sensor at x=101890, y=3940049: closest beacon is at x=955472, y=3457514
|
||||||
|
Sensor at x=3962702, y=2558425: closest beacon is at x=4226981, y=2604726
|
||||||
|
Sensor at x=2957890, y=2160813: closest beacon is at x=2827226, y=1579510
|
||||||
|
Sensor at x=3907456, y=3325610: closest beacon is at x=3696221, y=3226373
|
||||||
|
Sensor at x=3354177, y=3435919: closest beacon is at x=3696221, y=3226373
|
||||||
|
Sensor at x=3997379, y=3071868: closest beacon is at x=3696221, y=3226373
|
||||||
|
Sensor at x=145143, y=1714962: closest beacon is at x=-518661, y=2000000
|
||||||
|
Sensor at x=611563, y=3148864: closest beacon is at x=955472, y=3457514
|
||||||
|
Sensor at x=3080405, y=3904777: closest beacon is at x=3696221, y=3226373
|
||||||
|
Sensor at x=644383, y=10732: closest beacon is at x=364635, y=-294577
|
||||||
|
Sensor at x=3229566, y=1694167: closest beacon is at x=2827226, y=1579510
|
||||||
|
Sensor at x=1600637, y=3984884: closest beacon is at x=955472, y=3457514
|
||||||
|
Sensor at x=2959765, y=2820860: closest beacon is at x=2491502, y=2897876
|
||||||
|
Sensor at x=2235330, y=3427797: closest beacon is at x=2491502, y=2897876
|
||||||
|
Sensor at x=2428996, y=210881: closest beacon is at x=2827226, y=1579510
|
||||||
|
Sensor at x=369661, y=687805: closest beacon is at x=364635, y=-294577
|
||||||
|
Sensor at x=3558476, y=2123614: closest beacon is at x=4305648, y=2127118
|
||||||
|
Sensor at x=3551529, y=2825104: closest beacon is at x=3696221, y=3226373
|
||||||
|
Sensor at x=64895, y=3577: closest beacon is at x=364635, y=-294577
|
||||||
|
Sensor at x=3079531, y=1538659: closest beacon is at x=2827226, y=1579510
|
|
@ -1,3 +1,163 @@
|
||||||
fn main() {
|
use regex::Regex;
|
||||||
println!("Hello, world!");
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
};
|
||||||
|
|
||||||
|
const TUNING_FACTOR: i64 = 4_000_000;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
enum SolvePuzzle {
|
||||||
|
First,
|
||||||
|
#[default]
|
||||||
|
Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Sensor {
|
||||||
|
x: i64,
|
||||||
|
y: i64,
|
||||||
|
manhattan_radius: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Beacon {
|
||||||
|
x: i64,
|
||||||
|
y: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sensor {
|
||||||
|
pub const fn min_x(&self) -> i64 {
|
||||||
|
self.x - self.manhattan_radius as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn max_x(&self) -> i64 {
|
||||||
|
self.x + self.manhattan_radius as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_range(&self, x: i64, y: i64) -> bool {
|
||||||
|
x.abs_diff(self.x) + y.abs_diff(self.y) <= self.manhattan_radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input<R: BufRead>(reader: R) -> (Vec<Sensor>, Vec<Beacon>) {
|
||||||
|
let regex =
|
||||||
|
Regex::new(r"^Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)$")
|
||||||
|
.unwrap();
|
||||||
|
reader
|
||||||
|
.lines()
|
||||||
|
.map(Result::unwrap)
|
||||||
|
.map(|line| {
|
||||||
|
let captures = regex.captures(&line).expect("Regex matches line");
|
||||||
|
let sensor_x: i64 = captures
|
||||||
|
.get(1)
|
||||||
|
.expect("Sensor X position")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("Input not a number");
|
||||||
|
let sensor_y: i64 = captures
|
||||||
|
.get(2)
|
||||||
|
.expect("Sensor Y position")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("Input not a number");
|
||||||
|
let beacon_x: i64 = captures
|
||||||
|
.get(3)
|
||||||
|
.expect("Beacon X position")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("Input not a number");
|
||||||
|
let beacon_y: i64 = captures
|
||||||
|
.get(4)
|
||||||
|
.expect("Beacon Y position")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("Input not a number");
|
||||||
|
let manhattan_radius = sensor_x.abs_diff(beacon_x) + sensor_y.abs_diff(beacon_y);
|
||||||
|
(
|
||||||
|
Sensor {
|
||||||
|
x: sensor_x,
|
||||||
|
y: sensor_y,
|
||||||
|
manhattan_radius,
|
||||||
|
},
|
||||||
|
Beacon {
|
||||||
|
x: beacon_x,
|
||||||
|
y: beacon_y,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unzip()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_first_puzzle<const LINE: usize, R: BufRead>(reader: R) -> usize {
|
||||||
|
let (sensors, beacons) = parse_input(reader);
|
||||||
|
let min_x = sensors.iter().map(Sensor::min_x).min().unwrap_or_default();
|
||||||
|
let max_x = sensors.iter().map(Sensor::max_x).max().unwrap_or_default();
|
||||||
|
let mut occupied_counter = 0;
|
||||||
|
let y = LINE as i64;
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
if sensors.iter().any(|sensor| sensor.is_in_range(x, y))
|
||||||
|
&& !beacons.iter().any(|beacon| beacon.x == x && beacon.y == y)
|
||||||
|
{
|
||||||
|
occupied_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
occupied_counter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_second_puzzle<const MAX_X: i64, const MAX_Y: i64, R: BufRead>(reader: R) -> i64 {
|
||||||
|
let (sensors, _beacons) = parse_input(reader);
|
||||||
|
for y in 0_i64..=MAX_Y {
|
||||||
|
let mut x = 0;
|
||||||
|
while x <= MAX_X {
|
||||||
|
let sensor = sensors.iter().find(|sensor| sensor.is_in_range(x, y));
|
||||||
|
match sensor {
|
||||||
|
Some(sensor) => {
|
||||||
|
let dist_y = sensor.y.abs_diff(y) as i64;
|
||||||
|
let width_at_height = sensor.manhattan_radius as i64 - dist_y;
|
||||||
|
x = sensor.x + width_at_height + 1;
|
||||||
|
}
|
||||||
|
// FOUND!
|
||||||
|
None => return x * TUNING_FACTOR + y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("No spot found")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = ::std::env::args().skip(1);
|
||||||
|
let file = args.next().unwrap_or_else(|| String::from("./input"));
|
||||||
|
let solve = args
|
||||||
|
.next()
|
||||||
|
.map(|arg| match arg.as_str() {
|
||||||
|
"first" => SolvePuzzle::First,
|
||||||
|
"second" => SolvePuzzle::Second,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let file = BufReader::new(File::open(file).expect("Opening file"));
|
||||||
|
match solve {
|
||||||
|
SolvePuzzle::First => println!("{}", solve_first_puzzle::<2_000_000, _>(file)),
|
||||||
|
SolvePuzzle::Second => println!("{}", solve_second_puzzle::<4_000_000, 4_000_000, _>(file)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_on_first() {
|
||||||
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
|
assert_eq!(solve_first_puzzle::<10, _>(reader), 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_on_second() {
|
||||||
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
|
assert_eq!(solve_second_puzzle::<20, 20, _>(reader), 56000011);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,6 @@ build = true
|
||||||
app = true
|
app = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cached = "0.40.0"
|
||||||
|
regex = "1.7.0"
|
||||||
|
bitvec_simd = { git = "https://github.com/Imberflur/bitvec_simd", branch = "master" }
|
||||||
|
|
4
day16/input.example
Normal file
4
day16/input.example
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Valve AA has flow rate=0; tunnel leads to valve BB
|
||||||
|
Valve BB has flow rate=3; tunnels lead to valves CC, DD
|
||||||
|
Valve CC has flow rate=18; tunnel leads to valve DD
|
||||||
|
Valve DD has flow rate=20; tunnels lead to valves BB, CC
|
|
@ -1,3 +1,191 @@
|
||||||
fn main() {
|
use std::{
|
||||||
println!("Hello, world!");
|
collections::HashMap,
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bitvec_simd::BitVec;
|
||||||
|
use cached::proc_macro::cached;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
|
enum SolvePuzzle {
|
||||||
|
First,
|
||||||
|
#[default]
|
||||||
|
Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Valve {
|
||||||
|
name: String,
|
||||||
|
flow: u32,
|
||||||
|
leads_to: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Valves {
|
||||||
|
inner: Vec<Valve>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Valves {
|
||||||
|
pub fn start_idx(&self) -> usize {
|
||||||
|
self.inner
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_idx, valve)| valve.name == "AA")
|
||||||
|
.expect("Where to start now?")
|
||||||
|
.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input<R: BufRead>(reader: R) -> Valves {
|
||||||
|
let regex = Regex::new(
|
||||||
|
r"Valve ([A-Z]{2}) has flow rate=(\d+); tunnels? leads? to valves? (([A-Z]{2}(, )?)+)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let raw = reader
|
||||||
|
.lines()
|
||||||
|
.map(Result::unwrap)
|
||||||
|
.map(|line| {
|
||||||
|
let captures = regex.captures(&line).expect("Matched line");
|
||||||
|
let name = captures.get(1).expect("name").as_str().to_string();
|
||||||
|
let rate: u32 = captures
|
||||||
|
.get(2)
|
||||||
|
.expect("flow rate")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("flow rate is a number");
|
||||||
|
let leads_to = captures
|
||||||
|
.get(3)
|
||||||
|
.expect("leads to")
|
||||||
|
.as_str()
|
||||||
|
.split(", ")
|
||||||
|
.map(String::from)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
(name, rate, leads_to)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let indices: HashMap<String, usize> = raw
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, (name, _, _))| (name.clone(), idx))
|
||||||
|
.collect();
|
||||||
|
let inner = raw
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, flow, leads_to)| {
|
||||||
|
let leads_to = leads_to
|
||||||
|
.into_iter()
|
||||||
|
.map(|name| *indices.get(&name).expect("Pipe exists"))
|
||||||
|
.collect();
|
||||||
|
Valve {
|
||||||
|
name,
|
||||||
|
flow,
|
||||||
|
leads_to,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Valves { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cached(
|
||||||
|
key = "(usize, u32, Vec<usize>)",
|
||||||
|
convert = r#"{ (current_valve, time_left, opened.clone().into_usizes()) }"#
|
||||||
|
)]
|
||||||
|
fn solve(valves: &Valves, current_valve: usize, time_left: u32, opened: &mut BitVec) -> u32 {
|
||||||
|
if time_left == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let curr = &valves[current_valve];
|
||||||
|
let should_skip_opening = curr
|
||||||
|
.leads_to
|
||||||
|
.iter()
|
||||||
|
.filter(|&&idx| !opened[idx])
|
||||||
|
.any(|&idx| {
|
||||||
|
let other = &valves[idx];
|
||||||
|
let gain = other.flow as i32 - curr.flow as i32;
|
||||||
|
let min_gain_needed = other.flow as i32;
|
||||||
|
(time_left as i32 - 1) * gain > min_gain_needed
|
||||||
|
});
|
||||||
|
if time_left == 25 {
|
||||||
|
eprintln!("Checking {}: {:>6?}", curr.name, opened);
|
||||||
|
}
|
||||||
|
curr.leads_to
|
||||||
|
.iter()
|
||||||
|
.map(|&idx| {
|
||||||
|
if opened[current_valve] || should_skip_opening {
|
||||||
|
solve(valves, idx, time_left.saturating_sub(1), opened)
|
||||||
|
} else {
|
||||||
|
let if_closed = solve(valves, idx, time_left.saturating_sub(1), opened);
|
||||||
|
opened.set(current_valve, true);
|
||||||
|
let mut if_opened = solve(valves, idx, time_left.saturating_sub(2), opened);
|
||||||
|
opened.set(current_valve, false);
|
||||||
|
if_opened += (time_left - 1) * curr.flow;
|
||||||
|
if if_closed >= if_opened {
|
||||||
|
if_closed
|
||||||
|
} else {
|
||||||
|
if_opened
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// Leads nowhere
|
||||||
|
if opened[current_valve] {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(time_left.saturating_sub(1)) * curr.flow
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_first_puzzle<R: BufRead>(reader: R) -> u32 {
|
||||||
|
let valves = parse_input(reader);
|
||||||
|
let mut opened = BitVec::zeros(valves.len());
|
||||||
|
solve(&valves, valves.start_idx(), 30, &mut opened)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = ::std::env::args().skip(1);
|
||||||
|
let file = args.next().unwrap_or_else(|| String::from("./input"));
|
||||||
|
let solve = args
|
||||||
|
.next()
|
||||||
|
.map(|arg| match arg.as_str() {
|
||||||
|
"first" => SolvePuzzle::First,
|
||||||
|
"second" => SolvePuzzle::Second,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let file = BufReader::new(File::open(file).expect("Opening file"));
|
||||||
|
match solve {
|
||||||
|
SolvePuzzle::First => println!("{}", solve_first_puzzle(file)),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Valves {
|
||||||
|
type Target = Vec<Valve>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_on_first() {
|
||||||
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
|
assert_eq!(solve_first_puzzle(reader), 1651);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_on_second() {
|
||||||
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
|
// todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,33 @@ enum Wind {
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Repetition {
|
||||||
|
from_history_idx: usize,
|
||||||
|
to_history_idx: usize,
|
||||||
|
height_diff: usize,
|
||||||
|
}
|
||||||
|
|
||||||
struct Cave {
|
struct Cave {
|
||||||
height_map: [usize; CAVE_WIDTH],
|
|
||||||
wind: Vec<Wind>,
|
wind: Vec<Wind>,
|
||||||
wind_pos: usize,
|
wind_pos: usize,
|
||||||
|
content: Vec<[bool; CAVE_WIDTH]>,
|
||||||
|
// A list of rocks, their initial wind_pos and final height
|
||||||
|
history: Vec<(StrangeRock, usize, usize)>,
|
||||||
}
|
}
|
||||||
impl Cave {
|
impl Cave {
|
||||||
pub fn max_height(&self) -> usize {
|
pub fn max_height(&self) -> usize {
|
||||||
self.height_map.iter().max().copied().unwrap()
|
if let Some((rev_idx, _)) = self
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_idx, row)| row.iter().any(|&blocked| blocked))
|
||||||
|
{
|
||||||
|
self.content.len() - rev_idx
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_wind(&mut self) -> Wind {
|
pub fn use_wind(&mut self) -> Wind {
|
||||||
|
@ -36,58 +55,104 @@ impl Cave {
|
||||||
wind
|
wind
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop(&mut self, piece: StrangRock) {
|
pub fn drop(&mut self, piece: StrangeRock) {
|
||||||
let mut piece_height = self.max_height() + 4;
|
let initial_wind = self.wind_pos;
|
||||||
|
let piece_positions = piece.positions();
|
||||||
|
let curr_max_height = self.max_height();
|
||||||
|
let mut height = curr_max_height + 3;
|
||||||
let mut pos = 2_usize;
|
let mut pos = 2_usize;
|
||||||
eprintln!("{piece:>10?} {pos} {piece_height}");
|
|
||||||
loop {
|
loop {
|
||||||
match self.use_wind() {
|
match self.use_wind() {
|
||||||
Wind::Left if pos > 0 => {
|
Wind::Left if pos > 0 => {
|
||||||
let mut collision = false;
|
let is_impossible = (height <= curr_max_height)
|
||||||
for (offset_idx, offset) in piece.offsets().iter().enumerate() {
|
&& piece_positions
|
||||||
if self.height_map[pos - 1 + offset_idx] >= piece_height + offset {
|
.iter()
|
||||||
collision = true;
|
.map(|(x, y)| (x + pos - 1, y + height))
|
||||||
}
|
.any(|(x, y)| self.get(x, y));
|
||||||
}
|
if !is_impossible {
|
||||||
if !collision {
|
|
||||||
pos = pos.saturating_sub(1);
|
pos = pos.saturating_sub(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Wind::Right if pos < CAVE_WIDTH.saturating_sub(piece.width()) => {
|
Wind::Right if pos < CAVE_WIDTH.saturating_sub(piece.width()) => {
|
||||||
let mut collision = false;
|
let is_impossible = (height <= curr_max_height)
|
||||||
for (offset_idx, offset) in piece.offsets().iter().enumerate() {
|
&& piece_positions
|
||||||
if self.height_map[pos + 1 + offset_idx] >= piece_height + offset {
|
.iter()
|
||||||
collision = true;
|
.map(|(x, y)| (x + pos + 1, y + height))
|
||||||
}
|
.any(|(x, y)| self.get(x, y));
|
||||||
}
|
if !is_impossible {
|
||||||
if !collision {
|
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
piece_height -= 1;
|
let cant_move_down = height == 0
|
||||||
eprintln!("{piece:>10?} {pos} {piece_height}");
|
|| piece_positions
|
||||||
eprintln!(" - {:>2?} {:>2?}", self.height_map, piece.offsets());
|
.iter()
|
||||||
for (offset_idx, &offset) in piece.offsets().iter().enumerate() {
|
.map(|(x, y)| (x + pos, y + height - 1))
|
||||||
let height_at_pos = self.height_map[pos + offset_idx];
|
.any(|(x, y)| self.get(x, y));
|
||||||
let piece_height = piece_height + offset;
|
if !cant_move_down {
|
||||||
eprintln!(" - {offset_idx} {offset}: {height_at_pos} {piece_height}");
|
height -= 1;
|
||||||
if piece_height == height_at_pos {
|
}
|
||||||
for (height_idx, &height) in piece.height_offsets().iter().enumerate() {
|
if cant_move_down {
|
||||||
let total_height = piece_height + height + 1;
|
piece_positions
|
||||||
self.height_map[pos + height_idx] = total_height;
|
.iter()
|
||||||
}
|
.map(|(x, y)| (x + pos, y + height))
|
||||||
eprintln!(" == {:>2?} {:>2?}", self.height_map, piece.offsets());
|
.for_each(|(x, y)| self.block(x, y));
|
||||||
return;
|
self.history.push((piece, initial_wind, height));
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn min_repeat(&self) -> usize {
|
||||||
|
// Everything has to repeat after visiting every variant with every wind
|
||||||
|
StrangeRock::TOTAL_VARIANT_COUNT * self.wind.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_repetition(&self) -> Option<Repetition> {
|
||||||
|
// println!("IS REPEATING? {:>20}", self.history.len());
|
||||||
|
let min_repeat = self.min_repeat();
|
||||||
|
if self.history.len() <= min_repeat + 429 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let to_history_idx = self.history.len() - 1 - 429;
|
||||||
|
let to = &self.history[to_history_idx];
|
||||||
|
(min_repeat..)
|
||||||
|
.take_while(|&idx_sub| idx_sub < self.history.len())
|
||||||
|
.map(|idx_sub| self.history.len() - idx_sub)
|
||||||
|
.find_map(|from_history_idx| {
|
||||||
|
let from = &self.history[from_history_idx];
|
||||||
|
if to.0 == from.0 && to.1 == from.1 && self.content[to.2] == self.content[from.2] {
|
||||||
|
Some(Repetition {
|
||||||
|
from_history_idx,
|
||||||
|
to_history_idx,
|
||||||
|
height_diff: to.2 - from.2,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, x: usize, y: usize) -> bool {
|
||||||
|
debug_assert!(x < CAVE_WIDTH);
|
||||||
|
while y >= self.content.len() {
|
||||||
|
self.content.push([false; CAVE_WIDTH]);
|
||||||
|
}
|
||||||
|
self.content[y][x]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block(&mut self, x: usize, y: usize) {
|
||||||
|
debug_assert!(x < CAVE_WIDTH);
|
||||||
|
while y >= self.content.len() {
|
||||||
|
self.content.push([false; CAVE_WIDTH]);
|
||||||
|
}
|
||||||
|
self.content[y][x] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum StrangRock {
|
enum StrangeRock {
|
||||||
// ####
|
// ####
|
||||||
Horizontal,
|
Horizontal,
|
||||||
// .#.
|
// .#.
|
||||||
|
@ -108,34 +173,25 @@ enum StrangRock {
|
||||||
Square,
|
Square,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StrangRock {
|
impl StrangeRock {
|
||||||
const fn width(&self) -> usize {
|
pub const TOTAL_VARIANT_COUNT: usize = 5;
|
||||||
|
pub const fn positions(&self) -> &[(usize, usize)] {
|
||||||
match self {
|
match self {
|
||||||
StrangRock::Horizontal => 4,
|
StrangeRock::Horizontal => &[(0, 0), (1, 0), (2, 0), (3, 0)],
|
||||||
StrangRock::Plus => 3,
|
StrangeRock::Plus => &[(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)],
|
||||||
StrangRock::Corner => 3,
|
StrangeRock::Corner => &[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)],
|
||||||
StrangRock::Vertical => 1,
|
StrangeRock::Vertical => &[(0, 0), (0, 1), (0, 2), (0, 3)],
|
||||||
StrangRock::Square => 2,
|
StrangeRock::Square => &[(0, 0), (1, 0), (0, 1), (1, 1)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offsets(&self) -> &[usize] {
|
pub const fn width(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
StrangRock::Horizontal => &[0, 0, 0, 0],
|
StrangeRock::Horizontal => 4,
|
||||||
StrangRock::Plus => &[1, 0, 1],
|
StrangeRock::Plus => 3,
|
||||||
StrangRock::Corner => &[0, 0, 0],
|
StrangeRock::Corner => 3,
|
||||||
StrangRock::Vertical => &[0],
|
StrangeRock::Vertical => 1,
|
||||||
StrangRock::Square => &[0, 0],
|
StrangeRock::Square => 2,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height_offsets(&self) -> &[usize] {
|
|
||||||
match self {
|
|
||||||
StrangRock::Horizontal => &[0, 0, 0, 0],
|
|
||||||
StrangRock::Plus => &[1, 2, 1],
|
|
||||||
StrangRock::Corner => &[0, 0, 2],
|
|
||||||
StrangRock::Vertical => &[3],
|
|
||||||
StrangRock::Square => &[1, 1],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,22 +204,74 @@ fn read_input<R: BufRead>(reader: R) -> Cave {
|
||||||
.map(Wind::from)
|
.map(Wind::from)
|
||||||
.collect();
|
.collect();
|
||||||
let wind_pos = 0;
|
let wind_pos = 0;
|
||||||
let height_map = [0; CAVE_WIDTH];
|
|
||||||
Cave {
|
Cave {
|
||||||
height_map,
|
|
||||||
wind,
|
wind,
|
||||||
wind_pos,
|
wind_pos,
|
||||||
|
content: vec![],
|
||||||
|
history: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
|
fn solve_first_puzzle<const ROCK_COUNT: usize, R: BufRead>(reader: R) -> usize {
|
||||||
let mut cave = read_input(reader);
|
let mut cave = read_input(reader);
|
||||||
use StrangRock::*;
|
println!("{}", cave.min_repeat());
|
||||||
|
use StrangeRock::*;
|
||||||
let pieces = [Horizontal, Plus, Corner, Vertical, Square];
|
let pieces = [Horizontal, Plus, Corner, Vertical, Square];
|
||||||
for piece in pieces.into_iter().cycle().take(2022) {
|
let mut piece_idx = 0;
|
||||||
|
let mut current_idx = 0;
|
||||||
|
let mut additional_height = 0;
|
||||||
|
let mut have_skipped = false;
|
||||||
|
while current_idx < ROCK_COUNT {
|
||||||
|
let piece = pieces[piece_idx];
|
||||||
cave.drop(piece);
|
cave.drop(piece);
|
||||||
|
if !have_skipped {
|
||||||
|
if let Some(repetition) = cave.check_repetition() {
|
||||||
|
eprintln!("{repetition:?}");
|
||||||
|
eprintln!("{:?}", cave.history[repetition.from_history_idx]);
|
||||||
|
eprintln!("{:?}", cave.history[repetition.to_history_idx]);
|
||||||
|
eprintln!(
|
||||||
|
"{:?}",
|
||||||
|
cave.content[cave.history[repetition.from_history_idx].2]
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"{:?}",
|
||||||
|
cave.content[cave.history[repetition.to_history_idx].2]
|
||||||
|
);
|
||||||
|
let number_of_rocks_that_repeated =
|
||||||
|
repetition.to_history_idx - repetition.from_history_idx;
|
||||||
|
let repeat = (ROCK_COUNT - current_idx) / number_of_rocks_that_repeated;
|
||||||
|
eprintln!("XXXX");
|
||||||
|
println!("{number_of_rocks_that_repeated}");
|
||||||
|
for _ in 0..number_of_rocks_that_repeated {
|
||||||
|
piece_idx = (piece_idx + 1) % pieces.len();
|
||||||
|
cave.drop(pieces[piece_idx]);
|
||||||
|
}
|
||||||
|
eprintln!("XXXX");
|
||||||
|
for x in repetition.from_history_idx..repetition.to_history_idx {
|
||||||
|
assert_eq!(
|
||||||
|
cave.content[cave.history[x].2],
|
||||||
|
cave.content[cave.history[x + number_of_rocks_that_repeated].2],
|
||||||
|
"X: {} Y: {}, *X: {}, *Y: {}",
|
||||||
|
x,
|
||||||
|
x + number_of_rocks_that_repeated,
|
||||||
|
cave.history[x].2,
|
||||||
|
cave.history[x + number_of_rocks_that_repeated].2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
eprintln!("XXXX");
|
||||||
|
current_idx += number_of_rocks_that_repeated * repeat;
|
||||||
|
eprintln!("Skip {}", number_of_rocks_that_repeated * repeat);
|
||||||
|
eprintln!("To be simulated: {}", ROCK_COUNT - current_idx);
|
||||||
|
eprintln!("0..{}", cave.content.len());
|
||||||
|
additional_height += repetition.height_diff * repeat;
|
||||||
|
eprintln!("additional height {}", additional_height);
|
||||||
|
have_skipped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
piece_idx = (piece_idx + 1) % pieces.len();
|
||||||
|
current_idx += 1;
|
||||||
}
|
}
|
||||||
cave.max_height()
|
cave.max_height() + additional_height
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -179,8 +287,8 @@ fn main() {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let file = BufReader::new(File::open(file).expect("Opening file"));
|
let file = BufReader::new(File::open(file).expect("Opening file"));
|
||||||
match solve {
|
match solve {
|
||||||
SolvePuzzle::First => println!("{}", solve_first_puzzle(file)),
|
SolvePuzzle::First => println!("{}", solve_first_puzzle::<2022, _>(file)),
|
||||||
_ => todo!(),
|
SolvePuzzle::Second => println!("{}", solve_first_puzzle::<1_000_000_000_000, _>(file)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,12 +311,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn example_on_first() {
|
fn example_on_first() {
|
||||||
let reader = Cursor::new(include_str!("../input.test"));
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
assert_eq!(solve_first_puzzle(reader), 3068);
|
assert_eq!(solve_first_puzzle::<2022, _>(reader), 3068);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn example_on_second() {
|
fn example_on_second() {
|
||||||
let reader = Cursor::new(include_str!("../input.test"));
|
let reader = Cursor::new(include_str!("../input.test"));
|
||||||
// todo!()
|
assert_eq!(
|
||||||
|
solve_first_puzzle::<1000000000000, _>(reader),
|
||||||
|
1514285714288
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue