generated from janezicmatej/aoc-template
Compare commits
42 Commits
00000010fc
...
master
Author | SHA1 | Date | |
---|---|---|---|
0000043002
|
|||
00000420f1
|
|||
000004106a
|
|||
000004006d
|
|||
0000039019
|
|||
0000038000
|
|||
000003704a
|
|||
00000360db
|
|||
00000350d1
|
|||
0000034077
|
|||
0000033086
|
|||
0000032032
|
|||
00000310ba
|
|||
0000030011
|
|||
0000029007
|
|||
00000280ae
|
|||
000002707d
|
|||
0000026098
|
|||
000002508c
|
|||
0000024016
|
|||
000002301f
|
|||
000002200d
|
|||
0000021091
|
|||
000002005e
|
|||
00000190b5
|
|||
00000180c1
|
|||
0000017058
|
|||
00000160a1
|
|||
0000015082
|
|||
00000140d3
|
|||
00000130fa
|
|||
000001200a
|
|||
00000110da
|
|||
0000010044
|
|||
000000906f
|
|||
00000080fc
|
|||
000000701f
|
|||
00000060b5
|
|||
00000050ae
|
|||
000000402b
|
|||
00000030a5
|
|||
000000203e
|
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Check
|
|
||||||
|
|
||||||
on: push
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Check
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: cargo check
|
|
||||||
run: cargo check
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Test
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: cargo test
|
|
||||||
run: cargo test
|
|
||||||
clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Lint (clippy)
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: cargo clippy
|
|
||||||
run: cargo clippy -- -D warnings
|
|
||||||
fmt:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Format
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: cargo fmt
|
|
||||||
run: cargo fmt --check
|
|
197
Cargo.lock
generated
197
Cargo.lock
generated
@@ -17,9 +17,21 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc"
|
name = "aoc"
|
||||||
version = "46.0.0"
|
version = "46.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"z3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
@@ -48,12 +60,38 @@ version = "0.21.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.66.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"peeking_take_while",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn 2.0.42",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
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 = "bitflags"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.11.1"
|
version = "3.11.1"
|
||||||
@@ -72,12 +110,41 @@ version = "1.0.78"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -211,6 +278,12 @@ version = "0.28.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
@@ -372,12 +445,28 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.150"
|
version = "0.2.150"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.17"
|
||||||
@@ -399,6 +488,12 @@ version = "0.3.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -437,6 +532,16 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -468,7 +573,7 @@ version = "0.10.45"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
|
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -485,7 +590,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -507,6 +612,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peeking_take_while"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -561,9 +672,38 @@ version = "0.2.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -617,6 +757,12 @@ version = "0.1.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -648,7 +794,7 @@ version = "2.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -694,6 +840,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -734,13 +886,24 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"system-configuration-sys",
|
"system-configuration-sys",
|
||||||
]
|
]
|
||||||
@@ -931,7 +1094,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.107",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -965,7 +1128,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.107",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@@ -1126,3 +1289,23 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "z3"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a7ff5718c079e7b813378d67a5bed32ccc2086f151d6185074a7e24f4a565e8"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"z3-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "z3-sys"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7cf70fdbc0de3f42b404f49b0d4686a82562254ea29ff0a155eef2f5430f4b0"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cmake",
|
||||||
|
]
|
||||||
|
@@ -21,4 +21,6 @@ authors.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# so much for no dependencies this year, but I really cba so here it is
|
||||||
|
z3 = { version = "0.12.1", features = [ "static-link-z3" ] }
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||

|
|
||||||
# Advent-of-Code 2023
|
# Advent-of-Code 2023
|
||||||
*This is a dumbed down version of [fspoettel/advent-of-code-rust](https://github.com/fspoettel/advent-of-code-rust) with some extra features*
|
*This is a dumbed down version of [fspoettel/advent-of-code-rust](https://github.com/fspoettel/advent-of-code-rust) with some extra features*
|
||||||
|
|
||||||
|
10
data/examples/01.txt
Normal file
10
data/examples/01.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
1abc2
|
||||||
|
pqr3stu8vwx
|
||||||
|
a1b2c3d4e5f
|
||||||
|
treb7uchet
|
||||||
|
two1nine
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen
|
6
data/examples/02.txt
Normal file
6
data/examples/02.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
||||||
|
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
||||||
|
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
||||||
|
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
||||||
|
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|
||||||
|
|
10
data/examples/03.txt
Normal file
10
data/examples/03.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
6
data/examples/04.txt
Normal file
6
data/examples/04.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
33
data/examples/05.txt
Normal file
33
data/examples/05.txt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4
|
2
data/examples/06.txt
Normal file
2
data/examples/06.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
5
data/examples/07.txt
Normal file
5
data/examples/07.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
5
data/examples/08-1.txt
Normal file
5
data/examples/08-1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
LLR
|
||||||
|
|
||||||
|
AAA = (BBB, BBB)
|
||||||
|
BBB = (AAA, ZZZ)
|
||||||
|
ZZZ = (ZZZ, ZZZ)
|
10
data/examples/08-2.txt
Normal file
10
data/examples/08-2.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
LR
|
||||||
|
|
||||||
|
11A = (11B, XXX)
|
||||||
|
11B = (XXX, 11Z)
|
||||||
|
11Z = (11B, XXX)
|
||||||
|
22A = (22B, XXX)
|
||||||
|
22B = (22C, 22C)
|
||||||
|
22C = (22Z, 22Z)
|
||||||
|
22Z = (22B, 22B)
|
||||||
|
XXX = (XXX, XXX)
|
3
data/examples/09.txt
Normal file
3
data/examples/09.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
0 3 6 9 12 15
|
||||||
|
1 3 6 10 15 21
|
||||||
|
10 13 16 21 30 45
|
5
data/examples/10-1.txt
Normal file
5
data/examples/10-1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
..F7.
|
||||||
|
.FJ|.
|
||||||
|
SJ.L7
|
||||||
|
|F--J
|
||||||
|
LJ...
|
10
data/examples/10-2.txt
Normal file
10
data/examples/10-2.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FF7FSF7F7F7F7F7F---7
|
||||||
|
L|LJ||||||||||||F--J
|
||||||
|
FL-7LJLJ||||||LJL-77
|
||||||
|
F--JF--7||LJLJ7F7FJ-
|
||||||
|
L---JF-JLJ.||-FJLJJ7
|
||||||
|
|F|F-JF---7F7-L7L|7|
|
||||||
|
|FFJF7L7F-JF7|JL---7
|
||||||
|
7-L-JL7||F7|L7F-7F7|
|
||||||
|
L.L7LFJ|||||FJL7||LJ
|
||||||
|
L7JLJL-JLJLJL--JLJ.L
|
10
data/examples/11.txt
Normal file
10
data/examples/11.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....
|
6
data/examples/12.txt
Normal file
6
data/examples/12.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
15
data/examples/13.txt
Normal file
15
data/examples/13.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
10
data/examples/14.txt
Normal file
10
data/examples/14.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....
|
1
data/examples/15.txt
Normal file
1
data/examples/15.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
|
10
data/examples/16.txt
Normal file
10
data/examples/16.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
13
data/examples/17.txt
Normal file
13
data/examples/17.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
14
data/examples/18.txt
Normal file
14
data/examples/18.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
R 6 (#70c710)
|
||||||
|
D 5 (#0dc571)
|
||||||
|
L 2 (#5713f0)
|
||||||
|
D 2 (#d2c081)
|
||||||
|
R 2 (#59c680)
|
||||||
|
D 2 (#411b91)
|
||||||
|
L 5 (#8ceee2)
|
||||||
|
U 2 (#caa173)
|
||||||
|
L 1 (#1b58a2)
|
||||||
|
U 2 (#caa171)
|
||||||
|
R 2 (#7807d2)
|
||||||
|
U 3 (#a77fa3)
|
||||||
|
L 2 (#015232)
|
||||||
|
U 2 (#7a21e3)
|
17
data/examples/19.txt
Normal file
17
data/examples/19.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
px{a<2006:qkq,m>2090:A,rfg}
|
||||||
|
pv{a>1716:R,A}
|
||||||
|
lnx{m>1548:A,A}
|
||||||
|
rfg{s<537:gd,x>2440:R,A}
|
||||||
|
qs{s>3448:A,lnx}
|
||||||
|
qkq{x<1416:A,crn}
|
||||||
|
crn{x>2662:A,R}
|
||||||
|
in{s<1351:px,qqz}
|
||||||
|
qqz{s>2770:qs,m<1801:hdj,R}
|
||||||
|
gd{a>3333:R,R}
|
||||||
|
hdj{m>838:A,pv}
|
||||||
|
|
||||||
|
{x=787,m=2655,a=1222,s=2876}
|
||||||
|
{x=1679,m=44,a=2067,s=496}
|
||||||
|
{x=2036,m=264,a=79,s=2244}
|
||||||
|
{x=2461,m=1339,a=466,s=291}
|
||||||
|
{x=2127,m=1623,a=2188,s=1013}
|
5
data/examples/20-1.txt
Normal file
5
data/examples/20-1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
broadcaster -> a, b, c
|
||||||
|
%a -> b
|
||||||
|
%b -> c
|
||||||
|
%c -> inv
|
||||||
|
&inv -> a
|
5
data/examples/20-2.txt
Normal file
5
data/examples/20-2.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> output
|
11
data/examples/21.txt
Normal file
11
data/examples/21.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
...........
|
||||||
|
.....###.#.
|
||||||
|
.###.##..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.##..S####.
|
||||||
|
.##..#...#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##..##.##.
|
||||||
|
...........
|
7
data/examples/22.txt
Normal file
7
data/examples/22.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
1,0,1~1,2,1
|
||||||
|
0,0,2~2,0,2
|
||||||
|
0,2,3~2,2,3
|
||||||
|
0,0,4~0,2,4
|
||||||
|
2,0,5~2,2,5
|
||||||
|
0,1,6~2,1,6
|
||||||
|
1,1,9~1,1,8
|
23
data/examples/23.txt
Normal file
23
data/examples/23.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#
|
5
data/examples/24.txt
Normal file
5
data/examples/24.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
19, 13, 30 @ -2, 1, -2
|
||||||
|
18, 19, 22 @ -1, -1, -2
|
||||||
|
20, 25, 34 @ -2, -2, -4
|
||||||
|
12, 31, 28 @ -1, -2, -1
|
||||||
|
20, 19, 15 @ 1, -5, -3
|
13
data/examples/25.txt
Normal file
13
data/examples/25.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
jqt: rhn xhk nvd
|
||||||
|
rsh: frs pzl lsr
|
||||||
|
xhk: hfx
|
||||||
|
cmg: qnr nvd lhk bvb
|
||||||
|
rhn: xhk bvb hfx
|
||||||
|
bvb: xhk hfx
|
||||||
|
pzl: lsr hfx nvd
|
||||||
|
qnr: nvd
|
||||||
|
ntq: jqt hfx bvb xhk
|
||||||
|
nvd: lhk
|
||||||
|
lsr: lhk
|
||||||
|
rzs: qnr cmg lsr rsh
|
||||||
|
frs: qnr lhk lsr
|
68
src/bin/01.rs
Normal file
68
src/bin/01.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let mut c = 0;
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut itr = line.chars().filter(char::is_ascii_digit);
|
||||||
|
|
||||||
|
let first = itr.next().unwrap();
|
||||||
|
let last = itr.last().unwrap_or(first);
|
||||||
|
|
||||||
|
c += format!("{first}{last}").parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUMS: [&str; 9] = [
|
||||||
|
"one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
// NOTE:(matej) this solution is O(n^2) since we search string for each substring separately
|
||||||
|
|
||||||
|
let mut c = 0;
|
||||||
|
let nums: Vec<(u32, String)> = (1..=9).zip(NUMS.into_iter().map(String::from)).collect();
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let mut all_matches = Vec::new();
|
||||||
|
|
||||||
|
for (n, str_n) in nums.iter() {
|
||||||
|
all_matches.extend(line.match_indices(str_n).map(|(x, _)| (x, *n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
all_matches.extend(
|
||||||
|
line.chars()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, c)| c.is_ascii_digit())
|
||||||
|
.map(|(idx, c)| (idx, c.to_digit(10).unwrap())),
|
||||||
|
);
|
||||||
|
|
||||||
|
let first = all_matches.iter().min_by_key(|&(idx, _)| idx).unwrap().1;
|
||||||
|
let last = all_matches.iter().max_by_key(|&(idx, _)| idx).unwrap().1;
|
||||||
|
|
||||||
|
c += first * 10 + last;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(1);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 1)),
|
||||||
|
Some(351)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 1)),
|
||||||
|
Some(340)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
84
src/bin/02.rs
Normal file
84
src/bin/02.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use aoc::parsers::to_vec;
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
id: u32,
|
||||||
|
balls: Vec<(u32, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Game {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let rest = s.strip_prefix("Game ").unwrap();
|
||||||
|
let (id, rest) = rest.split_once(':').unwrap();
|
||||||
|
let id = id.parse().unwrap();
|
||||||
|
|
||||||
|
let balls = rest
|
||||||
|
.split([',', ';'])
|
||||||
|
.map(|x| x.strip_prefix(' ').unwrap().split_once(' ').unwrap())
|
||||||
|
.map(|(n, c)| (n.parse::<u32>().unwrap(), c.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Game { id, balls })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let games: Vec<Game> = to_vec(input, '\n');
|
||||||
|
let rules = [(12, "red"), (13, "green"), (14, "blue")];
|
||||||
|
|
||||||
|
Some(
|
||||||
|
games
|
||||||
|
.iter()
|
||||||
|
.filter(|g| {
|
||||||
|
!g.balls
|
||||||
|
.iter()
|
||||||
|
.any(|(n, c)| rules.iter().any(|(rn, rc)| c == rc && n > rn))
|
||||||
|
})
|
||||||
|
.map(|g| g.id)
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
let games: Vec<Game> = to_vec(input, '\n');
|
||||||
|
|
||||||
|
let mut total_power = 0;
|
||||||
|
|
||||||
|
for g in games {
|
||||||
|
let power: u32 = ["blue", "red", "green"]
|
||||||
|
.iter()
|
||||||
|
.map(|&c| {
|
||||||
|
g.balls
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, cd)| c == cd.as_str())
|
||||||
|
.map(|(n, _)| n)
|
||||||
|
.max()
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.product();
|
||||||
|
|
||||||
|
total_power += power;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(total_power)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(2);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 2)), Some(8));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 2)),
|
||||||
|
Some(2286)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
104
src/bin/03.rs
Normal file
104
src/bin/03.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
struct MappedPart {
|
||||||
|
number: u32,
|
||||||
|
line: usize,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MappedPart {
|
||||||
|
fn is_adjacent(&self, line: usize, column: usize) -> bool {
|
||||||
|
self.line.abs_diff(line) <= 1
|
||||||
|
&& (self.start.abs_diff(column) <= 1 || self.end.abs_diff(column) <= 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_map(input: &str) -> Vec<MappedPart> {
|
||||||
|
let mut numbers = Vec::new();
|
||||||
|
for (idl, line) in input.lines().enumerate() {
|
||||||
|
let mut index = 0;
|
||||||
|
for n in line.split(|c: char| !c.is_ascii_digit()) {
|
||||||
|
if n.is_empty() {
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let num: u32 = n.parse().unwrap();
|
||||||
|
let num_len = n.len();
|
||||||
|
|
||||||
|
numbers.push(MappedPart {
|
||||||
|
number: num,
|
||||||
|
line: idl,
|
||||||
|
start: index,
|
||||||
|
end: index + num_len - 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
index += num_len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let numbers = build_map(input);
|
||||||
|
|
||||||
|
let mut part_numbers = 0;
|
||||||
|
|
||||||
|
for (idl, line) in input.lines().enumerate() {
|
||||||
|
for (idc, _) in line
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, c)| !(*c == '.' || c.is_ascii_digit()))
|
||||||
|
{
|
||||||
|
part_numbers += numbers
|
||||||
|
.iter()
|
||||||
|
.filter(|mp| mp.is_adjacent(idl, idc))
|
||||||
|
.map(|mp| mp.number)
|
||||||
|
.sum::<u32>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(part_numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
let numbers = build_map(input);
|
||||||
|
|
||||||
|
let mut gear_ratios = 0;
|
||||||
|
|
||||||
|
for (idl, line) in input.lines().enumerate() {
|
||||||
|
for (idc, _) in line.chars().enumerate().filter(|(_, c)| *c == '*') {
|
||||||
|
let touching: Vec<_> = numbers
|
||||||
|
.iter()
|
||||||
|
.filter(|mp| mp.is_adjacent(idl, idc))
|
||||||
|
.map(|mp| mp.number)
|
||||||
|
.collect();
|
||||||
|
if touching.len() == 2 {
|
||||||
|
gear_ratios += touching.iter().product::<u32>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(gear_ratios)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(3);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 3)),
|
||||||
|
Some(4361)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 3)),
|
||||||
|
Some(467835)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
79
src/bin/04.rs
Normal file
79
src/bin/04.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use aoc::parsers::to_vec;
|
||||||
|
|
||||||
|
struct ParseCardError;
|
||||||
|
|
||||||
|
struct Card {
|
||||||
|
winning: Vec<u32>,
|
||||||
|
numbers: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Card {
|
||||||
|
type Err = ParseCardError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (_, rest) = s.split_once(": ").unwrap();
|
||||||
|
let (win, my) = rest.split_once(" | ").unwrap();
|
||||||
|
|
||||||
|
let winning: Vec<u32> = to_vec(win, ' ');
|
||||||
|
let numbers: Vec<u32> = to_vec(my, ' ');
|
||||||
|
|
||||||
|
Ok(Card { winning, numbers })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Card {
|
||||||
|
fn n_matches(&self) -> usize {
|
||||||
|
self.numbers
|
||||||
|
.iter()
|
||||||
|
.filter(|n| self.winning.contains(n))
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
fn score(&self) -> u32 {
|
||||||
|
let c = self.n_matches();
|
||||||
|
|
||||||
|
if c == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
2_u32.pow((self.n_matches() - 1) as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
Some(
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.parse::<Card>().ok())
|
||||||
|
.map(|g| g.score())
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
let cards: Vec<Card> = to_vec(input, '\n');
|
||||||
|
let mut multiples = vec![1; cards.len()];
|
||||||
|
|
||||||
|
for (i, card) in cards.iter().enumerate() {
|
||||||
|
for j in 0..card.n_matches() {
|
||||||
|
multiples[i + j + 1] += multiples[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(multiples.iter().sum::<u32>())
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(4);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 4)), Some(13));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 4)), Some(30));
|
||||||
|
}
|
||||||
|
}
|
170
src/bin/05.rs
Normal file
170
src/bin/05.rs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
fmt::Debug,
|
||||||
|
ops::RangeInclusive,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use aoc::parsers::to_vec;
|
||||||
|
|
||||||
|
trait RangeInclusiveExt {
|
||||||
|
fn overlaps(&self, other: &Self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RangeInclusiveExt for RangeInclusive<T>
|
||||||
|
where
|
||||||
|
T: PartialOrd,
|
||||||
|
{
|
||||||
|
fn overlaps(&self, other: &Self) -> bool {
|
||||||
|
self.contains(other.start()) || self.contains(other.end())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_range(start: u64, range: u64) -> RangeInclusive<u64> {
|
||||||
|
start..=(start + range - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Mapping {
|
||||||
|
pub source: RangeInclusive<u64>,
|
||||||
|
pub destination: RangeInclusive<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParseMappingError;
|
||||||
|
|
||||||
|
impl FromStr for Mapping {
|
||||||
|
type Err = ParseMappingError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let nums: Vec<u64> = to_vec(s, ' ');
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
destination: build_range(nums[0], nums[2]),
|
||||||
|
source: build_range(nums[1], nums[2]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mapping {
|
||||||
|
fn map(&self, n: u64) -> u64 {
|
||||||
|
let shift = n - self.source.start();
|
||||||
|
self.destination.start() + shift
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_range(&self, r: RangeInclusive<u64>) -> RangeInclusive<u64> {
|
||||||
|
self.map(*r.start())..=(self.map(*r.end()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_range(&self, r: RangeInclusive<u64>) -> [Option<RangeInclusive<u64>>; 3] {
|
||||||
|
let mut fences = [
|
||||||
|
*r.start(),
|
||||||
|
*r.end() + 1,
|
||||||
|
max(*self.source.start(), *r.start()),
|
||||||
|
min(*self.source.end() + 1, *r.end() + 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
fences.sort();
|
||||||
|
|
||||||
|
const ARRAY_REPEAT_VALUE: Option<RangeInclusive<u64>> = None;
|
||||||
|
let mut v = [ARRAY_REPEAT_VALUE; 3];
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
let f = fences[i];
|
||||||
|
let nf = fences[i + 1];
|
||||||
|
if f != nf {
|
||||||
|
v[i] = Some(f..=(nf - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map(maps: &str) -> Vec<Vec<Mapping>> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
|
||||||
|
for mapper in maps.split("\n\n") {
|
||||||
|
res.push(
|
||||||
|
mapper
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.filter_map(|m| m.parse().ok())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
let (first, rest) = input.split_once("\n\n").unwrap();
|
||||||
|
let maps = parse_map(rest);
|
||||||
|
let seeds = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ');
|
||||||
|
|
||||||
|
let mut m = u64::MAX;
|
||||||
|
|
||||||
|
for seed in seeds.iter() {
|
||||||
|
let mut s = *seed;
|
||||||
|
'maps: for map in maps.iter() {
|
||||||
|
for inner in map.iter() {
|
||||||
|
if inner.source.contains(&s) {
|
||||||
|
s = inner.map(s);
|
||||||
|
continue 'maps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m = min(m, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
let (first, rest) = input.split_once("\n\n").unwrap();
|
||||||
|
let maps = parse_map(rest);
|
||||||
|
let mut seeds_ranges: Vec<_> = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ')
|
||||||
|
.chunks(2)
|
||||||
|
.map(|x| build_range(x[0], x[1]))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for mapping in maps.iter() {
|
||||||
|
let mut new_ranges = Vec::new();
|
||||||
|
|
||||||
|
'queue: while let Some(rng) = seeds_ranges.pop() {
|
||||||
|
for map in mapping {
|
||||||
|
if map.source.overlaps(&rng) {
|
||||||
|
let [pre, to_map, post] = map.split_range(rng);
|
||||||
|
new_ranges.push(map.map_range(to_map.unwrap()));
|
||||||
|
for r in [pre, post].into_iter().flatten() {
|
||||||
|
seeds_ranges.push(r);
|
||||||
|
}
|
||||||
|
continue 'queue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_ranges.push(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
seeds_ranges = new_ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
seeds_ranges
|
||||||
|
.iter()
|
||||||
|
.map(RangeInclusive::start)
|
||||||
|
.min()
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(5);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 5)), Some(35));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 5)), Some(46));
|
||||||
|
}
|
||||||
|
}
|
67
src/bin/06.rs
Normal file
67
src/bin/06.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
fn win_options((time, distance): (u64, u64)) -> u64 {
|
||||||
|
let discriminant = ((time.pow(2) - 4 * distance) as f64).sqrt();
|
||||||
|
|
||||||
|
let first_zero = (time as f64 - discriminant) / 2.0;
|
||||||
|
let mut second_zero = (time as f64 + discriminant) / 2.0;
|
||||||
|
|
||||||
|
if second_zero.fract() == 0.0 {
|
||||||
|
second_zero -= 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
second_zero as u64 - first_zero as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
let [time, distance] = input
|
||||||
|
.lines()
|
||||||
|
.map(|l| {
|
||||||
|
l.split_whitespace()
|
||||||
|
.skip(1)
|
||||||
|
.filter_map(|n| n.parse().ok())
|
||||||
|
.collect::<Vec<u64>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some(time.into_iter().zip(distance).map(win_options).product())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
let [time, distance] = input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| {
|
||||||
|
l.split_whitespace()
|
||||||
|
.skip(1)
|
||||||
|
.flat_map(|x| x.chars())
|
||||||
|
.collect::<String>()
|
||||||
|
.parse()
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some(win_options((time, distance)))
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(6);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 6)),
|
||||||
|
Some(288)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 6)),
|
||||||
|
Some(71503)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
191
src/bin/07.rs
Normal file
191
src/bin/07.rs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
use std::{cmp::Ordering, collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
|
||||||
|
enum Label {
|
||||||
|
Ace = 14,
|
||||||
|
King = 13,
|
||||||
|
Queen = 12,
|
||||||
|
Jack = 11,
|
||||||
|
Ten = 10,
|
||||||
|
Nine = 9,
|
||||||
|
Eight = 8,
|
||||||
|
Seven = 7,
|
||||||
|
Six = 6,
|
||||||
|
Five = 5,
|
||||||
|
Four = 4,
|
||||||
|
Three = 3,
|
||||||
|
Two = 2,
|
||||||
|
Joker = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Label {
|
||||||
|
fn from(value: char) -> Self {
|
||||||
|
use Label::*;
|
||||||
|
match value {
|
||||||
|
'A' => Ace,
|
||||||
|
'K' => King,
|
||||||
|
'Q' => Queen,
|
||||||
|
'J' => Jack,
|
||||||
|
'T' => Ten,
|
||||||
|
'9' => Nine,
|
||||||
|
'8' => Eight,
|
||||||
|
'7' => Seven,
|
||||||
|
'6' => Six,
|
||||||
|
'5' => Five,
|
||||||
|
'4' => Four,
|
||||||
|
'3' => Three,
|
||||||
|
'2' => Two,
|
||||||
|
'X' => Joker,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseHandError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Hand {
|
||||||
|
labels: [Label; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Hand {
|
||||||
|
type Err = ParseHandError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let labels = s
|
||||||
|
.chars()
|
||||||
|
.map(Label::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| ParseHandError)?;
|
||||||
|
Ok(Hand { labels })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Hand {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Hand {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let sf = HandType::from(*self);
|
||||||
|
let so = HandType::from(*other);
|
||||||
|
|
||||||
|
sf.cmp(&so).then({
|
||||||
|
let mut c = Ordering::Equal;
|
||||||
|
for i in 0..5 {
|
||||||
|
c = self.labels[i].cmp(&other.labels[i]);
|
||||||
|
if c != Ordering::Equal {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum HandType {
|
||||||
|
Five = 6,
|
||||||
|
Four = 5,
|
||||||
|
FullHouse = 4,
|
||||||
|
Three = 3,
|
||||||
|
TwoPair = 2,
|
||||||
|
Pair = 1,
|
||||||
|
HighCard = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hand> for HandType {
|
||||||
|
fn from(value: Hand) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
for c in value.labels {
|
||||||
|
*map.entry(c).or_insert(0) += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let joker = map.remove(&Label::Joker).unwrap_or(0);
|
||||||
|
|
||||||
|
let is_n_k = |n| joker == n || map.values().filter(|&x| *x == n - joker).count() > 0;
|
||||||
|
|
||||||
|
if is_n_k(5) {
|
||||||
|
return Self::Five;
|
||||||
|
}
|
||||||
|
if is_n_k(4) {
|
||||||
|
return Self::Four;
|
||||||
|
}
|
||||||
|
// full house
|
||||||
|
if map.values().count() <= 2 {
|
||||||
|
return Self::FullHouse;
|
||||||
|
}
|
||||||
|
if is_n_k(3) {
|
||||||
|
return Self::Three;
|
||||||
|
}
|
||||||
|
// two pair
|
||||||
|
if map.values().count() <= 3 {
|
||||||
|
return Self::TwoPair;
|
||||||
|
}
|
||||||
|
if is_n_k(2) {
|
||||||
|
return Self::Pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::HighCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let mut v = input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.split_once(' '))
|
||||||
|
.map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
v.sort_by(|f, o| f.0.cmp(&o.0));
|
||||||
|
|
||||||
|
Some(
|
||||||
|
v.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| (i as u32 + 1) * x.1)
|
||||||
|
.sum::<u32>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
let mut v = input
|
||||||
|
.replace('J', "X")
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.split_once(' '))
|
||||||
|
.map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
v.sort_by(|f, o| f.0.cmp(&o.0));
|
||||||
|
|
||||||
|
Some(
|
||||||
|
v.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| (i as u32 + 1) * x.1)
|
||||||
|
.sum::<u32>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(7);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 7)),
|
||||||
|
Some(6440)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 7)),
|
||||||
|
Some(5905)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
95
src/bin/08.rs
Normal file
95
src/bin/08.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use aoc::lcm;
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
let (directions, map_data) = input.split_once("\n\n")?;
|
||||||
|
for line in map_data.lines() {
|
||||||
|
let (k, v) = line.split_once(" = ")?;
|
||||||
|
let v = v.strip_prefix('(')?;
|
||||||
|
let v = v.strip_suffix(')')?;
|
||||||
|
let (l, r) = v.split_once(", ")?;
|
||||||
|
|
||||||
|
*map.entry(k).or_default() = (l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
let mut loc = "AAA";
|
||||||
|
|
||||||
|
for d in directions.chars().cycle() {
|
||||||
|
if loc == "ZZZ" {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1;
|
||||||
|
let (l, r) = map[loc];
|
||||||
|
|
||||||
|
match d {
|
||||||
|
'L' => loc = l,
|
||||||
|
'R' => loc = r,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
let (directions, map_data) = input.split_once("\n\n")?;
|
||||||
|
for line in map_data.lines() {
|
||||||
|
let (k, v) = line.split_once(" = ")?;
|
||||||
|
let v = v.strip_prefix('(')?;
|
||||||
|
let v = v.strip_suffix(')')?;
|
||||||
|
let (l, r) = v.split_once(", ")?;
|
||||||
|
|
||||||
|
*map.entry(k).or_default() = (l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = 1;
|
||||||
|
|
||||||
|
for k in map.keys().filter(|x| x.ends_with('A')) {
|
||||||
|
let mut location = *k;
|
||||||
|
|
||||||
|
for (i, d) in directions.chars().cycle().enumerate() {
|
||||||
|
let (l, r) = map[location];
|
||||||
|
|
||||||
|
location = match d {
|
||||||
|
'L' => l,
|
||||||
|
'R' => r,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if location.ends_with('Z') {
|
||||||
|
res = lcm(res, i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(res as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(8);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file_part("examples", 8, 1)),
|
||||||
|
Some(6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file_part("examples", 8, 2)),
|
||||||
|
Some(6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
90
src/bin/09.rs
Normal file
90
src/bin/09.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
fn find_history(v: VecDeque<i32>) -> VecDeque<VecDeque<i32>> {
|
||||||
|
let mut s = VecDeque::new();
|
||||||
|
s.push_back(v);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut all_zeros = true;
|
||||||
|
|
||||||
|
let last = s.back().unwrap();
|
||||||
|
let mut new = VecDeque::new();
|
||||||
|
for i in 0..(last.len() - 1) {
|
||||||
|
let diff = last[i + 1] - last[i];
|
||||||
|
if diff != 0 {
|
||||||
|
all_zeros = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new.push_back(diff);
|
||||||
|
}
|
||||||
|
s.push_back(new);
|
||||||
|
|
||||||
|
if all_zeros {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extrapolate_forward(v: VecDeque<i32>) -> i32 {
|
||||||
|
let mut s = find_history(v);
|
||||||
|
|
||||||
|
for i in (1..s.len()).rev() {
|
||||||
|
let adder = *s[i].back().unwrap();
|
||||||
|
let last = *s[i - 1].back().unwrap();
|
||||||
|
s[i - 1].push_back(last + adder);
|
||||||
|
}
|
||||||
|
|
||||||
|
*s[0].back().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extrapolate_back(v: VecDeque<i32>) -> i32 {
|
||||||
|
let mut s = find_history(v);
|
||||||
|
|
||||||
|
for i in (1..s.len()).rev() {
|
||||||
|
let adder = *s[i].front().unwrap();
|
||||||
|
let first = *s[i - 1].front().unwrap();
|
||||||
|
s[i - 1].push_front(first - adder);
|
||||||
|
}
|
||||||
|
|
||||||
|
*s[0].front().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<i32> {
|
||||||
|
Some(
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.split(' ').filter_map(|y| y.parse().ok()).collect())
|
||||||
|
.map(extrapolate_forward)
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<i32> {
|
||||||
|
Some(
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.split(' ').filter_map(|y| y.parse().ok()).collect())
|
||||||
|
.map(extrapolate_back)
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(9);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 9)),
|
||||||
|
Some(114)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 9)), Some(2));
|
||||||
|
}
|
||||||
|
}
|
158
src/bin/10.rs
Normal file
158
src/bin/10.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
const DIRS: [(isize, isize); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)];
|
||||||
|
|
||||||
|
fn next_move(dy: isize, dx: isize, c: char) -> Option<(isize, isize)> {
|
||||||
|
Some(match (dy, dx) {
|
||||||
|
(1, 0) => match c {
|
||||||
|
'|' => (1, 0),
|
||||||
|
'J' => (0, -1),
|
||||||
|
'L' => (0, 1),
|
||||||
|
_ => None?,
|
||||||
|
},
|
||||||
|
(0, 1) => match c {
|
||||||
|
'-' => (0, 1),
|
||||||
|
'7' => (1, 0),
|
||||||
|
'J' => (-1, 0),
|
||||||
|
_ => None?,
|
||||||
|
},
|
||||||
|
(-1, 0) => match c {
|
||||||
|
'|' => (-1, 0),
|
||||||
|
'7' => (0, -1),
|
||||||
|
'F' => (0, 1),
|
||||||
|
_ => None?,
|
||||||
|
},
|
||||||
|
(0, -1) => match c {
|
||||||
|
'-' => (0, -1),
|
||||||
|
'F' => (1, 0),
|
||||||
|
'L' => (-1, 0),
|
||||||
|
_ => None?,
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> (Vec<Vec<char>>, (isize, isize)) {
|
||||||
|
let pipes: Vec<Vec<char>> = input.lines().map(|x| x.chars().collect()).collect();
|
||||||
|
|
||||||
|
let start = pipes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, x)| {
|
||||||
|
x.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(j, y)| (i, j, y))
|
||||||
|
.find(|(_, _, &x)| x == 'S')
|
||||||
|
})
|
||||||
|
.map(|(j, i, _)| (j as isize, i as isize))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(pipes, start)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let (pipes, start) = parse_input(input);
|
||||||
|
|
||||||
|
'start_dir: for d in DIRS {
|
||||||
|
let (mut dy, mut dx) = d;
|
||||||
|
let (mut ly, mut lx) = (start.0 + d.0, start.1 + d.1);
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
while start != (ly, lx) {
|
||||||
|
let p = match pipes.get(ly as usize).and_then(|x| x.get(lx as usize)) {
|
||||||
|
Some(x) => *x,
|
||||||
|
None => continue 'start_dir,
|
||||||
|
};
|
||||||
|
|
||||||
|
match next_move(dy, dx, p) {
|
||||||
|
Some((ndy, ndx)) => (dy, dx) = (ndy, ndx),
|
||||||
|
None => continue 'start_dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1;
|
||||||
|
(ly, lx) = (ly + dy, lx + dx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some((counter + 1) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
let (pipes, start) = parse_input(input);
|
||||||
|
|
||||||
|
let mut border = HashSet::with_capacity(pipes.len() * pipes[0].len());
|
||||||
|
|
||||||
|
'start_dir: for d in DIRS {
|
||||||
|
border.clear();
|
||||||
|
border.insert(start);
|
||||||
|
|
||||||
|
let (mut dy, mut dx) = d;
|
||||||
|
let (mut ly, mut lx) = (start.0 + d.0, start.1 + d.1);
|
||||||
|
|
||||||
|
while start != (ly, lx) {
|
||||||
|
let p = match pipes.get(ly as usize).and_then(|x| x.get(lx as usize)) {
|
||||||
|
Some(x) => *x,
|
||||||
|
None => continue 'start_dir,
|
||||||
|
};
|
||||||
|
|
||||||
|
match next_move(dy, dx, p) {
|
||||||
|
Some((ndx, ndy)) => {
|
||||||
|
(dy, dx) = (ndx, ndy);
|
||||||
|
border.insert((ly, lx));
|
||||||
|
}
|
||||||
|
None => continue 'start_dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
(ly, lx) = (ly + dy, lx + dx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
for (sy, sx) in (1..pipes.len())
|
||||||
|
.map(|x| (x, 0))
|
||||||
|
.chain((0..pipes[0].len()).map(|x| (0, x)))
|
||||||
|
{
|
||||||
|
let mut inside = false;
|
||||||
|
for range in 0.. {
|
||||||
|
if let Some(c) = pipes.get(sy + range).and_then(|x| x.get(sx + range)) {
|
||||||
|
let is_border = border.contains(&((sy + range) as isize, (sx + range) as isize));
|
||||||
|
if is_border && !['7', 'L'].contains(c) {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
if !is_border && inside {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(10);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file_part("examples", 10, 1)),
|
||||||
|
Some(8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file_part("examples", 10, 2)),
|
||||||
|
Some(10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
74
src/bin/11.rs
Normal file
74
src/bin/11.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> (Vec<(usize, usize)>, HashSet<usize>, HashSet<usize>) {
|
||||||
|
let mut galaxies = Vec::new();
|
||||||
|
let mut rows = HashSet::from_iter(0..input.lines().count());
|
||||||
|
let mut columns = HashSet::from_iter(0..input.lines().next().unwrap().len());
|
||||||
|
|
||||||
|
for (y, line) in input.lines().enumerate() {
|
||||||
|
for (x, c) in line.chars().enumerate() {
|
||||||
|
if c == '#' {
|
||||||
|
galaxies.push((y, x));
|
||||||
|
rows.remove(&y);
|
||||||
|
columns.remove(&x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(galaxies, rows, columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(input: &str, explode: usize) -> Option<usize> {
|
||||||
|
let (galaxies, rows, columns) = parse_input(input);
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
for ((y1, x1), (y2, x2)) in galaxies
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(i, x)| galaxies.iter().skip(i + 1).copied().map(move |y| (x, y)))
|
||||||
|
{
|
||||||
|
let y_abs = y1.abs_diff(y2);
|
||||||
|
let x_abs = x1.abs_diff(x2);
|
||||||
|
let x_extra = columns
|
||||||
|
.iter()
|
||||||
|
.filter(|x| (x1..=x2).contains(x) || (x2..=x1).contains(x))
|
||||||
|
.count();
|
||||||
|
let y_extra = rows
|
||||||
|
.iter()
|
||||||
|
.filter(|y| (y1..=y2).contains(y) || (y2..=y1).contains(y))
|
||||||
|
.count();
|
||||||
|
counter += x_abs + y_abs + (x_extra + y_extra) * (explode - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
solve(input, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
solve(input, 1_000_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(11);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 11)),
|
||||||
|
Some(374)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let input = aoc::template::read_file("examples", 11);
|
||||||
|
assert_eq!(solve(&input, 10), Some(1030));
|
||||||
|
assert_eq!(solve(&input, 100), Some(8410));
|
||||||
|
}
|
||||||
|
}
|
114
src/bin/12.rs
Normal file
114
src/bin/12.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use std::{collections::HashMap, iter::once};
|
||||||
|
|
||||||
|
fn is_valid(sequence: &[char], ptr: usize, group: usize) -> bool {
|
||||||
|
let edges_front = *once(&'.').chain(sequence.iter()).nth(ptr).unwrap() != '#';
|
||||||
|
let edges_back = *sequence.iter().chain(once(&'.')).nth(ptr + group).unwrap() != '#';
|
||||||
|
|
||||||
|
let filled = sequence
|
||||||
|
.iter()
|
||||||
|
.chain(once(&'.').cycle())
|
||||||
|
.skip(ptr)
|
||||||
|
.take(group)
|
||||||
|
.all(|x| *x != '.');
|
||||||
|
|
||||||
|
edges_front && edges_back && filled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(
|
||||||
|
memo: &mut HashMap<(usize, usize), usize>,
|
||||||
|
sequence: &[char],
|
||||||
|
groups: &[usize],
|
||||||
|
ptr: usize,
|
||||||
|
) -> usize {
|
||||||
|
match groups.split_first() {
|
||||||
|
None => !sequence.iter().skip(ptr).any(|c| *c == '#') as usize,
|
||||||
|
Some((group, r_groups)) => {
|
||||||
|
let remaining = r_groups.iter().sum();
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for idx in ptr..(sequence.len() - group - remaining + 1) {
|
||||||
|
if is_valid(sequence, idx, *group) {
|
||||||
|
let next = idx + *group + 1;
|
||||||
|
match memo.get(&(remaining, next)) {
|
||||||
|
Some(m) => total += m,
|
||||||
|
None => {
|
||||||
|
let count = count(memo, sequence, r_groups, next);
|
||||||
|
memo.insert((remaining, next), count);
|
||||||
|
total += count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sequence[idx] == '#' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_line(line: &str) -> (Vec<char>, Vec<usize>) {
|
||||||
|
let (str_seq, str_grp) = line.split_once(' ').unwrap();
|
||||||
|
let sequence = str_seq.chars().collect();
|
||||||
|
let groups = str_grp.split(',').filter_map(|x| x.parse().ok()).collect();
|
||||||
|
|
||||||
|
(sequence, groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unfold(sequence: Vec<char>, groups: Vec<usize>, n: usize) -> (Vec<char>, Vec<usize>) {
|
||||||
|
let seq_len = sequence.len();
|
||||||
|
let grp_len = groups.len();
|
||||||
|
|
||||||
|
let new_sequence = sequence
|
||||||
|
.into_iter()
|
||||||
|
.chain(once('?'))
|
||||||
|
.cycle()
|
||||||
|
.take(seq_len * n + n - 1)
|
||||||
|
.collect();
|
||||||
|
let new_groups = groups.into_iter().cycle().take(grp_len * n).collect();
|
||||||
|
|
||||||
|
(new_sequence, new_groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
Some(
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(parse_line)
|
||||||
|
.map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0))
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
Some(
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(parse_line)
|
||||||
|
.map(|(sequence, groups)| unfold(sequence, groups, 5))
|
||||||
|
.map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0))
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(12);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 12)),
|
||||||
|
Some(21)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 12)),
|
||||||
|
Some(525152)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
64
src/bin/13.rs
Normal file
64
src/bin/13.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
fn mirror_h(shape: &[Vec<char>], smudges: usize) -> Option<usize> {
|
||||||
|
(1..shape.len()).find(|&i| {
|
||||||
|
shape
|
||||||
|
.iter()
|
||||||
|
.skip(i)
|
||||||
|
.zip(shape.iter().take(i).rev())
|
||||||
|
.map(|(x, y)| {
|
||||||
|
x.iter()
|
||||||
|
.zip(y.iter())
|
||||||
|
.map(|(xx, yy)| (xx != yy) as usize)
|
||||||
|
.sum::<usize>()
|
||||||
|
})
|
||||||
|
.sum::<usize>()
|
||||||
|
== smudges
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mirror_v(shape: &[Vec<char>], smudges: usize) -> Option<usize> {
|
||||||
|
let shape: Vec<Vec<char>> = (0..shape[0].len())
|
||||||
|
.map(|col| (0..shape.len()).map(|row| shape[row][col]).collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
mirror_h(&shape, smudges)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(input: &str, smudges: usize) -> usize {
|
||||||
|
input
|
||||||
|
.split("\n\n")
|
||||||
|
.map(|x| x.lines().map(|line| line.chars().collect()).collect())
|
||||||
|
.map(|shape: Vec<Vec<char>>| {
|
||||||
|
mirror_v(&shape, smudges).unwrap_or_default()
|
||||||
|
+ mirror_h(&shape, smudges).unwrap_or_default() * 100
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
Some(solve(input, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
Some(solve(input, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(13);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 13)),
|
||||||
|
Some(405)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 13)),
|
||||||
|
Some(400)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
115
src/bin/14.rs
Normal file
115
src/bin/14.rs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
use std::{collections::HashMap, iter::once};
|
||||||
|
|
||||||
|
enum Tilt {
|
||||||
|
North,
|
||||||
|
West,
|
||||||
|
South,
|
||||||
|
East,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_load(floor: &[Vec<char>]) -> usize {
|
||||||
|
floor
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(i, x)| x.iter().map(move |y| (i, y)))
|
||||||
|
.filter(|x| *x.1 == 'O')
|
||||||
|
.map(|x| floor.len() - x.0)
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap<T: Copy>(floor: &mut [Vec<T>], from: (usize, usize), to: (usize, usize)) {
|
||||||
|
let a = floor[from.0][from.1];
|
||||||
|
let b = floor[to.0][to.1];
|
||||||
|
floor[from.0][from.1] = b;
|
||||||
|
floor[to.0][to.1] = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt(floor: &mut [Vec<char>], tilt: Tilt) {
|
||||||
|
let (inner, outer) = match tilt {
|
||||||
|
Tilt::North | Tilt::South => (floor[0].len(), floor.len()),
|
||||||
|
Tilt::West | Tilt::East => (floor.len(), floor[0].len()),
|
||||||
|
};
|
||||||
|
let inx_n = |(i, j)| (j, i);
|
||||||
|
let inx_s = |(i, j)| (inner - 1 - j, i);
|
||||||
|
let inx_w = |(i, j)| (i, j);
|
||||||
|
let inx_e = |(i, j)| (i, inner - 1 - j);
|
||||||
|
for i in 0..outer {
|
||||||
|
let mut ptr = 0;
|
||||||
|
for j in 0..inner {
|
||||||
|
let ((ii, jj), (pi, pj)) = match tilt {
|
||||||
|
Tilt::North => (inx_n((i, j)), inx_n((i, ptr))),
|
||||||
|
Tilt::South => (inx_s((i, j)), inx_s((i, ptr))),
|
||||||
|
Tilt::East => (inx_e((i, j)), inx_e((i, ptr))),
|
||||||
|
Tilt::West => (inx_w((i, j)), inx_w((i, ptr))),
|
||||||
|
};
|
||||||
|
match floor[ii][jj] {
|
||||||
|
'O' => {
|
||||||
|
swap(floor, (ii, jj), (pi, pj));
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
'#' => ptr = j + 1,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt_cycle(floor: &mut [Vec<char>]) {
|
||||||
|
use Tilt::*;
|
||||||
|
tilt(floor, North);
|
||||||
|
tilt(floor, West);
|
||||||
|
tilt(floor, South);
|
||||||
|
tilt(floor, East);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect();
|
||||||
|
tilt(&mut f, Tilt::North);
|
||||||
|
Some(get_load(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect();
|
||||||
|
let mut memo: HashMap<String, usize> = HashMap::new();
|
||||||
|
|
||||||
|
for i in 1.. {
|
||||||
|
tilt_cycle(&mut f);
|
||||||
|
let repr = f
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.iter().chain(once(&'\n')))
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
if let Some(ii) = memo.insert(repr, i) {
|
||||||
|
let m = i - ii;
|
||||||
|
let shift = (1_000_000_000 - ii) % m;
|
||||||
|
|
||||||
|
for _ in 0..shift {
|
||||||
|
tilt_cycle(&mut f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(get_load(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(14);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 14)),
|
||||||
|
Some(136)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 14)),
|
||||||
|
Some(64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
68
src/bin/15.rs
Normal file
68
src/bin/15.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
fn hash(s: &str) -> usize {
|
||||||
|
let mut total = 0;
|
||||||
|
for c in s.chars().map(|x| x as usize) {
|
||||||
|
total += c;
|
||||||
|
total *= 17;
|
||||||
|
total %= 256;
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
Some(input.lines().flat_map(|x| x.split(',')).map(hash).sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let mut map = vec![Vec::<(&str, usize)>::new(); 256];
|
||||||
|
|
||||||
|
for instruction in input.lines().flat_map(|x| x.split(',')) {
|
||||||
|
let (label, n) = instruction.split_once(|x| x == '-' || x == '=').unwrap();
|
||||||
|
let hash = hash(label);
|
||||||
|
let indexed_map = map.get_mut(hash).unwrap();
|
||||||
|
|
||||||
|
if instruction.contains('-') {
|
||||||
|
if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) {
|
||||||
|
indexed_map.remove(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let nbr = n.parse().unwrap();
|
||||||
|
if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) {
|
||||||
|
indexed_map[i] = (label, nbr);
|
||||||
|
} else {
|
||||||
|
indexed_map.push((label, nbr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(
|
||||||
|
map.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(i, x)| {
|
||||||
|
x.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(j, y)| (i + 1) * (j + 1) * y.1)
|
||||||
|
})
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(15);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 15)),
|
||||||
|
Some(1320)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 15)),
|
||||||
|
Some(145)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
173
src/bin/16.rs
Normal file
173
src/bin/16.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn next(&self, (y, x): (usize, usize)) -> (usize, usize) {
|
||||||
|
use Direction::*;
|
||||||
|
match self {
|
||||||
|
Up => (y.checked_sub(1).unwrap_or(usize::MAX), x),
|
||||||
|
Down => (y + 1, x),
|
||||||
|
Left => (y, x.checked_sub(1).unwrap_or(usize::MAX)),
|
||||||
|
Right => (y, x + 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Mirror {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
EvenSymmetric,
|
||||||
|
OddSymmetric,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParseMirrorError;
|
||||||
|
|
||||||
|
impl TryFrom<u8> for Mirror {
|
||||||
|
type Error = ParseMirrorError;
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match value {
|
||||||
|
b'|' => Self::Vertical,
|
||||||
|
b'-' => Self::Horizontal,
|
||||||
|
b'\\' => Self::EvenSymmetric,
|
||||||
|
b'/' => Self::OddSymmetric,
|
||||||
|
_ => return Err(ParseMirrorError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mirror {
|
||||||
|
fn bounce(&self, d: &Direction) -> (Direction, Option<Direction>) {
|
||||||
|
use Direction::*;
|
||||||
|
use Mirror::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Vertical => match d {
|
||||||
|
Up | Down => (*d, None),
|
||||||
|
Left | Right => (Up, Some(Down)),
|
||||||
|
},
|
||||||
|
Horizontal => match d {
|
||||||
|
Up | Down => (Left, Some(Right)),
|
||||||
|
Left | Right => (*d, None),
|
||||||
|
},
|
||||||
|
EvenSymmetric => (
|
||||||
|
match d {
|
||||||
|
Up => Left,
|
||||||
|
Down => Right,
|
||||||
|
Left => Up,
|
||||||
|
Right => Down,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
OddSymmetric => (
|
||||||
|
match d {
|
||||||
|
Up => Right,
|
||||||
|
Down => Left,
|
||||||
|
Left => Down,
|
||||||
|
Right => Up,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_with_start(layout: &[Vec<Option<Mirror>>], start: (usize, usize, Direction)) -> usize {
|
||||||
|
let mut queue = vec![start];
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
|
||||||
|
while let Some((y, x, d)) = queue.pop() {
|
||||||
|
let point = layout.get(y).and_then(|row| row.get(x));
|
||||||
|
|
||||||
|
if point.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visited.insert((y, x, d)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Some(m)) = point {
|
||||||
|
let (new_d, opt_new_d) = m.bounce(&d);
|
||||||
|
let (ny, nx) = new_d.next((y, x));
|
||||||
|
queue.push((ny, nx, new_d));
|
||||||
|
|
||||||
|
if let Some(od) = opt_new_d {
|
||||||
|
let (ony, onx) = od.next((y, x));
|
||||||
|
queue.push((ony, onx, od));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (ny, nx) = d.next((y, x));
|
||||||
|
queue.push((ny, nx, d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet::<(usize, usize)>::from_iter(visited.into_iter().map(|(y, x, _)| (y, x))).len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let layout: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(solve_with_start(&layout, (0, 0, Direction::Right)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let layout: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let h = layout.len();
|
||||||
|
let w = layout[0].len();
|
||||||
|
|
||||||
|
let mut scores = Vec::with_capacity(2 * (h + w) + 1);
|
||||||
|
|
||||||
|
for hh in 0..h {
|
||||||
|
let left = solve_with_start(&layout, (hh, 0, Direction::Right));
|
||||||
|
let right = solve_with_start(&layout, (hh, w - 1, Direction::Left));
|
||||||
|
|
||||||
|
scores.push(left);
|
||||||
|
scores.push(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ww in 0..w {
|
||||||
|
let downward = solve_with_start(&layout, (0, ww, Direction::Down));
|
||||||
|
let upward = solve_with_start(&layout, (h - 1, ww, Direction::Up));
|
||||||
|
|
||||||
|
scores.push(downward);
|
||||||
|
scores.push(upward);
|
||||||
|
}
|
||||||
|
|
||||||
|
scores.into_iter().max()
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(16);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 16)),
|
||||||
|
Some(46)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 16)),
|
||||||
|
Some(51)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
192
src/bin/17.rs
Normal file
192
src/bin/17.rs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{BinaryHeap, HashMap},
|
||||||
|
ops::Neg,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
const ALL: [Direction; 4] = [
|
||||||
|
Direction::Up,
|
||||||
|
Direction::Down,
|
||||||
|
Direction::Left,
|
||||||
|
Direction::Right,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Direction {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
use Direction::*;
|
||||||
|
match self {
|
||||||
|
Up => Down,
|
||||||
|
Down => Up,
|
||||||
|
Left => Right,
|
||||||
|
Right => Left,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct State {
|
||||||
|
heat: usize,
|
||||||
|
position: (usize, usize),
|
||||||
|
direction: Direction,
|
||||||
|
steps: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for State {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for State {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
other
|
||||||
|
.heat
|
||||||
|
.cmp(&self.heat)
|
||||||
|
.then_with(|| self.position.cmp(&other.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_path(
|
||||||
|
grid: &[Vec<usize>],
|
||||||
|
start: (usize, usize),
|
||||||
|
target: (usize, usize),
|
||||||
|
min_steps: usize,
|
||||||
|
max_steps: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some();
|
||||||
|
|
||||||
|
let mut heap = BinaryHeap::new();
|
||||||
|
heap.push(State {
|
||||||
|
heat: 0,
|
||||||
|
position: (0, 0),
|
||||||
|
direction: Direction::Right,
|
||||||
|
steps: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut heatmap = HashMap::new();
|
||||||
|
heatmap.insert((start, Direction::Down, 0), 0);
|
||||||
|
heatmap.insert((start, Direction::Up, 0), 0);
|
||||||
|
|
||||||
|
while let Some(State {
|
||||||
|
heat,
|
||||||
|
position,
|
||||||
|
direction,
|
||||||
|
steps,
|
||||||
|
}) = heap.pop()
|
||||||
|
{
|
||||||
|
if position == target {
|
||||||
|
return Some(heat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if *heatmap
|
||||||
|
.get(&(position, direction, steps))
|
||||||
|
.unwrap_or(&usize::MAX)
|
||||||
|
< heat
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (y, x) = position;
|
||||||
|
for d in Direction::ALL.iter().filter(|&x| *x != -direction) {
|
||||||
|
if steps < min_steps && *d != direction {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ny, nx) = match d {
|
||||||
|
Direction::Up => (y.wrapping_sub(1), x),
|
||||||
|
Direction::Down => (y + 1, x),
|
||||||
|
Direction::Left => (y, x.wrapping_sub(1)),
|
||||||
|
Direction::Right => (y, x + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !valid_indexing(ny, nx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_steps = if *d == direction { steps + 1 } else { 1 };
|
||||||
|
|
||||||
|
if new_steps > max_steps {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = State {
|
||||||
|
heat: heat + grid[ny][nx],
|
||||||
|
position: (ny, nx),
|
||||||
|
direction: *d,
|
||||||
|
steps: new_steps,
|
||||||
|
};
|
||||||
|
|
||||||
|
if *heatmap
|
||||||
|
.get(&((ny, nx), *d, new_steps))
|
||||||
|
.unwrap_or(&usize::MAX)
|
||||||
|
> state.heat
|
||||||
|
{
|
||||||
|
heatmap.insert(((ny, nx), *d, new_steps), state.heat);
|
||||||
|
heap.push(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let grid: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| {
|
||||||
|
x.chars()
|
||||||
|
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||||
|
|
||||||
|
find_path(&grid, (0, 0), target, 0, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let grid: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| {
|
||||||
|
x.chars()
|
||||||
|
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||||
|
|
||||||
|
find_path(&grid, (0, 0), target, 4, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(17);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 17)),
|
||||||
|
Some(102)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 17)),
|
||||||
|
Some(94)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
126
src/bin/18.rs
Normal file
126
src/bin/18.rs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseDirectionError;
|
||||||
|
|
||||||
|
impl FromStr for Direction {
|
||||||
|
type Err = ParseDirectionError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use Direction::*;
|
||||||
|
|
||||||
|
Ok(match s {
|
||||||
|
"U" | "3" => Up,
|
||||||
|
"D" | "1" => Down,
|
||||||
|
"L" | "2" => Left,
|
||||||
|
"R" | "0" => Right,
|
||||||
|
_ => return Err(ParseDirectionError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Direction> for (isize, isize) {
|
||||||
|
fn from(value: Direction) -> Self {
|
||||||
|
use Direction::*;
|
||||||
|
match value {
|
||||||
|
Up => (-1, 0),
|
||||||
|
Down => (1, 0),
|
||||||
|
Left => (0, -1),
|
||||||
|
Right => (0, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_area(border: &[(isize, isize)], border_length: isize) -> isize {
|
||||||
|
// get area with shoelace formula (trapezoid variant)
|
||||||
|
// https://en.wikipedia.org/wiki/Shoelace_formula
|
||||||
|
let mut shoelace: isize = 0;
|
||||||
|
for n in 0..border.len() {
|
||||||
|
let (y1, x1) = border[n];
|
||||||
|
let (y2, x2) = border[(n + 1) % border.len()];
|
||||||
|
shoelace += (y1 + y2) * (x1 - x2);
|
||||||
|
}
|
||||||
|
let area = shoelace / 2;
|
||||||
|
|
||||||
|
// get interior by inverting pick's theorem formula
|
||||||
|
// https://en.wikipedia.org/wiki/Pick%27s_theorem
|
||||||
|
let interior = area + 1 - border_length / 2;
|
||||||
|
interior + border_length
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_border(instructions: &[(Direction, isize)]) -> (Vec<(isize, isize)>, isize) {
|
||||||
|
let mut border = Vec::new();
|
||||||
|
let mut border_length = 0;
|
||||||
|
let (mut sy, mut sx) = (0, 0);
|
||||||
|
border.push((sy, sx));
|
||||||
|
|
||||||
|
for (d, l) in instructions.iter().copied() {
|
||||||
|
let (dy, dx) = d.into();
|
||||||
|
(sy, sx) = (sy + l * dy, sx + l * dx);
|
||||||
|
border.push((sy, sx));
|
||||||
|
border_length += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
border.pop();
|
||||||
|
|
||||||
|
(border, border_length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<isize> {
|
||||||
|
let instructions: Vec<(Direction, isize)> = input
|
||||||
|
.lines()
|
||||||
|
.map(|line| {
|
||||||
|
let [d, l, _] = line.splitn(3, ' ').collect::<Vec<_>>().try_into().unwrap();
|
||||||
|
(d.parse().unwrap(), l.parse::<isize>().unwrap())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (border, border_length) = get_border(&instructions);
|
||||||
|
Some(get_area(&border, border_length))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_option_tuple<T, U>((a, b): (Option<T>, Option<U>)) -> Option<(T, U)> {
|
||||||
|
Some((a?, b?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<isize> {
|
||||||
|
let instructions: Vec<(Direction, isize)> = input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| line.split_once(" (#"))
|
||||||
|
.filter_map(|(_, h)| h.strip_suffix(')'))
|
||||||
|
.map(|h| h.split_at(h.len() - 1))
|
||||||
|
.map(|(hex, dir)| (dir.parse().ok(), isize::from_str_radix(hex, 16).ok()))
|
||||||
|
.filter_map(join_option_tuple)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (border, border_length) = get_border(&instructions);
|
||||||
|
Some(get_area(&border, border_length))
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(18);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 18)),
|
||||||
|
Some(62)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 18)),
|
||||||
|
Some(952408144115)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
387
src/bin/19.rs
Normal file
387
src/bin/19.rs
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ops::{Index, IndexMut},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum Gear {
|
||||||
|
X,
|
||||||
|
M,
|
||||||
|
A,
|
||||||
|
S,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GearParseError;
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Gear {
|
||||||
|
type Error = GearParseError;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
use Gear::*;
|
||||||
|
Ok(match value {
|
||||||
|
"x" => X,
|
||||||
|
"m" => M,
|
||||||
|
"a" => A,
|
||||||
|
"s" => S,
|
||||||
|
_ => return Err(GearParseError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum Comparator {
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ComparatorParseError;
|
||||||
|
|
||||||
|
impl TryFrom<char> for Comparator {
|
||||||
|
type Error = ComparatorParseError;
|
||||||
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||||
|
use Comparator::*;
|
||||||
|
Ok(match value {
|
||||||
|
'<' => Lt,
|
||||||
|
'>' => Gt,
|
||||||
|
_ => return Err(ComparatorParseError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
enum Resolver {
|
||||||
|
Accepted,
|
||||||
|
Rejected,
|
||||||
|
Delegated(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Resolver {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
use Resolver::*;
|
||||||
|
match value {
|
||||||
|
"A" => Accepted,
|
||||||
|
"R" => Rejected,
|
||||||
|
x => Delegated(x.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
struct Workflow {
|
||||||
|
workflows: Vec<WorkflowInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseWorkflowError;
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Workflow {
|
||||||
|
type Error = ParseWorkflowError;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
workflows: value.split(',').filter_map(|x| x.try_into().ok()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum WorkflowInner {
|
||||||
|
Resolver(Resolver),
|
||||||
|
Rule((Gear, Comparator, usize, Resolver)),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseWorkflowInnerError;
|
||||||
|
|
||||||
|
impl TryFrom<&str> for WorkflowInner {
|
||||||
|
type Error = ParseWorkflowInnerError;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
if !value.contains(':') {
|
||||||
|
return Ok(WorkflowInner::Resolver(value.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (rest, resolver) = value.split_once(':').unwrap();
|
||||||
|
let resolver = resolver.into();
|
||||||
|
|
||||||
|
let (gear, number) = rest.split_once(|x| x == '<' || x == '>').unwrap();
|
||||||
|
let gear = gear.try_into().map_err(|_| ParseWorkflowInnerError)?;
|
||||||
|
let number = number.parse().map_err(|_| ParseWorkflowInnerError)?;
|
||||||
|
|
||||||
|
let comparator = if value.contains('<') { '<' } else { '>' };
|
||||||
|
let comparator = comparator.try_into().map_err(|_| ParseWorkflowInnerError)?;
|
||||||
|
|
||||||
|
Ok(WorkflowInner::Rule((gear, comparator, number, resolver)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Xmas {
|
||||||
|
x: usize,
|
||||||
|
m: usize,
|
||||||
|
a: usize,
|
||||||
|
s: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseXmasError;
|
||||||
|
|
||||||
|
impl FromStr for Xmas {
|
||||||
|
type Err = ParseXmasError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let s = s.strip_prefix('{').ok_or(ParseXmasError)?;
|
||||||
|
let s = s.strip_suffix('}').ok_or(ParseXmasError)?;
|
||||||
|
let xmas: Vec<_> = s
|
||||||
|
.split(',')
|
||||||
|
.filter_map(|x| x.split_once('='))
|
||||||
|
.map(|x| x.1)
|
||||||
|
.filter_map(|x| x.parse().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
x: xmas[0],
|
||||||
|
m: xmas[1],
|
||||||
|
a: xmas[2],
|
||||||
|
s: xmas[3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Gear> for Xmas {
|
||||||
|
type Output = usize;
|
||||||
|
fn index(&self, index: Gear) -> &Self::Output {
|
||||||
|
use Gear::*;
|
||||||
|
match index {
|
||||||
|
X => &self.x,
|
||||||
|
M => &self.m,
|
||||||
|
A => &self.a,
|
||||||
|
S => &self.s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<Gear> for Xmas {
|
||||||
|
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||||
|
use Gear::*;
|
||||||
|
match index {
|
||||||
|
X => &mut self.x,
|
||||||
|
M => &mut self.m,
|
||||||
|
A => &mut self.a,
|
||||||
|
S => &mut self.s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Xmas {
|
||||||
|
fn sum(&self) -> usize {
|
||||||
|
self.x + self.m + self.a + self.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&self, workflow: &Workflow) -> Resolver {
|
||||||
|
for w in workflow.workflows.iter() {
|
||||||
|
match w {
|
||||||
|
WorkflowInner::Resolver(x) => {
|
||||||
|
return x.clone();
|
||||||
|
}
|
||||||
|
WorkflowInner::Rule((g, c, n, r)) => {
|
||||||
|
let is_match = match c {
|
||||||
|
Comparator::Gt => self[*g] > *n,
|
||||||
|
Comparator::Lt => self[*g] < *n,
|
||||||
|
};
|
||||||
|
if is_match {
|
||||||
|
return r.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct XmasRange {
|
||||||
|
x: (usize, usize),
|
||||||
|
m: (usize, usize),
|
||||||
|
a: (usize, usize),
|
||||||
|
s: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Gear> for XmasRange {
|
||||||
|
type Output = (usize, usize);
|
||||||
|
fn index(&self, index: Gear) -> &Self::Output {
|
||||||
|
use Gear::*;
|
||||||
|
match index {
|
||||||
|
X => &self.x,
|
||||||
|
M => &self.m,
|
||||||
|
A => &self.a,
|
||||||
|
S => &self.s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<Gear> for XmasRange {
|
||||||
|
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||||
|
use Gear::*;
|
||||||
|
match index {
|
||||||
|
X => &mut self.x,
|
||||||
|
M => &mut self.m,
|
||||||
|
A => &mut self.a,
|
||||||
|
S => &mut self.s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XmasRange {
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
(self.x.1 - self.x.0 + 1)
|
||||||
|
* (self.m.1 - self.m.0 + 1)
|
||||||
|
* (self.a.1 - self.a.0 + 1)
|
||||||
|
* (self.s.1 - self.s.0 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide(self, workflow: &Workflow) -> Vec<(Self, Resolver)> {
|
||||||
|
let mut processed = Vec::new();
|
||||||
|
let mut rest = vec![self];
|
||||||
|
|
||||||
|
for w in workflow.workflows.iter() {
|
||||||
|
let mut new_rest = Vec::new();
|
||||||
|
|
||||||
|
while let Some(xmas_range) = rest.pop() {
|
||||||
|
match w {
|
||||||
|
WorkflowInner::Resolver(r) => processed.push((xmas_range, r.clone())),
|
||||||
|
WorkflowInner::Rule((g, c, n, r)) => {
|
||||||
|
let compare = |x: usize, y: usize| match c {
|
||||||
|
Comparator::Gt => x > y,
|
||||||
|
Comparator::Lt => x < y,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut min_ok = usize::MAX;
|
||||||
|
let mut max_ok = usize::MIN;
|
||||||
|
let mut min_e = usize::MAX;
|
||||||
|
let mut max_e = usize::MIN;
|
||||||
|
|
||||||
|
for i in xmas_range[*g].0..=xmas_range[*g].1 {
|
||||||
|
if compare(i, *n) {
|
||||||
|
max_ok = if max_ok < i { i } else { max_ok };
|
||||||
|
min_ok = if min_ok > i { i } else { min_ok };
|
||||||
|
} else {
|
||||||
|
max_e = if max_e < i { i } else { max_e };
|
||||||
|
min_e = if min_e > i { i } else { min_e };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if min_e <= max_e {
|
||||||
|
let mut r = xmas_range;
|
||||||
|
r[*g] = (min_e, max_e);
|
||||||
|
new_rest.push(r);
|
||||||
|
}
|
||||||
|
if min_ok <= max_ok {
|
||||||
|
let mut p = xmas_range;
|
||||||
|
p[*g] = (min_ok, max_ok);
|
||||||
|
processed.push((p, r.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rest = new_rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
processed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_map(input: &str) -> HashMap<&str, Workflow> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for (s, w) in input
|
||||||
|
.split("\n\n")
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.lines()
|
||||||
|
.filter_map(|x| x.strip_suffix('}'))
|
||||||
|
.filter_map(|x| x.split_once('{'))
|
||||||
|
.filter_map(|(s, w)| w.try_into().ok().map(|x| (s, x)))
|
||||||
|
{
|
||||||
|
map.insert(s, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let map = build_map(input);
|
||||||
|
let xmas_vec: Vec<Xmas> = input
|
||||||
|
.split("\n\n")
|
||||||
|
.nth(1)?
|
||||||
|
.lines()
|
||||||
|
.filter_map(|x| x.parse().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
'outer: for xmas in xmas_vec.into_iter() {
|
||||||
|
let mut workflow = &map["in"];
|
||||||
|
'apply: loop {
|
||||||
|
match xmas.apply(workflow) {
|
||||||
|
Resolver::Accepted => break 'apply,
|
||||||
|
Resolver::Rejected => continue 'outer,
|
||||||
|
Resolver::Delegated(x) => workflow = &map[x.as_str()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total += xmas.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(total)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let map = build_map(input);
|
||||||
|
|
||||||
|
let mut finished = Vec::new();
|
||||||
|
let mut stack = vec![(
|
||||||
|
XmasRange {
|
||||||
|
x: (1, 4000),
|
||||||
|
m: (1, 4000),
|
||||||
|
a: (1, 4000),
|
||||||
|
s: (1, 4000),
|
||||||
|
},
|
||||||
|
Resolver::Delegated("in".to_string()),
|
||||||
|
)];
|
||||||
|
|
||||||
|
while let Some((xmas_range, resolver)) = stack.pop() {
|
||||||
|
match resolver {
|
||||||
|
Resolver::Accepted => finished.push(xmas_range),
|
||||||
|
Resolver::Rejected => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Resolver::Delegated(x) => {
|
||||||
|
let workflow = &map[x.as_str()];
|
||||||
|
stack.append(&mut xmas_range.divide(workflow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(finished.iter().map(XmasRange::size).sum())
|
||||||
|
}
|
||||||
|
aoc::solution!(19);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 19)),
|
||||||
|
Some(19114)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 19)),
|
||||||
|
Some(167409079868000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
206
src/bin/20.rs
Normal file
206
src/bin/20.rs
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use aoc::lcm;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
enum Module {
|
||||||
|
Broadcaster,
|
||||||
|
FlipFlop(bool),
|
||||||
|
Conjuction,
|
||||||
|
Output,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseModuleError;
|
||||||
|
|
||||||
|
impl FromStr for Module {
|
||||||
|
type Err = ParseModuleError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(match s {
|
||||||
|
"%" => Module::FlipFlop(false),
|
||||||
|
"&" => Module::Conjuction,
|
||||||
|
"b" => Module::Broadcaster,
|
||||||
|
_ => return Err(ParseModuleError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Node {
|
||||||
|
index: usize,
|
||||||
|
module: Module,
|
||||||
|
inputs: Vec<usize>,
|
||||||
|
outputs: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> Vec<Node> {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut mapper: HashMap<String, usize> = HashMap::new();
|
||||||
|
|
||||||
|
let output = Node {
|
||||||
|
index: 0,
|
||||||
|
module: Module::Output,
|
||||||
|
inputs: Vec::new(),
|
||||||
|
outputs: Vec::new(),
|
||||||
|
};
|
||||||
|
nodes.push(output);
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let (from, _) = line.split_once(" -> ").unwrap();
|
||||||
|
let (module, name) = from.split_at(1);
|
||||||
|
let module: Module = module.parse().unwrap();
|
||||||
|
|
||||||
|
*mapper.entry(name.to_string()).or_default() = nodes.len();
|
||||||
|
|
||||||
|
let l = Node {
|
||||||
|
module,
|
||||||
|
index: nodes.len(),
|
||||||
|
inputs: Vec::new(),
|
||||||
|
outputs: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.push(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let (from, to) = line.split_once(" -> ").unwrap();
|
||||||
|
let (_, name) = from.split_at(1);
|
||||||
|
|
||||||
|
let index = mapper[name];
|
||||||
|
for destination in to.split(", ") {
|
||||||
|
let to_index = *mapper.get(destination).unwrap_or(&0);
|
||||||
|
nodes[index].outputs.push(to_index);
|
||||||
|
nodes[to_index].inputs.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle(input: &str, button_presses: Option<usize>) -> usize {
|
||||||
|
let mut nodes = parse_input(input);
|
||||||
|
|
||||||
|
let mut graph = vec![vec![false; nodes.len()]; nodes.len()];
|
||||||
|
|
||||||
|
let mut highs = 0;
|
||||||
|
let mut lows = 0;
|
||||||
|
|
||||||
|
let broadcaster = nodes
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.module == Module::Broadcaster)
|
||||||
|
.unwrap()
|
||||||
|
.index;
|
||||||
|
|
||||||
|
let mut hm = HashMap::new();
|
||||||
|
let critical_node = nodes[0].inputs.first().copied();
|
||||||
|
let critical_inputs = critical_node.map(|x| nodes[x].inputs.len());
|
||||||
|
|
||||||
|
for i in 1..=button_presses.unwrap_or(usize::MAX) {
|
||||||
|
if let Some(c) = critical_inputs {
|
||||||
|
if button_presses.is_none() && hm.values().len() == c {
|
||||||
|
return hm.values().copied().reduce(lcm).unwrap_or(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stack = VecDeque::from([(broadcaster, false)]);
|
||||||
|
|
||||||
|
while let Some((index, signal)) = stack.pop_front() {
|
||||||
|
if signal {
|
||||||
|
highs += 1;
|
||||||
|
} else {
|
||||||
|
lows += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = &nodes[index];
|
||||||
|
let mut new_module = None;
|
||||||
|
|
||||||
|
match node.module {
|
||||||
|
Module::Output => (),
|
||||||
|
Module::Broadcaster => {
|
||||||
|
for dest_index in node.outputs.iter().copied() {
|
||||||
|
stack.push_back((dest_index, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Module::FlipFlop(high) => {
|
||||||
|
if !signal {
|
||||||
|
let signal = !high;
|
||||||
|
|
||||||
|
for dest_index in nodes[index].outputs.iter().copied() {
|
||||||
|
graph[index][dest_index] = signal;
|
||||||
|
stack.push_back((dest_index, signal));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_module = Some(Module::FlipFlop(signal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Module::Conjuction => {
|
||||||
|
let mut all = true;
|
||||||
|
|
||||||
|
for in_index in nodes[index].inputs.iter().copied() {
|
||||||
|
all &= graph[in_index][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
for dest_index in nodes[index].outputs.iter().copied() {
|
||||||
|
graph[index][dest_index] = !all;
|
||||||
|
stack.push_back((dest_index, !all));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(c) = critical_node {
|
||||||
|
if index == c {
|
||||||
|
for in_index in nodes[index].inputs.iter().copied() {
|
||||||
|
if graph[in_index][index] {
|
||||||
|
hm.entry(in_index).or_insert(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = &mut nodes[index];
|
||||||
|
if let Some(module) = new_module {
|
||||||
|
node.module = module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highs * lows
|
||||||
|
}
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
Some(cycle(input, Some(1000)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
Some(cycle(input, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(20);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one_first() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file_part("examples", 20, 1)),
|
||||||
|
Some(32000000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_one_second() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file_part("examples", 20, 2)),
|
||||||
|
Some(11687500)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file_part("examples", 20, 2)),
|
||||||
|
Some(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
106
src/bin/21.rs
Normal file
106
src/bin/21.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use std::{collections::HashSet, usize};
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> (Vec<Vec<u8>>, (isize, isize)) {
|
||||||
|
let mut grid: Vec<_> = input.lines().map(|x| x.as_bytes().to_vec()).collect();
|
||||||
|
|
||||||
|
let start = grid
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, item)| item.iter().enumerate().map(move |(x, item)| (x, y, *item)))
|
||||||
|
.find_map(|(x, y, s)| if s == b'S' { Some((y, x)) } else { None })
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
grid[start.0][start.1] = b'.';
|
||||||
|
|
||||||
|
(grid, (start.0 as isize, start.1 as isize))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_return_at(
|
||||||
|
grid: &[Vec<u8>],
|
||||||
|
start: (isize, isize),
|
||||||
|
mut returns: Vec<usize>,
|
||||||
|
can_cycle: bool,
|
||||||
|
) -> Vec<usize> {
|
||||||
|
returns.sort_by(|a, b| b.cmp(a));
|
||||||
|
|
||||||
|
let h = grid.len() as isize;
|
||||||
|
let w = grid[0].len() as isize;
|
||||||
|
|
||||||
|
let invalid_indexing = |y, x| y < 0 || y >= h || x < 0 || x >= w;
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
let length = returns[0];
|
||||||
|
let mut next = returns.pop().unwrap();
|
||||||
|
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
visited.insert(start);
|
||||||
|
|
||||||
|
for i in 1..=length {
|
||||||
|
let mut new_visited = HashSet::new();
|
||||||
|
|
||||||
|
for (y, x) in visited.iter() {
|
||||||
|
for (dy, dx) in [(1, 0), (0, 1), (-1, 0), (0, -1)] {
|
||||||
|
let (ny, nx) = (y + dy, x + dx);
|
||||||
|
|
||||||
|
if !can_cycle && invalid_indexing(ny, nx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (cy, cx) = (ny.rem_euclid(h) as usize, nx.rem_euclid(w) as usize);
|
||||||
|
|
||||||
|
if grid[cy][cx] == b'.' {
|
||||||
|
new_visited.insert((ny, nx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visited = new_visited;
|
||||||
|
|
||||||
|
if i == next {
|
||||||
|
results.push(visited.len());
|
||||||
|
if !returns.is_empty() {
|
||||||
|
next = returns.pop().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let (grid, start) = parse_input(input);
|
||||||
|
let result = walk_return_at(&grid, start, vec![64], false);
|
||||||
|
|
||||||
|
Some(result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let (grid, start) = parse_input(input);
|
||||||
|
|
||||||
|
let h = grid.len();
|
||||||
|
let s = start.0 as usize;
|
||||||
|
|
||||||
|
let result = walk_return_at(&grid, start, vec![s, s + h, s + 2 * h], true);
|
||||||
|
|
||||||
|
let a = (result[2] - 2 * result[1] + result[0]) / 2;
|
||||||
|
let b = (result[1] - result[0]) - a;
|
||||||
|
let c = result[0];
|
||||||
|
|
||||||
|
let x = 26501365 / h;
|
||||||
|
|
||||||
|
Some(a * x * x + b * x + c)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(21);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 21)),
|
||||||
|
Some(42)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
143
src/bin/22.rs
Normal file
143
src/bin/22.rs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Brick {
|
||||||
|
from: (usize, usize, usize),
|
||||||
|
to: (usize, usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseBrickError;
|
||||||
|
|
||||||
|
impl FromStr for Brick {
|
||||||
|
type Err = ParseBrickError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (first, second) = s.split_once('~').ok_or(ParseBrickError)?;
|
||||||
|
|
||||||
|
let sorted: Vec<_> = first
|
||||||
|
.split(',')
|
||||||
|
.filter_map(|y| y.parse().ok())
|
||||||
|
.zip(second.split(',').filter_map(|y| y.parse().ok()))
|
||||||
|
.map(|(x, y)| if x < y { (x, y) } else { (y, x) })
|
||||||
|
.collect();
|
||||||
|
let from = (sorted[0].0, sorted[1].0, sorted[2].0);
|
||||||
|
let to = (sorted[0].1, sorted[1].1, sorted[2].1);
|
||||||
|
|
||||||
|
Ok(Self { from, to })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Brick {
|
||||||
|
fn height(&self) -> usize {
|
||||||
|
self.to.2 - self.from.2 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn points(&self) -> impl Iterator<Item = (usize, usize, usize)> + '_ {
|
||||||
|
(self.from.0..=self.to.0).flat_map(move |x| {
|
||||||
|
(self.from.1..=self.to.1)
|
||||||
|
.flat_map(move |y| (self.from.2..=self.to.2).map(move |z| (x, y, z)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Brick {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Brick {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.from
|
||||||
|
.2
|
||||||
|
.cmp(&other.from.2)
|
||||||
|
.then((self.from.0, self.from.1).cmp(&(other.from.0, other.from.1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Tower {
|
||||||
|
bricks: Vec<Brick>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tower {
|
||||||
|
fn new(mut bricks: Vec<Brick>) -> Self {
|
||||||
|
bricks.sort();
|
||||||
|
let mut ret = Self { bricks };
|
||||||
|
ret.compress(None);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compress(&mut self, skip: Option<usize>) -> usize {
|
||||||
|
let mut heights: HashMap<(usize, usize), usize> = HashMap::new();
|
||||||
|
let mut moved = 0;
|
||||||
|
|
||||||
|
for brick in self
|
||||||
|
.bricks
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, _)| *i != skip.unwrap_or(usize::MAX))
|
||||||
|
.map(|(_, x)| x)
|
||||||
|
{
|
||||||
|
let height = brick.height();
|
||||||
|
let new_height = brick
|
||||||
|
.points()
|
||||||
|
.map(|(x, y, _)| *heights.entry((x, y)).or_default())
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for (x, y, _) in brick.points() {
|
||||||
|
*heights.get_mut(&(x, y)).unwrap() = new_height + height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_height + 1 == brick.from.2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip.is_none() {
|
||||||
|
brick.from.2 = new_height + 1;
|
||||||
|
brick.to.2 = new_height + height;
|
||||||
|
}
|
||||||
|
|
||||||
|
moved += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
moved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let mut tower = Tower::new(input.lines().filter_map(|x| x.parse().ok()).collect());
|
||||||
|
|
||||||
|
Some(
|
||||||
|
(0..tower.bricks.len())
|
||||||
|
.map(|i| tower.compress(Some(i)))
|
||||||
|
.filter(|m| *m == 0)
|
||||||
|
.count(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let mut tower = Tower::new(input.lines().filter_map(|x| x.parse().ok()).collect());
|
||||||
|
|
||||||
|
Some(
|
||||||
|
(0..tower.bricks.len())
|
||||||
|
.map(|i| tower.compress(Some(i)))
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(22);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 22)), Some(5));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 22)), Some(7));
|
||||||
|
}
|
||||||
|
}
|
161
src/bin/23.rs
Normal file
161
src/bin/23.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
iter::once,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DIRS: [(isize, isize); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)];
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Node {
|
||||||
|
neighbours: Vec<(usize, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_graph(input: &str, two_way: bool) -> (Vec<Node>, usize) {
|
||||||
|
let mut grid: Vec<Vec<_>> = input.lines().map(|x| x.as_bytes().to_vec()).collect();
|
||||||
|
|
||||||
|
let l = grid[0].len();
|
||||||
|
|
||||||
|
let border: Vec<_> = once(b'#').cycle().take(l).collect();
|
||||||
|
grid.insert(0, border.clone());
|
||||||
|
grid.push(border);
|
||||||
|
|
||||||
|
let start = (1, 1);
|
||||||
|
let end = (grid.len() - 2, grid[0].len() - 2);
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
|
||||||
|
let mut mapper = HashMap::new();
|
||||||
|
mapper.insert(start, 0);
|
||||||
|
|
||||||
|
let mut visited: HashSet<(usize, usize)> = HashSet::new();
|
||||||
|
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
// (from_node, at, len)
|
||||||
|
stack.push((start, start, 0));
|
||||||
|
|
||||||
|
let forced = |x| match x {
|
||||||
|
b'>' => (0, 1),
|
||||||
|
b'v' => (1, 0),
|
||||||
|
_ => (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some((from, (y, x), len)) = stack.pop() {
|
||||||
|
let is_node = DIRS
|
||||||
|
.iter()
|
||||||
|
.filter(|(dy, dx)| {
|
||||||
|
grid[((y as isize) + dy) as usize][((x as isize) + dx) as usize] != b'#'
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
> 2
|
||||||
|
|| (y, x) == start
|
||||||
|
|| (y, x) == end;
|
||||||
|
|
||||||
|
if is_node {
|
||||||
|
if !visited.contains(&(y, x)) {
|
||||||
|
mapper.insert((y, x), nodes.len());
|
||||||
|
nodes.push(Node::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
if from != (y, x) {
|
||||||
|
nodes[mapper[&from]].neighbours.push((mapper[&(y, x)], len));
|
||||||
|
if two_way {
|
||||||
|
nodes[mapper[&(y, x)]].neighbours.push((mapper[&from], len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visited.insert((y, x)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (ny, nx, _, _) in DIRS
|
||||||
|
.iter()
|
||||||
|
.map(|(dy, dx)| {
|
||||||
|
(
|
||||||
|
((y as isize) + dy) as usize,
|
||||||
|
((x as isize) + dx) as usize,
|
||||||
|
dy,
|
||||||
|
dx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(ny, nx, _, _)| grid[*ny][*nx] != b'#')
|
||||||
|
.filter(|(ny, nx, dy, dx)| {
|
||||||
|
let (fy, fx) = forced(grid[*ny][*nx]);
|
||||||
|
(fy + **dy, fx + **dx) != (0, 0)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let new_len = if is_node { 1 } else { len + 1 };
|
||||||
|
let new_from = if is_node { (y, x) } else { from };
|
||||||
|
stack.push((new_from, (ny, nx), new_len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes, mapper[&end])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn longest_path(
|
||||||
|
nodes: &[Node],
|
||||||
|
mut visited: usize,
|
||||||
|
location: usize,
|
||||||
|
target: usize,
|
||||||
|
length: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
if location == target {
|
||||||
|
return Some(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// binary mask for visited since < 64 nodes in input
|
||||||
|
// nth bit tells if location n was visited already
|
||||||
|
visited |= 1 << location;
|
||||||
|
|
||||||
|
let mut max_len = 0;
|
||||||
|
|
||||||
|
for (n, l) in nodes[location]
|
||||||
|
.neighbours
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|(n, _)| (visited & 1 << n) == 0)
|
||||||
|
{
|
||||||
|
if let Some(new_len) = longest_path(nodes, visited, n, target, length + l) {
|
||||||
|
if max_len < new_len {
|
||||||
|
max_len = new_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if max_len > 0 {
|
||||||
|
Some(max_len)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let (nodes, target) = build_graph(input, false);
|
||||||
|
longest_path(&nodes, 0, 0, target, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let (nodes, target) = build_graph(input, true);
|
||||||
|
longest_path(&nodes, 0, 0, target, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(23);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 23)),
|
||||||
|
Some(94)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 23)),
|
||||||
|
Some(154)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
94
src/bin/24.rs
Normal file
94
src/bin/24.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use z3::ast::{Ast, Int};
|
||||||
|
|
||||||
|
type Vector3 = (f64, f64, f64);
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> Vec<(Vector3, Vector3)> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|x| x.split_once(" @ "))
|
||||||
|
.map(|(a, b)| {
|
||||||
|
let av: Vec<_> = a.splitn(3, ", ").filter_map(|x| x.parse().ok()).collect();
|
||||||
|
let bv: Vec<_> = b.splitn(3, ", ").filter_map(|x| x.parse().ok()).collect();
|
||||||
|
((av[0], av[1], av[2]), (bv[0], bv[1], bv[2]))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_one_with_area(input: &str, min: f64, max: f64) -> u32 {
|
||||||
|
let points = parse_input(input);
|
||||||
|
|
||||||
|
let mut c = 0;
|
||||||
|
|
||||||
|
let test_area = |x: f64, y: f64| x <= max && x >= min && y <= max && y >= min;
|
||||||
|
|
||||||
|
for (i, ((x1, y1, _), (dx1, dy1, _))) in points.iter().copied().enumerate() {
|
||||||
|
for ((x2, y2, _), (dx2, dy2, _)) in points.iter().skip(i + 1).copied() {
|
||||||
|
let n2 = (x2 - x1 + (y1 - y2) * dx1 / dy1) / (dy2 * dx1 / dy1 - dx2);
|
||||||
|
let n1 = (x1 - x2 + (y2 - y1) * dx2 / dy2) / (dy1 * dx2 / dy2 - dx1);
|
||||||
|
|
||||||
|
if n1 <= 0.0 || n2 <= 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = x1 + n1 * dx1;
|
||||||
|
let y = y1 + n1 * dy1;
|
||||||
|
|
||||||
|
if test_area(x, y) {
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c
|
||||||
|
}
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
let min = 200000000000000.0;
|
||||||
|
let max = 400000000000000.0;
|
||||||
|
Some(part_one_with_area(input, min, max))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
let points = parse_input(input);
|
||||||
|
|
||||||
|
let ctx = z3::Context::new(&z3::Config::new());
|
||||||
|
let solver = z3::Solver::new(&ctx);
|
||||||
|
|
||||||
|
let zero = Int::from_u64(&ctx, 0);
|
||||||
|
let [a, b, c, da, db, dc] = ["a", "b", "c", "da", "db", "dc"].map(|x| Int::new_const(&ctx, x));
|
||||||
|
|
||||||
|
for (i, ((x, y, z), (dx, dy, dz))) in points.iter().enumerate().take(3) {
|
||||||
|
let [x, y, z, dx, dy, dz] = [x, y, z, dx, dy, dz].map(|w| Int::from_i64(&ctx, *w as i64));
|
||||||
|
let t = Int::new_const(&ctx, format!("t_{i}").as_str());
|
||||||
|
solver.assert(&t.ge(&zero));
|
||||||
|
solver.assert(&((&x + &dx * &t)._eq(&(&a + &da * &t))));
|
||||||
|
solver.assert(&((&y + &dy * &t)._eq(&(&b + &db * &t))));
|
||||||
|
solver.assert(&((&z + &dz * &t)._eq(&(&c + &dc * &t))));
|
||||||
|
}
|
||||||
|
|
||||||
|
solver.check();
|
||||||
|
let model = solver.get_model().unwrap();
|
||||||
|
let res = model.eval(&(&a + &b + &c), true).unwrap();
|
||||||
|
|
||||||
|
res.as_u64()
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(24);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let input = aoc::template::read_file("examples", 24);
|
||||||
|
let min = 7.0;
|
||||||
|
let max = 27.0;
|
||||||
|
assert_eq!(part_one_with_area(&input, min, max), 2);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 24)),
|
||||||
|
Some(47)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
80
src/bin/25.rs
Normal file
80
src/bin/25.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn build_graph(input: &str, skips: &[(&str, &str)]) -> Vec<Vec<usize>> {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut mapper = HashMap::new();
|
||||||
|
|
||||||
|
for (node, neighs) in input.lines().filter_map(|x| x.split_once(": ")) {
|
||||||
|
if !mapper.contains_key(node) {
|
||||||
|
mapper.insert(node, nodes.len());
|
||||||
|
nodes.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let index_a = mapper[node];
|
||||||
|
|
||||||
|
for n in neighs.split(' ') {
|
||||||
|
if !mapper.contains_key(n) {
|
||||||
|
mapper.insert(n, nodes.len());
|
||||||
|
nodes.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
if skips.contains(&(node, n)) || skips.contains(&(n, node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index_b = mapper[n];
|
||||||
|
nodes[index_a].push(index_b);
|
||||||
|
nodes[index_b].push(index_a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one_wrapped(input: &str, skips: &[(&str, &str)]) -> Option<usize> {
|
||||||
|
let nodes = build_graph(input, skips);
|
||||||
|
|
||||||
|
let mut visited = vec![false; nodes.len()];
|
||||||
|
let mut stack = vec![0];
|
||||||
|
|
||||||
|
while let Some(node) = stack.pop() {
|
||||||
|
if visited[node] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited[node] = true;
|
||||||
|
for n in nodes[node].iter().copied() {
|
||||||
|
if !visited[n] {
|
||||||
|
stack.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let counter = visited.iter().copied().filter(|&x| x).count();
|
||||||
|
Some((nodes.len() - counter) * counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
// to figure out which nodes to skip
|
||||||
|
// echo "graph A {\n$(cat data/inputs/25.txt)\n}" | \
|
||||||
|
// sed 's/\(.*\): \(.*\)$/\1 -- {\2}/' | \
|
||||||
|
// dot -Tsvg -Kneato > graph.svg
|
||||||
|
let skips = [("sfm", "vmt"), ("vph", "mfc"), ("fql", "rmg")];
|
||||||
|
part_one_wrapped(input, &skips)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(_input: &str) -> Option<String> {
|
||||||
|
Some("Happy chrismas!".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(25);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let skips = [("pzl", "hfx"), ("bvb", "cmg"), ("nvd", "jqt")];
|
||||||
|
let input = aoc::template::read_file("examples", 25);
|
||||||
|
assert_eq!(part_one_wrapped(&input, &skips), Some(54));
|
||||||
|
}
|
||||||
|
}
|
24
src/lib.rs
24
src/lib.rs
@@ -2,3 +2,27 @@
|
|||||||
|
|
||||||
pub mod parsers;
|
pub mod parsers;
|
||||||
pub mod template;
|
pub mod template;
|
||||||
|
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
pub fn lcm(first: usize, second: usize) -> usize {
|
||||||
|
first * second / gcd(first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcd(first: usize, second: usize) -> usize {
|
||||||
|
let mut max = first;
|
||||||
|
let mut min = second;
|
||||||
|
if min > max {
|
||||||
|
swap(&mut min, &mut max)
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let res = max % min;
|
||||||
|
if res == 0 {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
max = min;
|
||||||
|
min = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -54,3 +54,14 @@ pub fn read_file(folder: &str, day: u8) -> String {
|
|||||||
let f = fs::read_to_string(filepath);
|
let f = fs::read_to_string(filepath);
|
||||||
f.expect("could not open input file").trim().to_string()
|
f.expect("could not open input file").trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn read_file_part(folder: &str, day: u8, part: u8) -> String {
|
||||||
|
let cwd = env::current_dir().unwrap();
|
||||||
|
let filepath = cwd
|
||||||
|
.join("data")
|
||||||
|
.join(folder)
|
||||||
|
.join(format!("{day:02}-{part}.txt"));
|
||||||
|
let f = fs::read_to_string(filepath);
|
||||||
|
f.expect("could not open input file").trim().to_string()
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user