Compare commits

...

27 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
Johannes
c162fcb6d9 day24 p1 wrong (example ok) 2018-12-29 16:43:39 +01:00
Johannes
097cfac516 day24 part1 input parsing 2018-12-26 23:30:39 +01:00
Johannes
2b51e67fc6 day23 hmmm 2018-12-26 22:27:36 +01:00
35 changed files with 9774 additions and 257 deletions

373
Cargo.lock generated
View File

@@ -1,177 +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)",
"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 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,9 +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"
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

23
input/day24.txt Normal file
View File

@@ -0,0 +1,23 @@
Immune System:
3400 units each with 1430 hit points (immune to fire, radiation, slashing) with an attack that does 4 radiation damage at initiative 4
138 units each with 8650 hit points (weak to bludgeoning; immune to slashing, cold, radiation) with an attack that does 576 slashing damage at initiative 16
255 units each with 9469 hit points (weak to radiation, fire) with an attack that does 351 bludgeoning damage at initiative 8
4145 units each with 2591 hit points (immune to cold; weak to slashing) with an attack that does 6 fire damage at initiative 12
3605 units each with 10989 hit points with an attack that does 26 fire damage at initiative 19
865 units each with 11201 hit points with an attack that does 102 slashing damage at initiative 10
633 units each with 10092 hit points (weak to slashing, radiation) with an attack that does 150 slashing damage at initiative 11
2347 units each with 3322 hit points with an attack that does 12 cold damage at initiative 2
7045 units each with 3877 hit points (weak to radiation) with an attack that does 5 bludgeoning damage at initiative 5
1086 units each with 8626 hit points (weak to radiation) with an attack that does 69 slashing damage at initiative 13
Infection:
2152 units each with 12657 hit points (weak to fire, cold) with an attack that does 11 fire damage at initiative 18
40 units each with 39458 hit points (immune to radiation, fire, slashing; weak to bludgeoning) with an attack that does 1519 slashing damage at initiative 7
59 units each with 35138 hit points (immune to radiation; weak to fire) with an attack that does 1105 fire damage at initiative 15
1569 units each with 51364 hit points (weak to radiation) with an attack that does 55 radiation damage at initiative 17
929 units each with 23887 hit points (weak to bludgeoning) with an attack that does 48 cold damage at initiative 14
5264 units each with 14842 hit points (immune to cold, fire; weak to slashing, bludgeoning) with an attack that does 4 bludgeoning damage at initiative 9
1570 units each with 30419 hit points (weak to radiation, cold; immune to fire) with an attack that does 35 slashing damage at initiative 1
1428 units each with 21393 hit points (weak to radiation) with an attack that does 29 cold damage at initiative 6
1014 units each with 25717 hit points (weak to fire) with an attack that does 47 fire damage at initiative 3
7933 units each with 29900 hit points (immune to bludgeoning, radiation, slashing) with an attack that does 5 slashing damage at initiative 20

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::day23::task1();
// aoc_2018::tasks::day15::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
}
.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(),
)
}).min_by_key(|it| it.1)
.len(),
)
})
.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
a == x && b == y
} else {
false
}
} 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,8 +24,7 @@ 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 != ' ' {
} else if c != ' ' {
map[x][y] = match c {
'>' => '-',
'<' => '-',
@@ -43,7 +42,6 @@ fn read_input() -> (Vec<Vec<char>>, Vec<Cart>) {
}
}
}
}
(map, carts)
}
@@ -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();
@@ -30,16 +34,188 @@ pub fn task1() {
println!("There are {} bots in range of the big bot.", bots_in_range);
}
pub fn task2() {
let input = utils::read_file("input/day23.txt");
let regex =
Regex::new(r"^pos=<(?P<x>-?\d+),(?P<y>-?\d+),(?P<z>-?\d+)>, r=(?P<range>\d+)$").unwrap();
let bots: Vec<Bot> = input
.lines()
.map(|line| {
let m = regex.captures(line).unwrap();
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::<isize>().unwrap();
Bot {
center: Point::new(x, y, z),
range,
}
})
.collect();
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;
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.center.distance(&cube.base) <= bot.range)
.count();
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!(
"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 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));
}
}

307
src/tasks/day24.rs Normal file
View File

@@ -0,0 +1,307 @@
use std::cmp::Reverse;
use std::collections::HashMap;
use itertools::Itertools;
use crate::utils;
pub fn task1() {
let input = utils::read_file("input/day24.txt");
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: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::ImmuneSystem)
.map(|(_, it)| it.units)
.sum::<i64>()
);
println!(
"Infection start units: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::Infection)
.map(|(_, it)| it.units)
.sum::<i64>()
);
while !game.is_over() {
game.round();
if game.draw {
return None;
}
}
Some((game.winning_team(), game.remaining_units()))
}
#[derive(Debug)]
struct Game {
groups: HashMap<usize, Group>,
draw: bool,
}
impl Game {
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() {
match line {
"Immune System:" => {
team = Team::ImmuneSystem;
}
"Infection:" => {
team = Team::Infection;
}
"" => (),
group => {
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,
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.values().collect();
all_by_power.sort_unstable_by_key(|a| Reverse((a.effective_power(), a.initiative)));
// for group in all_by_power.iter() {
// println!(
// "{}: {} ({})",
// group.id,
// group.effective_power(),
// group.initiative
// );
// }
// println!("{:?}", all_by_power);
for group in all_by_power.iter() {
if let Some(t) = self
.groups
.values()
.filter(|it| it.team != group.team)
.filter(|it| !target.values().any(|t| *t == it.id))
// .inspect(|it| {
// println!(
// "{} would deal {} damage to {}",
// group.id,
// group.compute_attack_damage_to(it),
// it.id
// )
// })
.max_by_key(|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
// );
continue;
} else {
target.insert(group.id, t.id);
}
}
}
// attack ordered by initiative
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);
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 dying_units = damage / enemy.hp_each;
self.groups.get_mut(enemy_id).unwrap().units -= dying_units;
if dying_units > 0 {
self.draw = false;
}
// println!(
// "{} dealt {} ({} units) damage to {}",
// active_id, damage, dying_units, enemy_id
// );
}
}
// clean up dead groups
self.groups.retain(|_, it| it.alive());
// println!("{:?}", self.groups);
}
fn is_over(&self) -> bool {
self.groups.iter().all(|(_, it)| it.team == Team::Infection)
|| self
.groups
.iter()
.all(|(_, it)| it.team == Team::ImmuneSystem)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
enum Team {
Infection,
ImmuneSystem,
}
#[derive(Debug, Clone)]
struct Group {
id: usize,
team: Team,
units: i64,
hp_each: i64,
weaknesses: Vec<String>,
immunities: Vec<String>,
attack_damage: i64,
attack_type: String,
initiative: u64,
}
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();
if let Some(m) = regex.captures(input) {
let units: i64 = m[1].parse().unwrap();
let hp_each: i64 = m[2].parse().unwrap();
let attack_damage: i64 = m[4].parse().unwrap();
let attack_type = m[5].to_string();
let initiative: u64 = m[6].parse().unwrap();
let mut weaknesses: Vec<String> = Vec::new();
let mut immunities: Vec<String> = Vec::new();
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();
}
}
let group = Group {
id,
team,
units,
hp_each,
weaknesses,
immunities,
attack_damage,
attack_type,
initiative,
};
Some(group)
} else {
None
}
}
fn alive(&self) -> bool {
self.units > 0
}
fn effective_power(&self) -> i64 {
if !self.alive() {
panic!("I have no power, im dead!")
}
self.units * self.attack_damage
}
fn compute_attack_damage_to(&self, other: &Self) -> i64 {
if self.alive() {
if other.weaknesses.contains(&self.attack_type) {
self.effective_power() * 2
} else if other.immunities.contains(&self.attack_type) {
0
} else {
self.effective_power()
}
} else {
panic!("I'm not alive, I cannot attack anyone")
}
}
}
#[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,6 +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)