Compare commits

...

24 Commits

Author SHA1 Message Date
fc803d8a0f Day 24 part 2. 2023-11-14 19:35:00 +01:00
3a9379f877 Day 24 part 1. 2023-11-14 18:48:45 +01:00
241c417a77 intermediate 2023-11-14 16:56:16 +01:00
4cbe831115 Removed warnings. 2023-11-14 16:41:54 +01:00
a4c33ae3d2 Day 21 part 2. Shitty bitty. 2023-11-13 23:22:51 +01:00
48ad7e7a96 Day 21 part 1. 2023-11-13 19:52:52 +01:00
caf307bf2d Day 19 part 2 (manually reverse engineered the assembler instructions). 2023-11-12 23:12:46 +01:00
2792304040 Day 19 part 1. 2023-11-12 21:25:31 +01:00
42ead11aa0 Day 16 part 2. 2023-11-12 19:15:31 +01:00
8e1f42205c Day 16 part 1. 2023-11-12 16:14:45 +01:00
c4ae25d579 Updated dependencies. 2023-11-12 12:36:02 +01:00
f472ace665 Day 25 part 1. 2023-11-10 21:15:00 +01:00
a9b3c90a6c Fixed compiler warnings. 2023-11-10 19:17:51 +01:00
30ef81cfac Day 18 part 2 2023-05-27 01:45:15 +02:00
861e31efee Day 18 part 1 2023-05-27 01:00:21 +02:00
d6c728ccfe Day 17 part 2 2023-05-26 22:25:26 +02:00
8bc589488c Day 17 part 1 2023-05-26 22:23:19 +02:00
cfb3ae497e Reformatted 2023-05-26 10:02:22 +02:00
b922b808fc Day 17 stuff 2023-05-26 10:02:17 +02:00
5c4e9a9e9d Day 17 parsing 2023-05-25 23:01:57 +02:00
b480601ee8 clippy fixes
clippy fixes #2

clippy fixed #3

removed unnecessary stuff
2023-05-20 23:28:37 +02:00
ef2340eaae Migrated to rust edition 2021 2023-05-20 22:39:07 +02:00
Johannes
b2a5da3222 day23 part 2 correct intersection test (speed up by factor 2, yay!) 2019-01-03 11:43:38 +01:00
Johannes
9cbbf63eb1 day23 part 2 (with a shortcut that could potentially break some cases)
I'm only using a lower bound on the number of intersections a cube has.
2019-01-01 18:40:00 +01:00
34 changed files with 9517 additions and 387 deletions

370
Cargo.lock generated
View File

@@ -1,184 +1,368 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.6.9"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "aoc_2018"
version = "0.1.0"
dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bmp",
"chrono",
"gcd",
"itertools",
"lazy_static",
"regex",
]
[[package]]
name = "bmp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69985ff4f58085ac696454692d0b646a66ad1f9cc9be294c91dc51bb5df511ae"
dependencies = [
"byteorder",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "0.1.6"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.6"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "either"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
[[package]]
name = "gcd"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0896cb73353671dbe2b17312c97b55b7032831d28c809c703bece4a392b1df3a"
[[package]]
name = "iana-time-zone"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "itertools"
version = "0.7.11"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"either",
]
[[package]]
name = "js-sys"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.2.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.44"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.1.1"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "num-traits"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
[[package]]
name = "redox_syscall"
version = "0.1.43"
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.1.0"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"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.6.4"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thread_local"
version = "0.3.6"
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "wasm-bindgen"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
dependencies = [
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "time"
version = "0.1.40"
name = "wasm-bindgen-backend"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "ucd-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
name = "wasm-bindgen-macro"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
name = "wasm-bindgen-macro-support"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
name = "wasm-bindgen-shared"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
[metadata]
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0896cb73353671dbe2b17312c97b55b7032831d28c809c703bece4a392b1df3a"
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
"checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d"
"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View File

@@ -2,10 +2,12 @@
name = "aoc_2018"
version = "0.1.0"
authors = ["Johannes <jschaef@mail.uni-paderborn.de>"]
edition = "2018"
edition = "2021"
[dependencies]
regex = "1.1.0"
chrono = "0.4.6"
itertools = "0.7.11"
gcd = "1.1.0"
regex = "1.10.2"
chrono = "0.4.31"
itertools = "0.11.0"
gcd = "1.1.0"
lazy_static = "1.4.0"
bmp = "0.5.0"

4261
input/day16.txt Normal file

File diff suppressed because it is too large Load Diff

1470
input/day17.txt Normal file

File diff suppressed because it is too large Load Diff

50
input/day18.txt Normal file
View File

@@ -0,0 +1,50 @@
|#..|#...|..|.#..|###|.....#.|.......||#..|....||.
#||..##.#........||#...##.|..###.|.||...|.#.|.|.#.
##.#.###....##....|..||#.||##.|.###|........||.##.
#.|.||#...|..####......|.#|#..#.#|##...||..#..|...
.....#..#.|.####..#..#...|||...||.|...#......#..|.
.|..#..#.......|...#.|...|.##....|#|..#|###..#..#.
.##..#..##..|.#|||.##..|..#.##..|....#..#|.##|.|#.
|#..|#...|...|.|.......#.#......|...|.#.|||.|||#.#
|....#...|..#..#.....#.|..#.#..|#|.#|...|..|#..|#|
.#...##..|#.##......##...#|||..|.....#.|..|...|..#
#.....|..|...||.|.|.....|....#|..|#...#|...#.....#
...|...###.||......|..#|..|...|.##|........#|#|..|
|.|.#.#......||#|||..|#....|#.|...#|..|.|.#|#.|.|.
###.#.|....|.......##.#|###.|#.#..#.|.#...#...###.
|###...|.....#.|.##..#...|#.#.|.##.#........#..|..
|.||.|...##...##|......#..|.##.##|..#..|#..#.##...
#....|#.....|...|...|............#..#|.....|.#.|.#
...#..|..|||#.|.......#|...#...##|.......####.|...
.#..|..#..|....||#.##.....|||...#..|.#..|.#..|..##
....#...##.........#....|..#.......#...|.....##.#.
|...|...|....#|####||###..|.|..|.||.#......#.|#...
.#.#|.|.|....#.....||...||..|...##.#..|.|.#......|
..|.......|||.|..#.#......|.|..##.||....|###....#.
##....#.......#.|#.##.........|.|....#...|.#|.|.#|
|#.##...|||||#.##.#...#.|#...|.||.|...|..#...#..|.
...#||..#.......||..|.###.#.|#......||..|.#.....#.
#..|.||#.#...|..........#.....#...#...###||.#.....
#..#.|###|#|..|##...##.#......#|.#.#|..#.......|#.
.|.....|.|..#.###|.#|.##.....|.|..|..|..#..|...##.
.|........#...#..|.|..||#....|....#..|.|........|#
....#.|...#|||...#......#...##......|#....#.||.#..
.|.....|....#......#.|#.|.|.|..#.#.|..##.#||.....#
.....#...|.#|..#..#|#.#|.|..|.#........#|..#|....#
|.||..##...|#.#||..|..#.|..|..#..|..#.|.#|.#...|#.
...|#.###...#..|#..##..||....#.||..#.|.|#.#..|..||
......|#|.#.#|.|....#..##|##|#...|.#.|.#....##|#..
#..||.....#....#....#.#.....|.....#....|....|...#.
.#....#.##..........|.||.#.....#|#|||.#..#|......|
..||..|....#..........#.|...#|.|#.|#..|#||.#...|#|
..#..#.#|......#|.....||.#..##.|.#..#.||...|.|||..
.#....|....#.|#...#..||..||.##..#.||....|.#|....|.
..#|.|.....#....#..|..||..#..##.|.||..||||#.#..|.|
.|#.|.||........#|.#|#....||..#||#...|..........##
..#|.|..|||..###..|||.#..#.#||||.#.|##...|#......|
..|...#|...|.#.#|.#...#.|..||##.#..#.|...#.#.#|#..
#..#..|##.#|......#...|#|##..#.|...#.#.....#..##..
..#.|..###|.|#.|........|.....|.....#..|.|.#...|.#
..#|.|#.#.|#..|....|#...|.....|........|.|##.|#||#
#.....##.#..#..#...|#||.#.#.#..|....|||.|.|......#
...#|#....|.#.#..##.|.....#....|.|||..##.|.#.|.##.

37
input/day19.txt Normal file
View File

@@ -0,0 +1,37 @@
#ip 4
addi 4 16 4
seti 1 9 5
seti 1 5 2
mulr 5 2 1
eqrr 1 3 1
addr 1 4 4
addi 4 1 4
addr 5 0 0
addi 2 1 2
gtrr 2 3 1
addr 4 1 4
seti 2 6 4
addi 5 1 5
gtrr 5 3 1
addr 1 4 4
seti 1 2 4
mulr 4 4 4
addi 3 2 3
mulr 3 3 3
mulr 4 3 3
muli 3 11 3
addi 1 5 1
mulr 1 4 1
addi 1 2 1
addr 3 1 3
addr 4 0 4
seti 0 2 4
setr 4 8 1
mulr 1 4 1
addr 4 1 1
mulr 4 1 1
muli 1 14 1
mulr 1 4 1
addr 3 1 3
seti 0 0 0
seti 0 2 4

32
input/day21.txt Normal file
View File

@@ -0,0 +1,32 @@
#ip 1
seti 123 0 2
bani 2 456 2
eqri 2 72 2
addr 2 1 1
seti 0 0 1
seti 0 3 2
bori 2 65536 5
seti 4843319 1 2
bani 5 255 4
addr 2 4 2
bani 2 16777215 2
muli 2 65899 2
bani 2 16777215 2
gtir 256 5 4
addr 4 1 1
addi 1 1 1
seti 27 4 1
seti 0 7 4
addi 4 1 3
muli 3 256 3
gtrr 3 5 3
addr 3 1 1
addi 1 1 1
seti 25 0 1
addi 4 1 4
seti 17 0 1
setr 4 1 5
seti 7 3 1
eqrr 2 0 4
addr 4 1 1
seti 5 3 1

1405
input/day25.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
fn main() {
aoc_2018::tasks::day24::task1();
// aoc_2018::tasks::day23::task2();
aoc_2018::tasks::day24::task2();
}

View File

@@ -1,5 +1,5 @@
use std::collections::HashSet;
use crate::utils;
use std::collections::HashSet;
pub fn task1() {
let frequency: i32 = utils::read_file("input/day01.txt")
@@ -23,7 +23,8 @@ pub fn task2() {
} else {
Some((*current, true))
}
}).find(|state| state.1);
})
.find(|state| state.1);
println!("Part 2: {} was there already!", final_state.unwrap().0);
}

View File

@@ -1,6 +1,6 @@
use crate::utils;
use std::collections::HashMap;
use std::collections::HashSet;
use crate::utils;
pub fn task1() {
let input = utils::read_file("input/day02.txt");
@@ -10,7 +10,7 @@ pub fn task1() {
for line in input.lines() {
let mut counts = [0u8; 26];
for c in line.chars() {
counts[(c as usize - 'a' as usize)] += 1;
counts[c as usize - 'a' as usize] += 1;
}
if counts.iter().any(|count| *count == 2) {
count_two += 1;
@@ -83,7 +83,7 @@ pub fn task2_linear() {
// has that prefix.
let mut root = Node::default();
for id in input.lines() {
add_id_to_tree(&mut root, &id);
add_id_to_tree(&mut root, id);
}
// find a match..
@@ -92,11 +92,11 @@ pub fn task2_linear() {
}
fn find_some_match(node: &Node) -> Option<String> {
if let Some(result) = check_children_for_match(&node) {
return Some(result);
if let Some(result) = check_children_for_match(node) {
Some(result)
} else {
for child in node.outgoing.values() {
if let Some(result) = find_some_match(&child) {
if let Some(result) = find_some_match(child) {
return Some(result);
}
}
@@ -113,14 +113,14 @@ fn find_some_match(node: &Node) -> Option<String> {
/// suffixes that appear more than once in a child node. Then we look at
/// all possible suffixes from all child nodes. If one suffix appears exactly
/// twice, we have a match.
fn check_children_for_match<'a>(node: &Node<'a>) -> Option<String> {
fn check_children_for_match(node: &Node<'_>) -> Option<String> {
let edges: Vec<_> = node.outgoing.keys().collect();
// create a set of candidate suffixes for each edge
let suffix_candidates: HashMap<char, HashSet<&str>> = edges
.iter()
.map(|c| {
let mut suffix_count = HashMap::<&str, HashSet<&str>>::new();
let ref ids = node.outgoing.get(&c).unwrap().same_prefix;
let ids = &node.outgoing.get(c).unwrap().same_prefix;
for id in ids {
suffix_count
.entry(&id[node.depth + 1..])
@@ -136,23 +136,19 @@ fn check_children_for_match<'a>(node: &Node<'a>) -> Option<String> {
.map(|(suffix, _)| *suffix)
.collect(),
)
}).collect();
// go over all suffixes and count their occurences. If # = 2, match!
})
.collect();
// go over all suffixes and count their occurrences. If # = 2, match!
let mut suffix_counter: HashMap<&str, usize> = HashMap::new();
for suffix_set in suffix_candidates.values() {
for suffix in suffix_set {
*suffix_counter.entry(suffix).or_insert(0) += 1;
}
}
if let Some(suffix) =
suffix_counter
.iter()
.find_map(|(suffix, count)| if *count == 2 { Some(suffix) } else { None })
{
Some(format!("{}{}", node.prefix, &suffix))
} else {
None
}
suffix_counter
.iter()
.find_map(|(suffix, count)| if *count == 2 { Some(suffix) } else { None })
.map(|suffix| format!("{}{}", node.prefix, &suffix))
}
#[derive(Default, Debug)]
@@ -165,12 +161,12 @@ struct Node<'a> {
fn add_id_to_tree<'a>(root: &mut Node<'a>, id: &'a str) {
let mut current = root;
current.same_prefix.insert(&id);
current.same_prefix.insert(id);
for (i, c) in id.chars().enumerate() {
{
let mut next = current.outgoing.entry(c).or_insert(Node::default());
let next = current.outgoing.entry(c).or_insert(Node::default());
next.depth = i + 1;
next.same_prefix.insert(&id);
next.same_prefix.insert(id);
next.prefix = &id[..=i];
}
current = { current }.outgoing.get_mut(&c).unwrap();

View File

@@ -1,6 +1,6 @@
use crate::utils;
use regex::Regex;
use std::collections::HashMap;
use crate::utils;
#[derive(PartialEq)]
enum Use {
@@ -16,19 +16,20 @@ pub fn task1() {
.lines()
.map(|line| {
// #12 @ 590,968: 25x14
let m = re.captures(&line).unwrap();
let m = re.captures(line).unwrap();
let id: u32 = m.get(1).unwrap().as_str().parse().unwrap();
let left: u32 = m.get(2).unwrap().as_str().parse().unwrap();
let top: u32 = m.get(3).unwrap().as_str().parse().unwrap();
let width: u32 = m.get(4).unwrap().as_str().parse().unwrap();
let height: u32 = m.get(5).unwrap().as_str().parse().unwrap();
(id, left, top, width, height)
}).fold(
})
.fold(
HashMap::<(u32, u32), Use>::new(),
|mut map, (id, left, top, width, height)| {
for x in left..left + width {
for y in top..top + height {
if let None = map.get(&(x, y)) {
if map.get(&(x, y)).is_none() {
map.insert((x, y), Use::Single(id));
} else {
map.insert((x, y), Use::Multi);
@@ -38,7 +39,8 @@ pub fn task1() {
map
},
).iter()
)
.iter()
.filter(|it| *it.1 == Use::Multi)
.count();
@@ -53,21 +55,22 @@ pub fn task2() {
let claims: Vec<_> = input
.lines()
.map(|line| {
let m = re.captures(&line).unwrap();
let m = re.captures(line).unwrap();
let id: u32 = m.get(1).unwrap().as_str().parse().unwrap();
let left: u32 = m.get(2).unwrap().as_str().parse().unwrap();
let top: u32 = m.get(3).unwrap().as_str().parse().unwrap();
let width: u32 = m.get(4).unwrap().as_str().parse().unwrap();
let height: u32 = m.get(5).unwrap().as_str().parse().unwrap();
(id, left, top, width, height)
}).collect();
})
.collect();
let distribution: HashMap<(u32, u32), Use> = claims.iter().fold(
HashMap::<(u32, u32), Use>::new(),
|mut map, (id, left, top, width, height)| {
for x in *left..*left + *width {
for y in *top..*top + *height {
if let None = map.get(&(x, y)) {
if map.get(&(x, y)).is_none() {
map.insert((x, y), Use::Single(*id));
} else {
map.insert((x, y), Use::Multi);
@@ -90,7 +93,8 @@ pub fn task2() {
}
}
true
}).unwrap();
})
.unwrap();
println!("Part 2: {}", winner_id);
}

View File

@@ -1,8 +1,8 @@
use crate::utils;
use chrono::prelude::*;
use chrono::Duration;
use chrono::NaiveDateTime;
use std::collections::HashMap;
use crate::utils;
enum Activity {
Starts(u32),
@@ -29,13 +29,14 @@ pub fn task1() {
} else if line.contains("wakes up") {
Activity::Awakens
} else {
let number: u32 = line.split(' ').find(|part| part.starts_with("#")).unwrap()[1..]
let number: u32 = line.split(' ').find(|part| part.starts_with('#')).unwrap()[1..]
.parse()
.unwrap();
Activity::Starts(number)
};
(time, activity)
}).collect();
})
.collect();
input.sort_by_key(|it| it.0);
let mut current_id: u32 = 0;
@@ -45,7 +46,7 @@ pub fn task1() {
let minute = Duration::minutes(1);
for (time, activity) in input.into_iter() {
// for all minutes since last time slot fill arrays
let mut iter_time = last_time.clone();
let mut iter_time = last_time;
while iter_time < time {
if current_state == State::Asleep && iter_time.hour() == 0 {
sleep_map.entry(current_id).or_insert([0; 60])[iter_time.minute() as usize] += 1;
@@ -98,13 +99,14 @@ pub fn task2() {
} else if line.contains("wakes up") {
Activity::Awakens
} else {
let number: u32 = line.split(' ').find(|part| part.starts_with("#")).unwrap()[1..]
let number: u32 = line.split(' ').find(|part| part.starts_with('#')).unwrap()[1..]
.parse()
.unwrap();
Activity::Starts(number)
};
(time, activity)
}).collect();
})
.collect();
input.sort_by_key(|it| it.0);
let mut current_id: u32 = 0;
@@ -114,7 +116,7 @@ pub fn task2() {
let minute = Duration::minutes(1);
for (time, activity) in input.into_iter() {
// for all minutes since last time slot fill arrays
let mut iter_time = last_time.clone();
let mut iter_time = last_time;
while iter_time < time {
if current_state == State::Asleep && iter_time.hour() == 0 {
sleep_map.entry(current_id).or_insert([0; 60])[iter_time.minute() as usize] += 1;

View File

@@ -1,5 +1,5 @@
use std::time::Instant;
use crate::utils;
use std::time::Instant;
pub fn task1() {
let mut input = utils::read_file("input/day05.txt");
@@ -24,9 +24,11 @@ pub fn task2() {
.chars()
.filter(|ch| ch.eq_ignore_ascii_case(&c))
.collect::<String>(),
).len(),
)
.len(),
)
}).min_by_key(|it| it.1)
})
.min_by_key(|it| it.1)
.unwrap();
println!("Duration: {:?}", Instant::now() - start);
@@ -56,6 +58,7 @@ fn reduce(input: &str) -> String {
stack.push(c);
}
stack
}).iter()
})
.iter()
.collect()
}

View File

@@ -1,3 +1,5 @@
use std::cmp::Ordering::{Equal, Greater, Less};
use crate::utils;
pub fn task1() {
@@ -12,7 +14,8 @@ pub fn task1() {
split.next().unwrap().parse().unwrap(),
split.next().unwrap().parse().unwrap(),
)
}).collect();
})
.collect();
let max_x = coordinates.iter().max_by_key(|it| it.0).unwrap().0;
let max_y = coordinates.iter().max_by_key(|it| it.1).unwrap().1;
@@ -26,22 +29,17 @@ pub fn task1() {
area.push(vec);
}
for (_, (a, b)) in coordinates.iter().enumerate() {
for x in 0..area.len() {
for y in 0..area[x].len() {
for (a, b) in coordinates.iter() {
for (x, col) in area.iter_mut().enumerate() {
for (y, cell) in col.iter_mut().enumerate() {
let d = (i32::abs(*a as i32 - x as i32) + i32::abs(*b as i32 - y as i32)) as u16;
area[x][y] = match area[x][y] {
*cell = match *cell {
None => Single(d, (*a, *b)),
Single(dd, (aa, bb)) => {
if dd < d {
Single(dd, (aa, bb))
} else if dd > d {
Single(d, (*a, *b))
} else {
// equal
Multi(d)
}
}
Single(dd, (aa, bb)) => match dd.cmp(&d) {
Less => Single(dd, (aa, bb)),
Equal => Single(d, (*a, *b)),
Greater => Multi(d),
},
Multi(dd) => {
if d < dd {
Single(d, (*a, *b))
@@ -60,18 +58,15 @@ pub fn task1() {
.map(|(_, (a, b))| {
let count = area
.iter()
.flat_map(|v| v)
.flatten()
.filter(|entry| {
if let Single(_, (x, y)) = entry {
if a == x && b == y {
true
} else {
false
}
a == x && b == y
} else {
false
}
}).count();
})
.count();
let infinite = area[0].iter().any(|bla| bla.belongs_to_point(*a, *b))
|| area[area.len() - 1]
.iter()
@@ -82,7 +77,8 @@ pub fn task1() {
.any(|line| line[line.len() - 1].belongs_to_point(*a, *b));
// println!("{} has size {} (infinite: {:?})", i, count, infinite);
(count, infinite)
}).collect::<Vec<_>>();
})
.collect::<Vec<_>>();
println!(
"Overall occupation: {} of {}",
@@ -127,7 +123,8 @@ pub fn task2() {
split.next().unwrap().parse().unwrap(),
split.next().unwrap().parse().unwrap(),
)
}).collect();
})
.collect();
let max_x = coordinates.iter().max_by_key(|it| it.0).unwrap().0;
let max_y = coordinates.iter().max_by_key(|it| it.1).unwrap().1;
@@ -141,10 +138,13 @@ pub fn task2() {
.map(|(a, b)| {
(i32::abs(*a as i32 - x as i32) + i32::abs(*b as i32 - y as i32))
as usize
}).sum::<usize>()
}).filter(|it| *it < 10000)
})
.sum::<usize>()
})
.filter(|it| *it < 10000)
.count()
}).sum();
})
.sum();
println!("Part 2: {}", result);
}

View File

@@ -19,12 +19,12 @@ pub fn task1() {
let mut open: HashSet<char> = tasks
.iter()
.filter(|task| !depends_on.contains_key(task))
.map(|c| *c)
.copied()
.collect();
let mut result = String::new();
while open.len() > 0 {
let next = open.iter().min().unwrap().clone();
while !open.is_empty() {
let next = *open.iter().min().unwrap();
open.remove(&next);
result.push(next);
let newly_open: HashSet<char> = depends_on
@@ -32,7 +32,7 @@ pub fn task1() {
.filter(|(_task, deps)| deps.iter().all(|dep| result.chars().any(|c| c == *dep)))
.map(|(task, _)| *task)
.collect();
open = open.union(&newly_open).map(|c| *c).collect();
open = open.union(&newly_open).copied().collect();
for c in newly_open {
depends_on.remove(&c);
}

View File

@@ -2,7 +2,7 @@ use crate::utils;
pub fn both() {
let input: Vec<usize> = utils::read_file("input/day08.txt")
.split(" ")
.split(' ')
.map(|x| x.parse().unwrap())
.collect();
@@ -23,13 +23,13 @@ impl Node {
let num_meta: usize = input[1];
let mut children = Vec::new();
while num_children > 0 {
let (node, rest) = Node::extract_from(&rest_input);
let (node, rest) = Node::extract_from(rest_input);
children.push(node);
rest_input = rest;
num_children -= 1;
}
let metadata = rest_input[..num_meta].iter().map(|x| *x).collect();
let metadata = rest_input[..num_meta].to_vec();
(Node { metadata, children }, &rest_input[num_meta..])
}
@@ -39,14 +39,14 @@ impl Node {
.children
.iter()
.map(|child| {
let s: usize = child.metadata_sum().clone();
let s: usize = child.metadata_sum();
s
})
.sum::<usize>()
}
fn task2(&self) -> usize {
if self.children.len() > 0 {
if !self.children.is_empty() {
self.metadata
.iter()
.map(|meta| match self.children.get(*meta - 1) {

View File

@@ -30,7 +30,7 @@ pub fn task1() {
let mut time = 0;
loop {
time += 1;
let new_lights = lights.iter().map(|light| light.move_copy()).collect();
let new_lights: Vec<Light> = lights.iter().map(|light| light.move_copy()).collect();
// print_lights(&new_lights);
let new_area = get_area(&new_lights);
// println!("Area: {} ", new_area);
@@ -45,7 +45,7 @@ pub fn task1() {
print_lights(&lights);
}
fn get_area(lights: &Vec<Light>) -> usize {
fn get_area(lights: &[Light]) -> usize {
let xmin = lights.iter().map(|it| it.x).min().unwrap();
let xmax = lights.iter().map(|it| it.x).max().unwrap();
let ymin = lights.iter().map(|it| it.y).min().unwrap();
@@ -53,7 +53,7 @@ fn get_area(lights: &Vec<Light>) -> usize {
(xmax - xmin) as usize * (ymax - ymin) as usize
}
fn print_lights(lights: &Vec<Light>) {
fn print_lights(lights: &[Light]) {
let xmin = lights.iter().map(|it| it.x).min().unwrap();
let xmax = lights.iter().map(|it| it.x).max().unwrap();
let ymin = lights.iter().map(|it| it.y).min().unwrap();
@@ -64,14 +64,12 @@ fn print_lights(lights: &Vec<Light>) {
let mut screen: Vec<Vec<u8>> = Vec::with_capacity(height as usize);
for _ in 0..=height {
let mut chars = Vec::with_capacity(width as usize);
for _ in 0..=width {
chars.push('.' as u8);
}
chars.resize(width as usize + 1, b'.');
screen.push(chars);
}
lights
.iter()
.for_each(|light| screen[(light.y - ymin) as usize][(light.x - xmin) as usize] = '#' as u8);
.for_each(|light| screen[(light.y - ymin) as usize][(light.x - xmin) as usize] = b'#');
for line in screen.iter() {
println!("{}", String::from_utf8(line.clone()).unwrap());
}

View File

@@ -56,9 +56,9 @@ pub fn task2_fast() {
let serial = 6392;
let mut cache: Vec<Vec<i32>> = Vec::with_capacity(301);
for x in 0 as usize..=300 {
for x in 0_usize..=300 {
let mut v: Vec<i32> = Vec::with_capacity(301);
for y in 0 as usize..=300 {
for y in 0_usize..=300 {
if x == 0 || y == 0 {
v.push(0);
} else {
@@ -72,10 +72,10 @@ pub fn task2_fast() {
}
let values = &cache;
for x in 0 as usize..=300 {
for y in 0 as usize..=300 {
for x in 0_usize..=300 {
for y in 0_usize..=300 {
if x != 0 && y != 0 {
let cached = area_sum(&values, x, y, 1);
let cached = area_sum(values, x, y, 1);
let calc = fuel_level(x as i32, y as i32, serial);
//println!("{},{}: {} ({})", x, y, values[x][y], calc);
assert_eq!(calc, cached);
@@ -87,7 +87,7 @@ pub fn task2_fast() {
.map(|square_size| {
let result = (1..=301 - square_size)
.flat_map(|x: usize| (1..=301 - square_size).map(move |y: usize| (x, y)))
.map(|(x, y)| (x, y, area_sum(&values, x, y, square_size)))
.map(|(x, y)| (x, y, area_sum(values, x, y, square_size)))
.max_by_key(|(_, _, value)| *value)
.unwrap();
(result.0, result.1, result.2, square_size)
@@ -96,7 +96,7 @@ pub fn task2_fast() {
println!("{:?}", result);
}
fn area_sum(values: &Vec<Vec<i32>>, x: usize, y: usize, length: usize) -> i32 {
fn area_sum(values: &[Vec<i32>], x: usize, y: usize, length: usize) -> i32 {
values[x + length - 1][y + length - 1] + values[x - 1][y - 1]
- values[x - 1][y + length - 1]
- values[x + length - 1][y - 1]
@@ -108,7 +108,7 @@ fn fuel_level(x: i32, y: i32, serial: i32) -> i32 {
power += serial;
power *= rack_id;
power = power / 100 % 10;
power = power - 5;
power -= 5;
power
}

View File

@@ -18,7 +18,7 @@ pub fn task1() {
input.next();
for line in input {
let key = line.split(" => ").nth(0).unwrap();
let key = line.split(" => ").next().unwrap();
transformations.insert(key.to_string(), line.chars().last().unwrap());
}

View File

@@ -24,23 +24,21 @@ fn read_input() -> (Vec<Vec<char>>, Vec<Cart>) {
for (x, c) in line.chars().enumerate() {
if tiles.contains(&c) {
map[x][y] = c;
} else {
if c != ' ' {
map[x][y] = match c {
'>' => '-',
'<' => '-',
'^' => '-',
'v' => '-',
_ => panic!("{} is invalid input char at this point", c),
};
carts.push(Cart {
x,
y,
direction: c,
intersections_visited: 0,
active: true,
});
}
} else if c != ' ' {
map[x][y] = match c {
'>' => '-',
'<' => '-',
'^' => '-',
'v' => '-',
_ => panic!("{} is invalid input char at this point", c),
};
carts.push(Cart {
x,
y,
direction: c,
intersections_visited: 0,
active: true,
});
}
}
}
@@ -57,7 +55,7 @@ struct Cart {
active: bool,
}
fn perform_round(map: &Vec<Vec<char>>, carts: &mut Vec<Cart>) {
fn perform_round(map: &[Vec<char>], carts: &mut Vec<Cart>) {
carts.sort_unstable_by(|a, b| {
if a.y == b.y {
a.x.cmp(&b.x)
@@ -73,7 +71,7 @@ fn perform_round(map: &Vec<Vec<char>>, carts: &mut Vec<Cart>) {
.collect();
for cart_index in 0..carts.len() {
let mut cart = &mut carts[cart_index];
let cart = &mut carts[cart_index];
if !cart.active {
continue;
}

View File

@@ -19,7 +19,7 @@ pub fn task1() {
println!("Final full round was {}", round);
println!(
"Result: {}",
game.units.iter().map(|it| it.health).sum::<i32>() * round as i32
game.units.iter().map(|it| it.health).sum::<i32>() * round
);
}
@@ -56,7 +56,7 @@ pub fn task2() {
}
println!(
"Result: {}",
game.units.iter().map(|it| it.health).sum::<i32>() * round as i32
game.units.iter().map(|it| it.health).sum::<i32>() * round
);
}
println!("Searching stopped with lowest win {:?}", lowest_win); // 7169 too low
@@ -158,7 +158,7 @@ impl Game {
from: Position,
target_type: WarriorType,
) -> Option<Position> {
let mut map = Map::from_game(&self, from);
let mut map = Map::from_game(self, from);
for unit in self.units.iter() {
if unit.warrior_type == target_type {
@@ -199,13 +199,9 @@ impl Game {
// } else {
// None
// }
let map = Map::from_game(&self, from);
if let Some(path) = map.shortest_path(from, to) {
// println!("{:?}", path);
Some(*path.get(1).unwrap_or(&from))
} else {
None
}
let map = Map::from_game(self, from);
map.shortest_path(from, to)
.map(|path| *path.get(1).unwrap_or(&from))
}
/// Returns true if a full round was played, false if the round aborted because all
@@ -284,7 +280,7 @@ impl Game {
}
curr += 1;
}
return true;
true
}
fn from_input(input: &Vec<&str>, goblin_attack: i32, elve_attack: i32) -> Self {
@@ -354,7 +350,7 @@ impl Display for Game {
}
}
f.write_str(&line)?;
f.write_str(&"\n")?;
f.write_str("\n")?;
}
Ok(())
}
@@ -387,7 +383,7 @@ impl Display for Map {
});
}
f.write_str(&line)?;
f.write_str(&"\n")?;
f.write_str("\n")?;
}
Ok(())
}
@@ -411,7 +407,7 @@ impl Map {
}
fields[clear.0][clear.1] = MapTile::Empty;
Map {
fields: fields,
fields,
width: game.width,
height: game.height,
}
@@ -487,10 +483,6 @@ impl Map {
}
candidates.sort_unstable();
if let Some(x) = candidates.first() {
Some(*x)
} else {
None
}
candidates.first().copied()
}
}

630
src/tasks/day16.rs Normal file
View File

@@ -0,0 +1,630 @@
use std::collections::{HashMap, HashSet};
use std::ops::{BitAnd, BitOr};
use itertools::Itertools;
pub fn task1() {
let count = include_str!("../../input/day16.txt")
.split_once("\n\n\n\n")
.unwrap()
.0
.split("\n\n")
.map(|sample_input| parse(sample_input))
.map(matches_count)
.filter(|count| *count >= 3)
.count();
println!("Day 16 part1: {count}");
}
pub fn task2() {
let (samples, program) = include_str!("../../input/day16.txt")
.split_once("\n\n\n\n")
.unwrap();
let code_to_op_candidates = samples
.split("\n\n")
.map(|sample_input| parse(sample_input))
.map(|sample| (sample.1.opcode, matches_ops(sample)))
.into_group_map_by(|x| x.0);
// let code_to_op: HashMap<usize, HashSet<Op>> =
let mut code_to_op_candidates: HashMap<_, _> = code_to_op_candidates
.into_iter()
.map(|(code, ops)| {
let mut set: HashSet<_> = HashSet::from_iter(OPS.iter().map(ToOwned::to_owned));
for (_, candidate_set) in ops {
set = set
.intersection(&candidate_set)
.map(ToOwned::to_owned)
.collect();
}
(code, set)
})
.collect();
let mut codes_to_op: HashMap<usize, Op> = HashMap::new();
loop {
let single = code_to_op_candidates.iter().find(|(_, ops)| ops.len() == 1);
let Some(single) = single else {
break;
};
let code = *single.0;
let op = *single.1.iter().next().unwrap();
codes_to_op.insert(code, op);
code_to_op_candidates.iter_mut().for_each(|(_, ops)| {
ops.remove(&op);
});
}
if codes_to_op.len() != OPS.len() {
panic!("Bad final opcode assignments: {codes_to_op:?}");
}
let program: Vec<Instruction> = program.lines().map(parse_instruction).collect();
let mut registers = Registers([0, 0, 0, 0]);
for instruction in program {
let op: Op = codes_to_op[&instruction.opcode];
registers = op.process(registers, instruction);
}
println!("Day 16 part 2: {}", registers.0[0]);
}
fn parse(sample: &str) -> Sample {
let (before, instruction, after) = sample.lines().collect_tuple().unwrap();
let before: Vec<_> = before
.trim_start_matches("Before: [")
.trim_end_matches("]")
.split(", ")
.collect();
let before = Registers::from(before);
let after: Vec<_> = after
.trim_start_matches("After: [")
.trim_end_matches("]")
.split(", ")
.collect();
let after = Registers::from(after);
let instruction = parse_instruction(instruction);
Sample(before, instruction, after)
}
fn parse_instruction(instruction: &str) -> Instruction {
let instruction: Vec<usize> = instruction.split(" ").map(|x| x.parse().unwrap()).collect();
let instruction = Instruction {
opcode: instruction[0],
a: instruction[1],
b: instruction[2],
c: instruction[3],
};
instruction
}
fn matches(sample: Sample, op: Op) -> bool {
op.process(sample.0, sample.1) == sample.2
}
fn matches_count(sample: Sample) -> usize {
let vec = OPS.iter().filter(|op| matches(sample, **op)).collect_vec();
vec.len()
}
fn matches_ops(sample: Sample) -> HashSet<Op> {
OPS.iter()
.filter(|op| matches(sample, **op))
.map(|op| op.to_owned())
.collect()
}
const OPS: &[Op] = &[
Op::AddR,
Op::AddI,
Op::MulR,
Op::MulI,
Op::BanR,
Op::BanI,
Op::BorR,
Op::BorI,
Op::SetR,
Op::SetI,
Op::GtIR,
Op::GtRI,
Op::GtRR,
Op::EqIR,
Op::EqRI,
Op::EqRR,
];
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
enum Op {
AddR,
AddI,
MulR,
MulI,
BanR,
BanI,
BorR,
BorI,
SetR,
SetI,
GtIR,
GtRI,
GtRR,
EqIR,
EqRI,
EqRR,
}
impl Op {
fn process(&self, registers: Registers, instruction: Instruction) -> Registers {
let mut result = registers.0.clone();
match self {
Op::AddR => {
result[instruction.c] = result[instruction.a] + result[instruction.b];
}
Op::AddI => {
result[instruction.c] = result[instruction.a] + instruction.b as i32;
}
Op::MulR => {
result[instruction.c] = result[instruction.a] * result[instruction.b];
}
Op::MulI => {
result[instruction.c] = result[instruction.a] * instruction.b as i32;
}
Op::BanR => {
result[instruction.c] = result[instruction.a].bitand(result[instruction.b]);
}
Op::BanI => {
result[instruction.c] = result[instruction.a].bitand(instruction.b as i32);
}
Op::BorR => {
result[instruction.c] = result[instruction.a].bitor(result[instruction.b]);
}
Op::BorI => {
result[instruction.c] = result[instruction.a].bitor(instruction.b as i32);
}
Op::SetR => {
result[instruction.c] = result[instruction.a];
}
Op::SetI => {
result[instruction.c] = instruction.a as i32;
}
Op::GtIR => {
result[instruction.c] = if instruction.a as i32 > result[instruction.b] {
1
} else {
0
}
}
Op::GtRI => {
result[instruction.c] = if result[instruction.a] > instruction.b as i32 {
1
} else {
0
}
}
Op::GtRR => {
result[instruction.c] = if result[instruction.a] > result[instruction.b] {
1
} else {
0
}
}
Op::EqIR => {
result[instruction.c] = if instruction.a as i32 == result[instruction.b] {
1
} else {
0
}
}
Op::EqRI => {
result[instruction.c] = if result[instruction.a] == instruction.b as i32 {
1
} else {
0
}
}
Op::EqRR => {
result[instruction.c] = if result[instruction.a] == result[instruction.b] {
1
} else {
0
}
}
}
Registers(result)
}
}
#[derive(Copy, Clone)]
struct Sample(Registers, Instruction, Registers);
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
struct Registers([i32; 4]);
impl From<Vec<&str>> for Registers {
fn from(value: Vec<&str>) -> Self {
let values: Vec<_> = value.iter().map(|x| x.parse().unwrap()).collect();
Registers([values[0], values[1], values[2], values[3]])
}
}
#[derive(Copy, Clone)]
struct Instruction {
opcode: usize,
a: usize,
b: usize,
c: usize,
}
#[cfg(test)]
mod test {
use crate::tasks::day16::{matches_count, parse, Instruction, Op, Registers};
#[test]
fn example1() {
let input = "Before: [3, 2, 1, 1]\n\
9 2 1 2\n\
After: [3, 2, 2, 1]";
assert_eq!(matches_count(parse(input)), 3);
}
#[test]
#[allow(non_snake_case)]
fn test_AddR() {
let op = Op::AddR;
let result = op.process(
Registers([3, 4, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 1,
c: 3,
},
);
assert_eq!(7, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_AddI() {
let op = Op::AddI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 1337,
c: 3,
},
);
assert_eq!(1338, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_MulR() {
let op = Op::MulR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 2,
c: 3,
},
);
assert_eq!(6, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_MulI() {
let op = Op::MulI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1337,
c: 3,
},
);
assert_eq!(2 * 1337, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_BanR() {
let op = Op::BanR;
let result = op.process(
Registers([0x10101, 0x10110, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 1,
c: 3,
},
);
assert_eq!(0x10100, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_BanI() {
let op = Op::BanI;
let result = op.process(
Registers([0x10101, 2, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 0x10110,
c: 3,
},
);
assert_eq!(0x10100, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_BorR() {
let op = Op::BorR;
let result = op.process(
Registers([0x10101, 0x10110, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 1,
c: 3,
},
);
assert_eq!(0x10111, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_BorI() {
let op = Op::BorI;
let result = op.process(
Registers([0x10101, 2, 3, 4]),
Instruction {
opcode: 0,
a: 0,
b: 0x10110,
c: 3,
},
);
assert_eq!(0x10111, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_SetR() {
let op = Op::SetR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1,
c: 3,
},
);
assert_eq!(2, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_SetI() {
let op = Op::SetI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1234,
b: 1,
c: 3,
},
);
assert_eq!(1234, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtIR_true() {
let op = Op::GtIR;
let result = op.process(
Registers([1, 29, 3, 4]),
Instruction {
opcode: 0,
a: 30,
b: 1,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtIR_false() {
let op = Op::GtIR;
let result = op.process(
Registers([1, 1337, 3, 4]),
Instruction {
opcode: 0,
a: 1337,
b: 1,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtRI_true() {
let op = Op::GtRI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 2,
b: 1,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtRI_false() {
let op = Op::GtRI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 2,
b: 3,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtRR_true() {
let op = Op::GtRR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 0,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_GtRR() {
let op = Op::GtRR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqIR_true() {
let op = Op::EqIR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 0,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqIR_false() {
let op = Op::EqIR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqRI_true() {
let op = Op::EqRI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 2,
b: 3,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqRI_false() {
let op = Op::EqRI;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqRR_true() {
let op = Op::EqRR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 1,
c: 3,
},
);
assert_eq!(1, result.0[3]);
}
#[test]
#[allow(non_snake_case)]
fn test_EqRR_false() {
let op = Op::EqRR;
let result = op.process(
Registers([1, 2, 3, 4]),
Instruction {
opcode: 0,
a: 1,
b: 2,
c: 3,
},
);
assert_eq!(0, result.0[3]);
}
}

198
src/tasks/day17.rs Normal file
View File

@@ -0,0 +1,198 @@
use bmp::Image;
use itertools::Itertools;
use regex::Regex;
use crate::utils;
use std::{collections::HashMap, fmt::Display};
use Tile::*;
fn horizontal(line: &str) -> Option<(i32, i32, i32)> {
let regex = Regex::new(r"y=(?P<y>\d+), x=(?P<x1>\d+)..(?P<x2>\d+)").unwrap();
regex.captures(line).map(|m| {
(
m.name("y").unwrap().as_str().parse::<i32>().unwrap(),
m.name("x1").unwrap().as_str().parse::<i32>().unwrap(),
m.name("x2").unwrap().as_str().parse::<i32>().unwrap(),
)
})
}
fn vertical(line: &str) -> Option<(i32, i32, i32)> {
let regex = Regex::new(r"x=(?P<y>\d+), y=(?P<x1>\d+)..(?P<x2>\d+)").unwrap();
regex.captures(line).map(|m| {
(
m.name("y").unwrap().as_str().parse::<i32>().unwrap(),
m.name("x1").unwrap().as_str().parse::<i32>().unwrap(),
m.name("x2").unwrap().as_str().parse::<i32>().unwrap(),
)
})
}
pub fn task1() {
let input = utils::read_file("input/day17.txt");
let mut clay_tiles: HashMap<Pos, Tile> =
input.lines().fold(HashMap::new(), |mut tiles, line| {
if let Some((x, y1, y2)) = vertical(line) {
for y in y1..=y2 {
tiles.insert(Pos(x, y), Clay);
}
}
if let Some((y, x1, x2)) = horizontal(line) {
for x in x1..=x2 {
tiles.insert(Pos(x, y), Clay);
}
}
tiles
});
let (y_min, y_max) = clay_tiles
.keys()
.map(|p| p.1)
.minmax()
.into_option()
.unwrap();
flow(&mut clay_tiles, y_max, Pos(500, 0), Pos(500, 0));
let water_count = clay_tiles
.iter()
.filter(|(_, tile)| **tile == StuckWater || **tile == FallingWater)
.filter(|(p, _)| (y_min..=y_max).contains(&p.1))
.count();
println!("part 1 water count: {water_count}");
// print_tiles(&clay_tiles);
// 30605 too low
print_bmp(&clay_tiles);
let stuck_water = clay_tiles
.iter()
.filter(|(_, tile)| **tile == StuckWater)
.filter(|(p, _)| (y_min..=y_max).contains(&p.1))
.count();
println!("part 2 water count: {stuck_water}");
}
#[allow(dead_code)]
fn print_tiles(tiles: &HashMap<Pos, Tile>) {
let (x_min, x_max) = tiles.keys().map(|p| p.0).minmax().into_option().unwrap();
let (_y_min, y_max) = tiles.keys().map(|p| p.1).minmax().into_option().unwrap();
for y in 0..=y_max {
for x in x_min..=x_max {
let s = match tiles.get(&Pos(x, y)) {
Some(t) => format!("{t}"),
None => " ".to_string(),
};
print!("{s}");
}
print!("\n");
}
}
fn print_bmp(tiles: &HashMap<Pos, Tile>) {
let (x_min, x_max) = tiles.keys().map(|p| p.0).minmax().into_option().unwrap();
let (y_min, y_max) = tiles.keys().map(|p| p.1).minmax().into_option().unwrap();
let mut img = Image::new((x_max - x_min + 1) as u32, (y_max - y_min + 1) as u32);
for y in 0..=y_max {
for x in x_min..=x_max {
let color = match tiles.get(&Pos(x, y)) {
Some(t) => match t {
Clay => bmp::consts::BROWN,
FallingWater => bmp::consts::LIGHT_BLUE,
StuckWater => bmp::consts::DARK_BLUE,
},
None => bmp::consts::WHITE,
};
img.set_pixel((x - x_min) as u32, (y - y_min) as u32, color);
}
}
img.save("final.bmp").unwrap();
}
/// returns true iff going left/right and hit a wall
fn flow(tiles: &mut HashMap<Pos, Tile>, y_max: i32, pos: Pos, from: Pos) -> bool {
if pos.1 > y_max {
return false;
}
if let Some(Clay) = tiles.get(&pos) {
return true;
}
if let Some(StuckWater) = tiles.get(&pos) {
return true;
}
if tiles.contains_key(&pos) {
return false;
}
tiles.insert(pos, FallingWater);
// println!();
// print_tiles(tiles);
if flow(tiles, y_max, pos.down(), pos) {
let bumped_left = flow(tiles, y_max, pos.left(), pos);
let bumped_right = flow(tiles, y_max, pos.right(), pos);
if from.down() == pos {
if bumped_right && bumped_left {
fill(tiles, pos);
// println!();
// print_tiles(tiles);
return true;
}
} else if from.right() == pos && bumped_right {
return true;
} else if from.left() == pos && bumped_left {
return true;
}
}
false
}
fn fill(tiles: &mut HashMap<Pos, Tile>, pos: Pos) {
if let Some(FallingWater) = tiles.get(&pos) {
tiles.insert(pos, StuckWater);
fill(tiles, pos.left());
fill(tiles, pos.right());
}
}
#[derive(PartialEq, Eq)]
enum Tile {
Clay,
FallingWater,
StuckWater,
}
impl Display for Tile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Clay => "#",
FallingWater => "|",
StuckWater => "~",
};
f.write_str(s)
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
struct Pos(i32, i32);
impl Pos {
fn left(&self) -> Self {
Pos(self.0 - 1, self.1)
}
fn right(&self) -> Self {
Pos(self.0 + 1, self.1)
}
fn down(&self) -> Self {
Pos(self.0, self.1 + 1)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn part1() {
assert_eq!(horizontal("y=10, x=5..20"), Some((10, 5, 20)));
assert_eq!(vertical("x=10, y=5..20"), Some((10, 5, 20)));
}
}

145
src/tasks/day18.rs Normal file
View File

@@ -0,0 +1,145 @@
use std::collections::HashMap;
use itertools::Itertools;
use crate::utils;
pub fn task1() {
let initial = load_initial_map();
let fin = (0..10).fold(initial, |map, _| next_map(map));
let (trees, lumberyards) = resource_value(fin);
println!("Part 1 - {trees} x {lumberyards} = {}", trees * lumberyards);
}
pub fn task2() {
let mut map = load_initial_map();
const MINUTES: i32 = 1000000000;
let mut maps_at_time: HashMap<String, i32> = HashMap::new();
let mut minute = 0;
while minute < MINUTES {
if let Some(time) = maps_at_time.get(&hash(&map)) {
let delta = minute - time;
let remaining_minutes = MINUTES - minute;
let remaining_cycles = remaining_minutes / delta;
minute += remaining_cycles * delta;
}
maps_at_time.insert(hash(&map), minute);
minute += 1;
map = next_map(map);
}
let (trees, lumberyards) = resource_value(map);
println!("Part 1 - {trees} x {lumberyards} = {}", trees * lumberyards);
}
fn hash(map: &HashMap<Pos, char>) -> String {
let mut result = String::new();
let (x_min, x_max) = map.keys().map(|p| p.0).minmax().into_option().unwrap();
let (_y_min, y_max) = map.keys().map(|p| p.1).minmax().into_option().unwrap();
for y in 0..=y_max {
for x in x_min..=x_max {
let s = map[&Pos(x, y)];
result.push(s);
}
}
result
}
fn resource_value(fin: HashMap<Pos, char>) -> (usize, usize) {
let trees = fin.values().filter(|c| **c == '|').count();
let lumberyards = fin.values().filter(|c| **c == '#').count();
(trees, lumberyards)
}
fn load_initial_map() -> HashMap<Pos, char> {
let input = utils::read_file("input/day18.txt");
let initial: HashMap<Pos, char> = input
.lines()
.enumerate()
.flat_map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| (Pos(x as i32, y as i32), c))
.collect_vec()
})
.collect();
initial
}
fn next_map(map: HashMap<Pos, char>) -> HashMap<Pos, char> {
map.keys()
.map(|p| {
let old = map[p];
let new = match old {
'.' => {
if neighbors(&map, *p)
.into_iter()
.filter(|c| *c == '|')
.count()
>= 3
{
'|'
} else {
'.'
}
}
'|' => {
if neighbors(&map, *p)
.into_iter()
.filter(|c| *c == '#')
.count()
>= 3
{
'#'
} else {
'|'
}
}
'#' => {
if neighbors(&map, *p).contains(&'#') && neighbors(&map, *p).contains(&'|') {
'#'
} else {
'.'
}
}
_ => panic!("unknown type"),
};
(*p, new)
})
.collect()
}
#[allow(dead_code)]
fn printmap(map: &HashMap<Pos, char>) {
println!();
let (x_min, x_max) = map.keys().map(|p| p.0).minmax().into_option().unwrap();
let (_y_min, y_max) = map.keys().map(|p| p.1).minmax().into_option().unwrap();
for y in 0..=y_max {
for x in x_min..=x_max {
let s = map[&Pos(x, y)];
print!("{s}");
}
print!("\n");
}
}
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
struct Pos(i32, i32);
fn neighbors(map: &HashMap<Pos, char>, p: Pos) -> Vec<char> {
[
Pos(p.0 - 1, p.1 - 1),
Pos(p.0 - 1, p.1),
Pos(p.0 - 1, p.1 + 1),
Pos(p.0, p.1 - 1),
Pos(p.0, p.1 + 1),
Pos(p.0 + 1, p.1 - 1),
Pos(p.0 + 1, p.1),
Pos(p.0 + 1, p.1 + 1),
]
.into_iter()
.flat_map(|pos| map.get(&pos).map(|c| *c))
.collect_vec()
}

243
src/tasks/day19.rs Normal file
View File

@@ -0,0 +1,243 @@
use std::ops::{BitAnd, BitOr};
pub fn task1() {
let input = include_str!("../../input/day19.txt");
let result = run1(input, 1);
println!("Day 19 part 1: {result}");
}
pub fn task2() {
let mut a = 1i64;
let mut d = 948;
if a == 1 {
d += 10550400;
a = 0;
}
let mut f = 1;
'outer: loop {
let mut c = 1;
'inner: loop {
let b = f * c;
if b == d {
a = a + f;
} else if b > d {
// this is the important part: break the loop because otherwise we count and count
// and count ... but cannot reach the b == d condition without resetting c.
break 'inner;
}
c += 1;
if c > d {
break 'inner;
}
}
f += 1;
if f > d {
break 'outer;
}
}
println!("Day 19 part 2: {a}");
}
fn run1(input: &str, start_value: i32) -> i32 {
let (config, program) = input.split_once("\n").unwrap();
let ip_register: usize = config.split_once(" ").unwrap().1.parse().unwrap();
let program: Vec<Instruction> = program.lines().map(parse_instruction).collect();
let mut registers: Registers = [start_value, 0, 0, 0, 0, 0];
let mut ip = 0;
let mut count = 0;
loop {
//print!("ip={ip}, {registers:?}");
registers[ip_register] = ip as i32;
let instruction = program.get(ip).unwrap();
if ip == 2 {
println!("{registers:?}");
}
//print!("{instruction:?}");
let op: Op = instruction.opcode;
registers = op.process(registers, *instruction);
//println!("{registers:?}");
ip = registers[ip_register] as usize;
ip += 1;
if program.get(ip).is_none() {
break;
}
count += 1;
}
println!("executed instructions: {count}");
registers[0]
}
fn parse_instruction(instruction: &str) -> Instruction {
let instruction: Vec<_> = instruction.split(" ").collect();
let opcode = match instruction[0] {
"addr" => Op::AddR,
"addi" => Op::AddI,
"mulr" => Op::MulR,
"muli" => Op::MulI,
"banr" => Op::BanR,
"bani" => Op::BanI,
"borr" => Op::BorR,
"bori" => Op::BorI,
"setr" => Op::SetR,
"seti" => Op::SetI,
"gtir" => Op::GtIR,
"gtri" => Op::GtRI,
"gtrr" => Op::GtRR,
"eqir" => Op::EqIR,
"eqri" => Op::EqRI,
"eqrr" => Op::EqRR,
&_ => {
panic!("unknown op {}", instruction[0])
}
};
let instruction = Instruction {
opcode,
a: instruction[1].parse().unwrap(),
b: instruction[2].parse().unwrap(),
c: instruction[3].parse().unwrap(),
};
instruction
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
enum Op {
AddR,
AddI,
MulR,
MulI,
BanR,
BanI,
BorR,
BorI,
SetR,
SetI,
GtIR,
GtRI,
GtRR,
EqIR,
EqRI,
EqRR,
}
impl Op {
fn process(&self, registers: Registers, instruction: Instruction) -> Registers {
let mut result = registers.clone();
match self {
Op::AddR => {
result[instruction.c] = result[instruction.a] + result[instruction.b];
}
Op::AddI => {
result[instruction.c] = result[instruction.a] + instruction.b as i32;
}
Op::MulR => {
result[instruction.c] = result[instruction.a] * result[instruction.b];
}
Op::MulI => {
result[instruction.c] = result[instruction.a] * instruction.b as i32;
}
Op::BanR => {
result[instruction.c] = result[instruction.a].bitand(result[instruction.b]);
}
Op::BanI => {
result[instruction.c] = result[instruction.a].bitand(instruction.b as i32);
}
Op::BorR => {
result[instruction.c] = result[instruction.a].bitor(result[instruction.b]);
}
Op::BorI => {
result[instruction.c] = result[instruction.a].bitor(instruction.b as i32);
}
Op::SetR => {
result[instruction.c] = result[instruction.a];
}
Op::SetI => {
result[instruction.c] = instruction.a as i32;
}
Op::GtIR => {
result[instruction.c] = if instruction.a as i32 > result[instruction.b] {
1
} else {
0
}
}
Op::GtRI => {
result[instruction.c] = if result[instruction.a] > instruction.b as i32 {
1
} else {
0
}
}
Op::GtRR => {
result[instruction.c] = if result[instruction.a] > result[instruction.b] {
1
} else {
0
}
}
Op::EqIR => {
result[instruction.c] = if instruction.a as i32 == result[instruction.b] {
1
} else {
0
}
}
Op::EqRI => {
result[instruction.c] = if result[instruction.a] == instruction.b as i32 {
1
} else {
0
}
}
Op::EqRR => {
result[instruction.c] = if result[instruction.a] == result[instruction.b] {
1
} else {
0
}
}
}
result
}
}
type Registers = [i32; 6];
#[derive(Copy, Clone, Debug)]
struct Instruction {
opcode: Op,
a: usize,
b: usize,
c: usize,
}
#[cfg(test)]
mod test {
use crate::tasks::day19::run1;
#[test]
fn example1() {
let input = "#ip 0\n\
seti 5 0 1\n\
seti 6 0 2\n\
addi 0 1 0\n\
addr 1 2 3\n\
setr 1 0 0\n\
seti 8 0 4\n\
seti 9 0 5";
assert_eq!(run1(input, 0), 6);
}
}

View File

@@ -10,7 +10,7 @@ pub fn task1() {
let input = &input[1..];
let mut map: HashMap<Point, Tile> = HashMap::new();
add_default_neighbors_for_room(&mut map, Point(0, 0));
parse_input(&mut map, &input, Point(0, 0));
parse_input(&mut map, input, Point(0, 0));
// print_map(&map);
@@ -33,14 +33,14 @@ fn parse_input(map: &mut HashMap<Point, Tile>, input: &str, position: Point) ->
}
let mut position = position;
let mut input = input;
let mut iterator = input.chars();
while let Some(c) = iterator.next() {
let iterator = input.chars();
for c in iterator {
match c {
'(' => {
let (parts, rest) = split_parts(&input);
let (parts, rest) = split_parts(input);
let mut middle_points: HashSet<Point> = HashSet::new();
for part in parts {
if part.len() > 0 {
if !part.is_empty() {
for x in parse_input(map, part, position) {
middle_points.insert(x);
}
@@ -48,7 +48,7 @@ fn parse_input(map: &mut HashMap<Point, Tile>, input: &str, position: Point) ->
}
let mut end_points: HashSet<Point> = HashSet::new();
for point in middle_points {
if rest.len() > 0 {
if !rest.is_empty() {
for x in parse_input(map, rest, point) {
end_points.insert(x);
}
@@ -122,7 +122,7 @@ fn split_parts(input: &str) -> (Vec<&str>, &str) {
}
}
let closing_index =
closing_index.expect(&format!("No matching closing parenthesis in {}", input));
closing_index.unwrap_or_else(|| panic!("No matching closing parenthesis in {}", input));
(parts, &input[closing_index + 1..])
}
@@ -132,9 +132,7 @@ fn add_default_neighbors_for_room(map: &mut HashMap<Point, Tile>, position: Poin
map.insert(p, Wall);
}
for p in position.neighbors() {
if !map.contains_key(&p) {
map.insert(p, Unknown);
}
map.entry(p).or_insert(Unknown);
}
}

229
src/tasks/day21.rs Normal file
View File

@@ -0,0 +1,229 @@
use std::collections::HashSet;
use std::ops::{BitAnd, BitOr};
pub fn task1() {
let input = include_str!("../../input/day21.txt");
let result = run1(input, 3007673);
println!("{result:?}");
}
fn run1(input: &str, start_value: i64) -> (i64, usize) {
let (config, program) = input.split_once("\n").unwrap();
let ip_register: usize = config.split_once(" ").unwrap().1.parse().unwrap();
let program: Vec<Instruction> = program.lines().map(parse_instruction).collect();
let mut registers: Registers = [start_value, 0, 0, 0, 0, 0];
let mut ip = 0;
let mut count = 0usize;
let mut seen: HashSet<_> = HashSet::new();
let mut last = 0;
loop {
//print!("ip={ip}, {registers:?}");
registers[ip_register] = ip as i64;
let instruction = program.get(ip).unwrap();
//print!("{instruction:?}");
let op: Op = instruction.opcode;
registers = op.process(registers, *instruction);
//println!("{registers:?}");
ip = registers[ip_register] as usize;
ip += 1;
// Part 1: uncomment this and print look at register 2 to find the value for register 0
// that would cause the program to halt
// if ip == 28 {
// println!("Value in register 2: {}", registers[2]);
// }
// Part 2: 9969507 too high
// 16774755 highest value
// Store all values of register 2 at the possible exit point. Once you see one you already
// saw: you've seen all (there is a loop). The one before that was the one it took the longest
// to reach without seeing anything twice.
if ip == 28 {
if seen.contains(&registers[2]) {
println!("First double: {registers:?}");
println!("Last was {last}");
break;
} else {
seen.insert(registers[2]);
last = registers[2];
}
}
if program.get(ip).is_none() {
println!("exit by leaving instruction space");
break;
}
count += 1;
}
println!("executed instructions: {count}");
(registers[0], count)
}
fn parse_instruction(instruction: &str) -> Instruction {
let instruction: Vec<_> = instruction.split(" ").collect();
let opcode = match instruction[0] {
"addr" => Op::AddR,
"addi" => Op::AddI,
"mulr" => Op::MulR,
"muli" => Op::MulI,
"banr" => Op::BanR,
"bani" => Op::BanI,
"borr" => Op::BorR,
"bori" => Op::BorI,
"setr" => Op::SetR,
"seti" => Op::SetI,
"gtir" => Op::GtIR,
"gtri" => Op::GtRI,
"gtrr" => Op::GtRR,
"eqir" => Op::EqIR,
"eqri" => Op::EqRI,
"eqrr" => Op::EqRR,
&_ => {
panic!("unknown op {}", instruction[0])
}
};
let instruction = Instruction {
opcode,
a: instruction[1].parse().unwrap(),
b: instruction[2].parse().unwrap(),
c: instruction[3].parse().unwrap(),
};
instruction
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
enum Op {
AddR,
AddI,
MulR,
MulI,
BanR,
BanI,
BorR,
BorI,
SetR,
SetI,
GtIR,
GtRI,
GtRR,
EqIR,
EqRI,
EqRR,
}
impl Op {
fn process(&self, registers: Registers, instruction: Instruction) -> Registers {
let mut result = registers.clone();
match self {
Op::AddR => {
result[instruction.c] = result[instruction.a] + result[instruction.b];
}
Op::AddI => {
result[instruction.c] = result[instruction.a] + instruction.b as i64;
}
Op::MulR => {
result[instruction.c] = result[instruction.a] * result[instruction.b];
}
Op::MulI => {
result[instruction.c] = result[instruction.a] * instruction.b as i64;
}
Op::BanR => {
result[instruction.c] = result[instruction.a].bitand(result[instruction.b]);
}
Op::BanI => {
result[instruction.c] = result[instruction.a].bitand(instruction.b as i64);
}
Op::BorR => {
result[instruction.c] = result[instruction.a].bitor(result[instruction.b]);
}
Op::BorI => {
result[instruction.c] = result[instruction.a].bitor(instruction.b as i64);
}
Op::SetR => {
result[instruction.c] = result[instruction.a];
}
Op::SetI => {
result[instruction.c] = instruction.a as i64;
}
Op::GtIR => {
result[instruction.c] = if instruction.a as i64 > result[instruction.b] {
1
} else {
0
}
}
Op::GtRI => {
result[instruction.c] = if result[instruction.a] > instruction.b as i64 {
1
} else {
0
}
}
Op::GtRR => {
result[instruction.c] = if result[instruction.a] > result[instruction.b] {
1
} else {
0
}
}
Op::EqIR => {
result[instruction.c] = if instruction.a as i64 == result[instruction.b] {
1
} else {
0
}
}
Op::EqRI => {
result[instruction.c] = if result[instruction.a] == instruction.b as i64 {
1
} else {
0
}
}
Op::EqRR => {
result[instruction.c] = if result[instruction.a] == result[instruction.b] {
1
} else {
0
}
}
}
result
}
}
type Registers = [i64; 6];
#[derive(Copy, Clone, Debug)]
struct Instruction {
opcode: Op,
a: usize,
b: usize,
c: usize,
}
#[cfg(test)]
mod test {
use crate::tasks::day21::run1;
#[test]
fn example1() {
let input = "#ip 0\n\
seti 5 0 1\n\
seti 6 0 2\n\
addi 0 1 0\n\
addr 1 2 3\n\
setr 1 0 0\n\
seti 8 0 4\n\
seti 9 0 5";
assert_eq!(run1(input, 0).0, 6);
}
}

View File

@@ -60,7 +60,7 @@ impl Cave {
fn erosion_index(&mut self, x: usize, y: usize) -> usize {
if let Some(value) = self.map.get(&(x, y)) {
return *value;
*value
} else {
let geo_index = match (x, y) {
(0, 0) => 0,
@@ -71,7 +71,7 @@ impl Cave {
};
let erosion_index = (geo_index + self.depth) % 20183;
self.map.insert((x, y), erosion_index);
return erosion_index;
erosion_index
}
}
@@ -154,7 +154,7 @@ impl Cave {
(position.0 as i32, position.1 as i32 - 1),
(position.0 as i32, position.1 as i32 + 1),
]
.into_iter()
.iter()
.filter_map(|(x, y)| {
if *x >= 0 && *y >= 0 {
Some(Node(*x as usize, *y as usize, position.2))
@@ -164,10 +164,7 @@ impl Cave {
})
.for_each(|node| {
if self.equipment_allowed_for_region(node.0, node.1, node.2) {
result.push(Edge {
cost: 1,
node: node,
})
result.push(Edge { cost: 1, node })
}
});

View File

@@ -1,6 +1,7 @@
use crate::utils;
extern crate regex;
use regex::Regex;
use std::collections::BinaryHeap;
pub fn task1() {
let input = utils::read_file("input/day23.txt");
@@ -13,9 +14,12 @@ pub fn task1() {
let x = m["x"].parse::<isize>().unwrap();
let y = m["y"].parse::<isize>().unwrap();
let z = m["z"].parse::<isize>().unwrap();
let range = m["range"].parse::<usize>().unwrap();
let range = m["range"].parse::<isize>().unwrap();
Bot { x, y, z, range }
Bot {
center: Point::new(x, y, z),
range,
}
})
.collect();
@@ -41,110 +45,177 @@ pub fn task2() {
let x = m["x"].parse::<isize>().unwrap();
let y = m["y"].parse::<isize>().unwrap();
let z = m["z"].parse::<isize>().unwrap();
let range = m["range"].parse::<usize>().unwrap();
let range = m["range"].parse::<isize>().unwrap();
Bot { x, y, z, range }
Bot {
center: Point::new(x, y, z),
range,
}
})
.collect();
// let r_min = bots.iter().min_by_key(|it| it.range).unwrap().range;
// let r_max = bots.iter().max_by_key(|it| it.range).unwrap().range;
// println!("Radius min max: {}/{}", r_min, r_max);
// let x_min = bots.iter().min_by_key(|it| it.x).unwrap().x;
// let x_max = bots.iter().max_by_key(|it| it.x).unwrap().x;
// println!("X range: {}", x_max - x_min);
// let y_min = bots.iter().min_by_key(|it| it.y).unwrap().y;
// let y_max = bots.iter().max_by_key(|it| it.y).unwrap().y;
// println!("Y range: {}", y_max - y_min);
// let z_min = bots.iter().min_by_key(|it| it.z).unwrap().z;
// let z_max = bots.iter().max_by_key(|it| it.z).unwrap().z;
// println!("Z range: {}", z_max - z_min);
let mut heap: BinaryHeap<Candidate> = BinaryHeap::new();
heap.push(Candidate {
count: bots.len(),
cube: Cube::new(-1 << 32, -1 << 32, -1 << 32, 1 << 33),
});
let mut candidate_points: Vec<(Point, usize)> = Vec::new();
let mut best_candidate_count = 0;
let neighbor_counts: Vec<(Bot, usize)> = bots
.iter()
.flat_map(|bot| bot.corners())
.map(|corner| {
while let Some(Candidate { count, cube }) = heap.pop() {
// println!("{:?}: {} ({})", cube, count, best_candidate_count);
if count < best_candidate_count {
break;
}
if cube.len == 1 {
let count = bots
.iter()
.filter(|bot| bot.distance(&corner) <= bot.range)
.filter(|bot| bot.center.distance(&cube.base) <= bot.range)
.count();
(corner, count)
})
.collect();
let max = neighbor_counts.iter().max_by_key(|it| it.1).unwrap().1;
let start = Bot {
x: 0,
y: 0,
z: 0,
range: 0,
};
let candidates = neighbor_counts.iter().filter(|it| it.1 == max).count();
println!("{} points in range of {} bots", candidates, max);
let candidate = neighbor_counts
.iter()
.filter(|it| it.1 == max)
.min_by_key(|it| it.0.distance(&start));
if count > best_candidate_count {
candidate_points.push((cube.base, count));
println!("pushed with {}!", count);
best_candidate_count = count;
}
} else {
for child in cube.children() {
heap.push(Candidate {
count: bots.iter().filter(|bot| child.intersects(bot)).count(),
cube: child,
})
}
}
}
let origin = Point::new(0, 0, 0);
println!(
"Corner with most bots in range: {:?}",
candidate.unwrap().0.distance(&start)
"Found {} candidates - best is {}.",
candidate_points.len(),
best_candidate_count
);
let best = candidate_points
.iter()
.filter(|(_, count)| *count == best_candidate_count)
.min_by_key(|(point, _)| origin.distance(point));
println!("{:?}", best);
if let Some((best, _)) = best {
println!("{}", best.x + best.y + best.z);
}
// wrong: 37446460,43177892,57318660; 137943012; 102224079;
}
#[derive(Eq, PartialEq)]
struct Candidate {
count: usize,
cube: Cube,
}
impl Ord for Candidate {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.count.cmp(&other.count)
}
}
impl PartialOrd for Candidate {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.count.cmp(&other.count))
}
}
#[derive(Debug)]
struct Bot {
x: isize,
y: isize,
z: isize,
range: usize,
center: Point,
range: isize,
}
impl Bot {
fn distance(&self, other: &Self) -> usize {
((other.x - self.x).abs() + (other.y - self.y).abs() + (other.z - self.z).abs()) as usize
fn distance(&self, other: &Self) -> isize {
self.dist(&other.center)
}
fn corners(&self) -> Vec<Self> {
vec![
Bot {
x: self.x + self.range as isize,
y: self.y,
z: self.z,
range: 0,
},
Bot {
x: self.x - self.range as isize,
y: self.y,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y + self.range as isize,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y - self.range as isize,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y,
z: self.z + self.range as isize,
range: 0,
},
Bot {
x: self.x,
y: self.y,
z: self.z - self.range as isize,
range: 0,
},
]
fn dist(&self, p: &Point) -> isize {
self.center.distance(p)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Point {
x: isize,
y: isize,
z: isize,
}
impl Point {
fn new(x: isize, y: isize, z: isize) -> Self {
Point { x, y, z }
}
fn distance(&self, other: &Point) -> isize {
(other.x - self.x).abs() + (other.y - self.y).abs() + (other.z - self.z).abs()
}
}
#[derive(PartialEq, Eq, Debug)]
struct Cube {
base: Point,
len: isize,
}
impl Cube {
fn new(x: isize, y: isize, z: isize, len: isize) -> Self {
if len < 1 {
panic!("The side length of a cube has to be at least 1");
}
if (len & (len - 1)) != 0 {
panic!("The side length has to be a power of two");
}
Cube {
base: Point::new(x, y, z),
len,
}
}
fn children(&self) -> Vec<Self> {
let l = self.len / 2;
let x = self.base.x;
let y = self.base.y;
let z = self.base.z;
vec![
Cube::new(x + l, y + l, z, l),
Cube::new(x + l, y + l, z + l, l),
Cube::new(x + l, y, z, l),
Cube::new(x + l, y, z + l, l),
Cube::new(x, y + l, z, l),
Cube::new(x, y + l, z + l, l),
Cube::new(x, y, z, l),
Cube::new(x, y, z + l, l),
]
}
fn intersects(&self, bot: &Bot) -> bool {
((self.base.x - bot.center.x).abs()
+ (self.base.x + self.len - bot.center.x).abs()
+ (self.base.y - bot.center.y).abs()
+ (self.base.y + self.len - bot.center.y).abs()
+ (self.base.z - bot.center.z).abs()
+ (self.base.z + self.len - bot.center.z).abs()
- 3 * self.len)
/ 2
<= bot.range
}
}
mod test {
#[test]
fn intersection() {
use super::*;
let cube = Cube::new(0, 0, 0, 16);
let bot = Bot {
center: Point::new(8, 8, 8),
range: 4,
};
assert!(cube.intersects(&bot));
}
}

View File

@@ -1,17 +1,32 @@
use crate::utils;
use std::cmp::Reverse;
use std::collections::HashMap;
pub fn task1() {
let input = "Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
use itertools::Itertools;
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
use crate::utils;
pub fn task1() {
let input = utils::read_file("input/day24.txt");
let mut game = Game::create(&input);
let result = play(&input, 0);
println!("Standing units after the game: {}", result.unwrap().1);
}
pub fn task2() {
let input = utils::read_file("input/day24.txt");
let immune_final_count = (0..)
.map(|boost| play(&input, boost))
.find(|result| result.map(|(team, _)| team) == Some(Team::ImmuneSystem))
.unwrap()
.unwrap()
.1;
println!("Immune systems final count: {immune_final_count}");
}
fn play(input: &str, boost: i64) -> Option<(Team, i64)> {
let mut game = Game::create(&input, boost);
println!(
"Immune start units: {}",
@@ -30,31 +45,41 @@ Infection:
.sum::<i64>()
);
let mut rounds_played = 0;
while !game.is_over() {
game.round();
rounds_played += 1;
if game.draw {
return None;
}
}
println!("{:#?}", game);
println!("Played {} rounds", rounds_played);
println!(
"Standing units after the game: {}",
game.groups.iter().map(|(_, it)| it.units).sum::<i64>()
);
// 21107 too high
// 21004 too high
Some((game.winning_team(), game.remaining_units()))
}
#[derive(Debug)]
struct Game {
groups: HashMap<usize, Group>,
draw: bool,
}
impl Game {
fn create(input: &str) -> Self {
fn remaining_units(&self) -> i64 {
self.groups.values().map(|it| it.units).sum::<i64>()
}
fn winning_team(&self) -> Team {
let teams: Vec<Team> = self
.groups
.values()
.map(|group| group.team)
.unique()
.collect();
if teams.len() != 1 {
panic!("No winning team. Remaining teams: {teams:?}");
}
teams[0]
}
fn create(input: &str, boost: i64) -> Self {
let mut groups = HashMap::new();
let mut team = Team::ImmuneSystem;
for (id, line) in input.lines().enumerate() {
@@ -67,19 +92,27 @@ impl Game {
}
"" => (),
group => {
if let Some(group) = Group::from_str(group, team, id) {
if let Some(mut group) = Group::from_str(group, team, id) {
if group.team == Team::ImmuneSystem {
group.attack_damage += boost;
}
groups.insert(id, group);
} else {
panic!("bad group: {group}");
}
}
}
}
Game { groups }
Game {
groups,
draw: false,
}
}
fn round(&mut self) {
let mut target: HashMap<usize, usize> = HashMap::new();
// lock targets ordered by effective power
let mut all_by_power: Vec<&Group> = self.groups.iter().map(|(_, it)| it).collect();
let mut all_by_power: Vec<&Group> = self.groups.values().collect();
all_by_power.sort_unstable_by_key(|a| Reverse((a.effective_power(), a.initiative)));
// for group in all_by_power.iter() {
// println!(
@@ -93,11 +126,9 @@ impl Game {
for group in all_by_power.iter() {
if let Some(t) = self
.groups
.iter()
.map(|(_, it)| it)
.values()
.filter(|it| it.team != group.team)
.filter(|it| !target.values().any(|t| *t == it.id))
// .filter(|it| group.compute_attack_damage_to(&it) >= it.hp_each)
// .inspect(|it| {
// println!(
// "{} would deal {} damage to {}",
@@ -108,17 +139,17 @@ impl Game {
// })
.max_by_key(|it| {
(
group.compute_attack_damage_to(&it),
group.compute_attack_damage_to(it),
it.effective_power(),
it.initiative,
)
})
{
if group.compute_attack_damage_to(&t) <= 0 {
println!(
"Didn't find a target where {:?} can deal positive damage.",
group
);
if group.compute_attack_damage_to(t) <= 0 {
// println!(
// "Didn't find a target where {:?} can deal positive damage.",
// group
// );
continue;
} else {
target.insert(group.id, t.id);
@@ -127,22 +158,24 @@ impl Game {
}
// attack ordered by initiative
let mut all_ids_by_initiative: Vec<usize> =
self.groups.iter().map(|(_, it)| it.id).collect();
let mut all_ids_by_initiative: Vec<usize> = self.groups.values().map(|it| it.id).collect();
all_ids_by_initiative.sort_unstable_by_key(|id| Reverse(self.groups[id].initiative));
self.draw = true;
for active_id in all_ids_by_initiative {
if !self.groups[&active_id].alive() {
// was killed in this round
println!("Group {} already dead", active_id);
// println!("Group {} already dead", active_id);
continue;
}
if let Some(enemy_id) = target.get(&active_id) {
let enemy = &self.groups[enemy_id];
let damage: i64 = self.groups[&active_id].compute_attack_damage_to(&enemy);
let damage: i64 = self.groups[&active_id].compute_attack_damage_to(enemy);
let dying_units = damage / enemy.hp_each;
if let Some(enemy) = self.groups.get_mut(enemy_id) {
enemy.units -= dying_units;
self.groups.get_mut(enemy_id).unwrap().units -= dying_units;
if dying_units > 0 {
self.draw = false;
}
// println!(
// "{} dealt {} ({} units) damage to {}",
@@ -157,8 +190,7 @@ impl Game {
}
fn is_over(&self) -> bool {
self.groups.len() == 0
|| self.groups.iter().all(|(_, it)| it.team == Team::Infection)
self.groups.iter().all(|(_, it)| it.team == Team::Infection)
|| self
.groups
.iter()
@@ -166,7 +198,7 @@ impl Game {
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
enum Team {
Infection,
ImmuneSystem,
@@ -188,8 +220,9 @@ struct Group {
impl Group {
fn from_str(input: &str, team: Team, id: usize) -> Option<Self> {
// 801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
// 2347 units each with 3322 hit points with an attack that does 12 cold damage at initiative 2
use regex::Regex;
let regex = Regex::new(r"(\d+) units each with (\d+) hit points \((.+)\) with an attack that does (\d+) (\w+) damage at initiative (\d+)").unwrap();
let regex = Regex::new(r"(\d+) units each with (\d+) hit points(.*)with an attack that does (\d+) (\w+) damage at initiative (\d+)").unwrap();
if let Some(m) = regex.captures(input) {
let units: i64 = m[1].parse().unwrap();
let hp_each: i64 = m[2].parse().unwrap();
@@ -198,11 +231,12 @@ impl Group {
let initiative: u64 = m[6].parse().unwrap();
let mut weaknesses: Vec<String> = Vec::new();
let mut immunities: Vec<String> = Vec::new();
for part in m[3].split("; ") {
if part.starts_with("weak to ") {
weaknesses = part[8..].split(", ").map(|it| it.to_string()).collect();
} else if part.starts_with("immune to ") {
immunities = part[10..].split(", ").map(|it| it.to_string()).collect();
let attributes = m[3].trim().trim_start_matches("(").trim_end_matches(")");
for part in attributes.split("; ") {
if let Some(stripped) = part.strip_prefix("weak to ") {
weaknesses = stripped.split(", ").map(|it| it.to_string()).collect();
} else if let Some(stripped) = part.strip_prefix("immune to ") {
immunities = stripped.split(", ").map(|it| it.to_string()).collect();
}
}
@@ -248,3 +282,26 @@ impl Group {
}
}
}
#[cfg(test)]
mod test {
use crate::tasks::day24::play;
const INPUT: &str = "Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
#[test]
fn example1() {
assert_eq!(5216, play(INPUT, 0).unwrap().1);
}
#[test]
fn example2() {
assert_eq!(51, play(INPUT, 1570).unwrap().1);
}
}

122
src/tasks/day25.rs Normal file
View File

@@ -0,0 +1,122 @@
use std::collections::VecDeque;
use crate::utils;
pub fn task1() {
println!("{}", solve(&utils::read_file("input/day25.txt")));
}
fn solve(input: &str) -> u32 {
let mut points: Vec<STP> = input.lines().map(STP::from).collect();
let mut constellations = 0;
while let Some(next_origin) = points.pop() {
constellations += 1;
let mut queue = VecDeque::new();
queue.push_back(next_origin);
while let Some(next) = queue.pop_front() {
let (neighbors, remainder) = points.into_iter().partition(|p| next.is_neighbor_to(p));
points = remainder;
for n in neighbors {
queue.push_back(n);
}
}
}
constellations
}
#[derive(Hash, Eq, PartialEq, Debug)]
struct STP(i32, i32, i32, i32);
impl STP {}
impl STP {
fn distance(&self, other: &Self) -> u32 {
((self.0 - other.0).abs()
+ (self.1 - other.1).abs()
+ (self.2 - other.2).abs()
+ (self.3 - other.3).abs()) as u32
}
fn is_neighbor_to(&self, other: &Self) -> bool {
self.distance(other) <= 3
}
}
impl From<&str> for STP {
fn from(value: &str) -> Self {
let mut split = value.split(",");
STP(
split.next().unwrap().parse().unwrap(),
split.next().unwrap().parse().unwrap(),
split.next().unwrap().parse().unwrap(),
split.next().unwrap().parse().unwrap(),
)
}
}
#[cfg(test)]
mod test {
use crate::tasks::day25::solve;
#[test]
fn example1() {
let input = "0,0,0,0
3,0,0,0
0,3,0,0
0,0,3,0
0,0,0,3
0,0,0,6
9,0,0,0
12,0,0,0";
assert_eq!(solve(input), 2);
}
#[test]
fn example2() {
let input = "-1,2,2,0
0,0,2,-2
0,0,0,-2
-1,2,0,0
-2,-2,-2,2
3,0,2,-1
-1,3,2,2
-1,0,-1,0
0,2,1,-2
3,0,0,0";
assert_eq!(solve(input), 4);
}
#[test]
fn example3() {
let input = "1,-1,0,1
2,0,-1,0
3,2,-1,0
0,0,3,1
0,0,-1,-1
2,3,-2,0
-2,2,0,0
2,-2,0,-1
1,-1,0,-1
3,2,0,2
";
assert_eq!(solve(input), 3);
}
#[test]
fn example4() {
let input = "1,-1,-1,-2
-2,-2,0,1
0,2,1,3
-2,3,-2,1
0,2,3,-2
-1,-1,1,-2
0,-2,-1,0
-2,2,3,-1
1,2,2,0
-1,-2,0,-2
";
assert_eq!(solve(input), 8);
}
}

View File

@@ -13,7 +13,13 @@ pub mod day12;
pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day17;
pub mod day18;
pub mod day19;
pub mod day20;
pub mod day21;
pub mod day22;
pub mod day23;
pub mod day24;
pub mod day25;

View File

@@ -2,7 +2,7 @@ use std::fs::File;
use std::io::prelude::*;
pub fn read_file(path: &str) -> String {
let mut f = File::open(path).expect(&format!("file '{}' not found", path));
let mut f = File::open(path).unwrap_or_else(|_| panic!("file '{}' not found", path));
let mut contents = String::new();
f.read_to_string(&mut contents)