switch to different AoC template
parent
562cd63d87
commit
c6e8a2d19f
|
@ -0,0 +1,12 @@
|
||||||
|
[alias]
|
||||||
|
today = "run --quiet --release --features today -- today"
|
||||||
|
scaffold = "run --quiet --release -- scaffold"
|
||||||
|
download = "run --quiet --release -- download"
|
||||||
|
read = "run --quiet --release -- read"
|
||||||
|
|
||||||
|
solve = "run --quiet --release -- solve"
|
||||||
|
all = "run --quiet --release -- all"
|
||||||
|
time = "run --quiet --release -- time"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
AOC_YEAR = "2023"
|
|
@ -0,0 +1,17 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.txt]
|
||||||
|
insert_final_newline = false
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
|
@ -1 +1,30 @@
|
||||||
input
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
debug/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
||||||
|
# Advent of Code
|
||||||
|
# @see https://old.reddit.com/r/adventofcode/comments/k99rod/sharing_input_data_were_we_requested_not_to/gf2ukkf/?context=3
|
||||||
|
|
||||||
|
data/inputs/*
|
||||||
|
!data/inputs/.keep
|
||||||
|
data/puzzles/*
|
||||||
|
!data/puzzles/.keep
|
||||||
|
|
||||||
|
# Dhat
|
||||||
|
dhat-heap.json
|
||||||
|
|
||||||
|
# Benchmarks
|
||||||
|
|
||||||
|
data/timings.json
|
||||||
|
|
|
@ -2,6 +2,34 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "advent_of_code"
|
||||||
|
version = "0.11.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc-parse",
|
||||||
|
"chrono",
|
||||||
|
"dhat",
|
||||||
|
"num",
|
||||||
|
"pathfinding",
|
||||||
|
"pico-args",
|
||||||
|
"tinyjson",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -11,6 +39,21 @@ dependencies = [
|
||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "aoc-parse"
|
name = "aoc-parse"
|
||||||
version = "0.2.18"
|
version = "0.2.18"
|
||||||
|
@ -25,52 +68,74 @@ dependencies = [
|
||||||
"tuple_utils",
|
"tuple_utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aoc-runner"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d21ef9204ad206a5a3e918e9920da04e1118ad91ce4f23570be964b9d6b9dfcb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aoc-runner-derive"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba8b944269d3fee645d281b1335e1797044db497bb02d0098cc3fdb8900069cc"
|
|
||||||
dependencies = [
|
|
||||||
"aoc-runner-internal",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aoc-runner-internal"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "274b0ba7f3669a45ec0aaacf94eb032a749de880ab776091576cca94037c9982"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aoc2023"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aoc-parse",
|
|
||||||
"aoc-runner",
|
|
||||||
"aoc-runner-derive",
|
|
||||||
"num",
|
|
||||||
"pathfinding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||||
|
|
||||||
|
[[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 = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deprecate-until"
|
name = "deprecate-until"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -80,7 +145,23 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"semver",
|
"semver",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dhat"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f2aaf837aaf456f6706cb46386ba8dffd4013a757e36f4ea05c20dd46b209a3"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"lazy_static",
|
||||||
|
"mintex",
|
||||||
|
"parking_lot",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thousands",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -95,6 +176,12 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.28.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.3"
|
version = "0.14.3"
|
||||||
|
@ -102,10 +189,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "iana-time-zone"
|
||||||
version = "2.1.0"
|
version = "0.1.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
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 = "indexmap"
|
||||||
|
version = "2.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
@ -126,18 +236,68 @@ version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.66"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.150"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mintex"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"sys-info",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -165,28 +325,27 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-iter"
|
name = "num-iter"
|
||||||
version = "0.1.43"
|
version = "0.1.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
|
@ -215,10 +374,48 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathfinding"
|
name = "object"
|
||||||
version = "4.8.0"
|
version = "0.32.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6f4a3f5089b981000cb50ec24320faf7a19649a45e8730e4adf49f78f066528"
|
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinding"
|
||||||
|
version = "4.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35df0d074a99be583b76dc771329be9f7be45ebae444d601285dc7a094c2ac0a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deprecate-until",
|
"deprecate-until",
|
||||||
"fixedbitset",
|
"fixedbitset",
|
||||||
|
@ -230,28 +427,43 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "pico-args"
|
||||||
version = "1.0.70"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "redox_syscall"
|
||||||
version = "1.10.2"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -261,9 +473,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.3"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -276,6 +488,12 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -289,10 +507,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "scopeguard"
|
||||||
version = "1.0.20"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
|
@ -311,7 +535,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -326,10 +550,16 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "smallvec"
|
||||||
version = "1.0.109"
|
version = "1.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -337,36 +567,47 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "sys-info"
|
||||||
version = "2.0.39"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"cc",
|
||||||
"quote",
|
"libc",
|
||||||
"unicode-ident",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.52"
|
version = "1.0.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
|
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.52"
|
version = "1.0.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
|
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thousands"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyjson"
|
||||||
|
version = "2.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tuple_utils"
|
name = "tuple_utils"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -378,3 +619,123 @@ name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
[package]
|
[package]
|
||||||
name = "aoc2023"
|
name = "advent_of_code"
|
||||||
version = "0.1.0"
|
version = "0.11.0"
|
||||||
|
authors = ["Andrew Coleman <penguincoder@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
default-run = "advent_of_code"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[lib]
|
[lib]
|
||||||
name = "aoc2023"
|
doctest = false
|
||||||
path = "src/lib.rs"
|
|
||||||
|
[profile.dhat]
|
||||||
|
inherits = "release"
|
||||||
|
debug = 1
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dhat-heap = ["dhat"]
|
||||||
|
today = ["chrono"]
|
||||||
|
test_lib = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aoc-runner = "0.3.0"
|
aoc-parse = "0.2.18"
|
||||||
aoc-runner-derive = "0.3.0"
|
|
||||||
aoc-parse = "0.2.17"
|
|
||||||
num = "0.4"
|
|
||||||
pathfinding = "4.8.0"
|
|
||||||
|
|
||||||
[profile.release]
|
# Template dependencies
|
||||||
incremental = true
|
chrono = { version = "0.4.31", optional = true }
|
||||||
|
dhat = { version = "0.3.2", optional = true }
|
||||||
|
num = "0.4.1"
|
||||||
|
pathfinding = "4.8.2"
|
||||||
|
pico-args = "0.5.0"
|
||||||
|
tinyjson = "2.5.1"
|
||||||
|
|
||||||
|
# Solution dependencies
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Felix Spoettel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1 @@
|
||||||
|
Template from https://github.com/fspoettel/advent-of-code-rust
|
|
@ -0,0 +1,7 @@
|
||||||
|
two1nine
|
||||||
|
eightwothree
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen
|
|
@ -0,0 +1,4 @@
|
||||||
|
1abc2
|
||||||
|
pqr3stu8vwx
|
||||||
|
a1b2c3d4e5f
|
||||||
|
treb7uchet
|
|
@ -0,0 +1,5 @@
|
||||||
|
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
||||||
|
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
||||||
|
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
||||||
|
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
||||||
|
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|
|
@ -0,0 +1,10 @@
|
||||||
|
467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
|
@ -0,0 +1,6 @@
|
||||||
|
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
|
@ -0,0 +1,33 @@
|
||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4
|
|
@ -0,0 +1,2 @@
|
||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
|
@ -0,0 +1,5 @@
|
||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
|
@ -0,0 +1,10 @@
|
||||||
|
LR
|
||||||
|
|
||||||
|
11A = (11B, XXX)
|
||||||
|
11B = (XXX, 11Z)
|
||||||
|
11Z = (11B, XXX)
|
||||||
|
22A = (22B, XXX)
|
||||||
|
22B = (22C, 22C)
|
||||||
|
22C = (22Z, 22Z)
|
||||||
|
22Z = (22B, 22B)
|
||||||
|
XXX = (XXX, XXX)
|
|
@ -0,0 +1,9 @@
|
||||||
|
RL
|
||||||
|
|
||||||
|
AAA = (BBB, CCC)
|
||||||
|
BBB = (DDD, EEE)
|
||||||
|
CCC = (ZZZ, GGG)
|
||||||
|
DDD = (DDD, DDD)
|
||||||
|
EEE = (EEE, EEE)
|
||||||
|
GGG = (GGG, GGG)
|
||||||
|
ZZZ = (ZZZ, ZZZ)
|
|
@ -0,0 +1,3 @@
|
||||||
|
0 3 6 9 12 15
|
||||||
|
1 3 6 10 15 21
|
||||||
|
10 13 16 21 30 45
|
|
@ -0,0 +1,5 @@
|
||||||
|
..F7.
|
||||||
|
.FJ|.
|
||||||
|
SJ.L7
|
||||||
|
|F--J
|
||||||
|
LJ...
|
|
@ -0,0 +1,9 @@
|
||||||
|
..........
|
||||||
|
.S------7.
|
||||||
|
.|F----7|.
|
||||||
|
.||....||.
|
||||||
|
.||....||.
|
||||||
|
.|L-7F-J|.
|
||||||
|
.|..||..|.
|
||||||
|
.L--JL--J.
|
||||||
|
..........
|
|
@ -0,0 +1,10 @@
|
||||||
|
.F----7F7F7F7F-7....
|
||||||
|
.|F--7||||||||FJ....
|
||||||
|
.||.FJ||||||||L7....
|
||||||
|
FJL7L7LJLJ||LJ.L-7..
|
||||||
|
L--J.L7...LJS7F-7L7.
|
||||||
|
....F-J..F7FJ|L7L7L7
|
||||||
|
....L7.F7||L7|.L7L7|
|
||||||
|
.....|FJLJ|FJ|F7|.LJ
|
||||||
|
....FJL-7.||.||||...
|
||||||
|
....L---J.LJ.LJLJ...
|
|
@ -0,0 +1,5 @@
|
||||||
|
.....
|
||||||
|
.S-7.
|
||||||
|
.|.|.
|
||||||
|
.L-J.
|
||||||
|
.....
|
|
@ -0,0 +1,10 @@
|
||||||
|
...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....
|
|
@ -0,0 +1,6 @@
|
||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
||||||
|
|
||||||
|
.#.##.#.#
|
||||||
|
.##..##..
|
||||||
|
.#.##.#..
|
||||||
|
#......##
|
||||||
|
#......##
|
||||||
|
.#.##.#..
|
||||||
|
.##..##.#
|
||||||
|
|
||||||
|
#..#....#
|
||||||
|
###..##..
|
||||||
|
.##.#####
|
||||||
|
.##.#####
|
||||||
|
###..##..
|
||||||
|
#..#....#
|
||||||
|
#..##...#
|
||||||
|
|
||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##..#...#
|
||||||
|
##...#..#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....
|
|
@ -0,0 +1 @@
|
||||||
|
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
|
|
@ -0,0 +1,10 @@
|
||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
|
@ -0,0 +1,13 @@
|
||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
|
@ -0,0 +1,14 @@
|
||||||
|
R 6 (#70c710)
|
||||||
|
D 5 (#0dc571)
|
||||||
|
L 2 (#5713f0)
|
||||||
|
D 2 (#d2c081)
|
||||||
|
R 2 (#59c680)
|
||||||
|
D 2 (#411b91)
|
||||||
|
L 5 (#8ceee2)
|
||||||
|
U 2 (#caa173)
|
||||||
|
L 1 (#1b58a2)
|
||||||
|
U 2 (#caa171)
|
||||||
|
R 2 (#7807d2)
|
||||||
|
U 3 (#a77fa3)
|
||||||
|
L 2 (#015232)
|
||||||
|
U 2 (#7a21e3)
|
|
@ -0,0 +1,17 @@
|
||||||
|
px{a<2006:qkq,m>2090:A,rfg}
|
||||||
|
pv{a>1716:R,A}
|
||||||
|
lnx{m>1548:A,A}
|
||||||
|
rfg{s<537:gd,x>2440:R,A}
|
||||||
|
qs{s>3448:A,lnx}
|
||||||
|
qkq{x<1416:A,crn}
|
||||||
|
crn{x>2662:A,R}
|
||||||
|
in{s<1351:px,qqz}
|
||||||
|
qqz{s>2770:qs,m<1801:hdj,R}
|
||||||
|
gd{a>3333:R,R}
|
||||||
|
hdj{m>838:A,pv}
|
||||||
|
|
||||||
|
{x=787,m=2655,a=1222,s=2876}
|
||||||
|
{x=1679,m=44,a=2067,s=496}
|
||||||
|
{x=2036,m=264,a=79,s=2244}
|
||||||
|
{x=2461,m=1339,a=466,s=291}
|
||||||
|
{x=2127,m=1623,a=2188,s=1013}
|
|
@ -0,0 +1,5 @@
|
||||||
|
broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> output
|
|
@ -0,0 +1,5 @@
|
||||||
|
broadcaster -> a, b, c
|
||||||
|
%a -> b
|
||||||
|
%b -> c
|
||||||
|
%c -> inv
|
||||||
|
&inv -> a
|
|
@ -0,0 +1,11 @@
|
||||||
|
...........
|
||||||
|
.....###.#.
|
||||||
|
.###.##..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.##..S####.
|
||||||
|
.##..#...#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##..##.##.
|
||||||
|
...........
|
|
@ -1,4 +1,4 @@
|
||||||
use aoc_runner_derive::aoc;
|
advent_of_code::solution!(1);
|
||||||
|
|
||||||
fn parse(input: &str) -> Vec<u32> {
|
fn parse(input: &str) -> Vec<u32> {
|
||||||
input
|
input
|
||||||
|
@ -16,15 +16,13 @@ fn parse(input: &str) -> Vec<u32> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day1, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1_sum(input: &str) -> u32 {
|
|
||||||
let lines = parse(input);
|
let lines = parse(input);
|
||||||
lines.iter().sum()
|
Some(lines.iter().sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day1, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2_sum(input: &str) -> u32 {
|
Some(sub_letters_for_digits(input).iter().sum())
|
||||||
sub_letters_for_digits(input).iter().sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char_to_num(c: char) -> Option<u32> {
|
fn char_to_num(c: char) -> Option<u32> {
|
||||||
|
@ -101,37 +99,15 @@ fn sub_letters_for_digits(input: &str) -> Vec<u32> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"
|
#[test]
|
||||||
1abc2
|
fn test_part_one() {
|
||||||
pqr3stu8vwx
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
a1b2c3d4e5f
|
assert_eq!(result, Some(142));
|
||||||
treb7uchet
|
}
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let expected = [12, 38, 15, 77];
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY, 2));
|
||||||
let data = parse(SAMPLE_DATA);
|
assert_eq!(result, Some(281));
|
||||||
assert_eq!(data, expected);
|
|
||||||
let part1 = part1_sum(SAMPLE_DATA);
|
|
||||||
assert_eq!(part1, 142);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SAMPLE_DATA2: &'static str = r#"
|
|
||||||
two1nine
|
|
||||||
eightwothree
|
|
||||||
abcone2threexyz
|
|
||||||
xtwone3four
|
|
||||||
4nineeightseven2
|
|
||||||
zoneight234
|
|
||||||
7pqrstsixteen
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sample_data_part2() {
|
|
||||||
let data = sub_letters_for_digits(SAMPLE_DATA2);
|
|
||||||
assert_eq!(data, [29, 83, 13, 24, 42, 14, 76]);
|
|
||||||
let part2 = part2_sum(SAMPLE_DATA2);
|
|
||||||
assert_eq!(part2, 281);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(2);
|
||||||
|
|
||||||
const MAX_RED: u32 = 12;
|
const MAX_RED: u32 = 12;
|
||||||
const MAX_GREEN: u32 = 13;
|
const MAX_GREEN: u32 = 13;
|
||||||
|
@ -46,7 +46,6 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day2)]
|
|
||||||
fn parse(input: &str) -> Vec<Game> {
|
fn parse(input: &str) -> Vec<Game> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -81,17 +80,32 @@ fn parse(input: &str) -> Vec<Game> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day2, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1_valid_game_sum(input: &[Game]) -> u32 {
|
Some(
|
||||||
input.iter().filter_map(|game| game.part1_possible()).sum()
|
parse(input)
|
||||||
|
.iter()
|
||||||
|
.filter_map(|game| game.part1_possible())
|
||||||
|
.sum(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day2, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2_power_sum(input: &[Game]) -> u32 {
|
Some(parse(input).iter().map(|game| game.part2_power()).sum())
|
||||||
input.iter().map(|game| game.part2_power()).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(2286));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(3);
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
type PartList = BTreeMap<(usize, usize, char), Vec<u32>>;
|
type PartList = BTreeMap<(usize, usize, char), Vec<u32>>;
|
||||||
|
@ -16,7 +17,6 @@ fn parse(input: &str) -> Vec<Vec<char>> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day3)]
|
|
||||||
fn associate_parts(input: &str) -> PartList {
|
fn associate_parts(input: &str) -> PartList {
|
||||||
let parsed_input = parse(input);
|
let parsed_input = parse(input);
|
||||||
let mut result: PartList = BTreeMap::new();
|
let mut result: PartList = BTreeMap::new();
|
||||||
|
@ -87,46 +87,37 @@ fn associate_parts(input: &str) -> PartList {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day3, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &PartList) -> u32 {
|
|
||||||
let mut sum: u32 = 0;
|
let mut sum: u32 = 0;
|
||||||
for (_coord, plist) in input.iter() {
|
for (_coord, plist) in associate_parts(input).iter() {
|
||||||
sum += plist.iter().sum::<u32>();
|
sum += plist.iter().sum::<u32>();
|
||||||
}
|
}
|
||||||
sum
|
Some(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day3, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &PartList) -> u32 {
|
|
||||||
let mut sum: u32 = 0;
|
let mut sum: u32 = 0;
|
||||||
for (coord, plist) in input.iter() {
|
for (coord, plist) in associate_parts(input).iter() {
|
||||||
if coord.2 == '*' && plist.len() == 2 {
|
if coord.2 == '*' && plist.len() == 2 {
|
||||||
sum += plist.iter().product::<u32>();
|
sum += plist.iter().product::<u32>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sum
|
Some(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"467..114..
|
#[test]
|
||||||
...*......
|
fn test_part_one() {
|
||||||
..35..633.
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
......#...
|
assert_eq!(result, Some(4361));
|
||||||
617*......
|
}
|
||||||
.....+.58.
|
|
||||||
..592.....
|
|
||||||
......755.
|
|
||||||
...$.*....
|
|
||||||
.664.598..
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let parts = associate_parts(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&parts), 4361);
|
assert_eq!(result, Some(467835));
|
||||||
assert_eq!(part2(&parts), 467835);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(4);
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
struct Card {
|
struct Card {
|
||||||
|
@ -15,7 +16,6 @@ impl Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day4)]
|
|
||||||
fn parse(input: &str) -> Vec<Card> {
|
fn parse(input: &str) -> Vec<Card> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -54,11 +54,10 @@ fn parse(input: &str) -> Vec<Card> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day4, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[Card]) -> i32 {
|
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
let base: i32 = 2;
|
let base: i32 = 2;
|
||||||
for c in input.iter() {
|
for c in parse(input).iter() {
|
||||||
let m = c.matching_numbers();
|
let m = c.matching_numbers();
|
||||||
if m.is_empty() {
|
if m.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -68,12 +67,12 @@ fn part1(input: &[Card]) -> i32 {
|
||||||
sum += p;
|
sum += p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sum
|
Some(sum.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day4, part2)]
|
pub fn part_two(input_str: &str) -> Option<u32> {
|
||||||
fn part2(input: &[Card]) -> u32 {
|
|
||||||
let mut cards: [u32; 202] = [1; 202];
|
let mut cards: [u32; 202] = [1; 202];
|
||||||
|
let input = parse(input_str);
|
||||||
for i in input.len()..cards.len() {
|
for i in input.len()..cards.len() {
|
||||||
cards[i] = 0;
|
cards[i] = 0;
|
||||||
}
|
}
|
||||||
|
@ -88,25 +87,22 @@ fn part2(input: &[Card]) -> u32 {
|
||||||
cards[dest] += cards[cur];
|
cards[dest] += cards[cur];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cards.iter().sum()
|
Some(cards.iter().sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
#[test]
|
||||||
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
fn test_part_one() {
|
||||||
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
assert_eq!(result, Some(13));
|
||||||
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
}
|
||||||
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let cards = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&cards), 13);
|
assert_eq!(result, Some(30));
|
||||||
assert_eq!(part2(&cards), 30);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(5);
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::str::Lines;
|
use std::str::Lines;
|
||||||
|
|
||||||
|
@ -43,7 +44,6 @@ fn next_rangemap(lines: &mut Lines) -> Vec<RangeMap> {
|
||||||
rm
|
rm
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day5)]
|
|
||||||
fn parse(input: &str) -> Almanac {
|
fn parse(input: &str) -> Almanac {
|
||||||
let mut lines = input.lines();
|
let mut lines = input.lines();
|
||||||
let seeds = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
|
let seeds = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
|
||||||
|
@ -102,13 +102,13 @@ fn min_location(seeds: &Vec<u64>, input: &Almanac) -> u64 {
|
||||||
*locations.iter().min().unwrap()
|
*locations.iter().min().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day5, part1)]
|
pub fn part_one(input_str: &str) -> Option<u64> {
|
||||||
fn part1(input: &Almanac) -> u64 {
|
let input = parse(input_str);
|
||||||
min_location(&input.seeds, input)
|
Some(min_location(&input.seeds, &input))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day5, part2, bf)]
|
pub fn part_two(input_str: &str) -> Option<u64> {
|
||||||
fn part2(input: &Almanac) -> u64 {
|
let input = parse(input_str);
|
||||||
let mut seeds: Vec<u64> = vec![];
|
let mut seeds: Vec<u64> = vec![];
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
while index < input.seeds.len() {
|
while index < input.seeds.len() {
|
||||||
|
@ -119,52 +119,22 @@ fn part2(input: &Almanac) -> u64 {
|
||||||
seeds.push(n);
|
seeds.push(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
min_location(&seeds, input)
|
Some(min_location(&seeds, &input))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"seeds: 79 14 55 13
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
seed-to-soil map:
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
50 98 2
|
assert_eq!(result, Some(35));
|
||||||
52 50 48
|
}
|
||||||
|
|
||||||
soil-to-fertilizer map:
|
|
||||||
0 15 37
|
|
||||||
37 52 2
|
|
||||||
39 0 15
|
|
||||||
|
|
||||||
fertilizer-to-water map:
|
|
||||||
49 53 8
|
|
||||||
0 11 42
|
|
||||||
42 0 7
|
|
||||||
57 7 4
|
|
||||||
|
|
||||||
water-to-light map:
|
|
||||||
88 18 7
|
|
||||||
18 25 70
|
|
||||||
|
|
||||||
light-to-temperature map:
|
|
||||||
45 77 23
|
|
||||||
81 45 19
|
|
||||||
68 64 13
|
|
||||||
|
|
||||||
temperature-to-humidity map:
|
|
||||||
0 69 1
|
|
||||||
1 0 69
|
|
||||||
|
|
||||||
humidity-to-location map:
|
|
||||||
60 56 37
|
|
||||||
56 93 4
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let maps = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&maps), 35);
|
assert_eq!(result, Some(46));
|
||||||
assert_eq!(part2(&maps), 46);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(6);
|
||||||
|
|
||||||
struct Race {
|
struct Race {
|
||||||
time: u64,
|
time: u64,
|
||||||
|
@ -17,7 +17,6 @@ impl Race {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day6)]
|
|
||||||
fn parse(input: &str) -> Vec<Race> {
|
fn parse(input: &str) -> Vec<Race> {
|
||||||
let mut lines = input.lines();
|
let mut lines = input.lines();
|
||||||
let times: Vec<u64> = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
|
let times: Vec<u64> = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
|
||||||
|
@ -39,19 +38,18 @@ fn parse(input: &str) -> Vec<Race> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day6, part1)]
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
fn part1(input: &[Race]) -> u64 {
|
Some(parse(input)
|
||||||
input
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|race| race.winning_times().len() as u64)
|
.map(|race| race.winning_times().len() as u64)
|
||||||
.collect::<Vec<u64>>()
|
.collect::<Vec<u64>>()
|
||||||
.iter()
|
.iter()
|
||||||
.product()
|
.product())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day6, part2)]
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
fn part2(input: &[Race]) -> u64 {
|
let races = parse(input);
|
||||||
let time = input
|
let time = races
|
||||||
.iter()
|
.iter()
|
||||||
.map(|race| race.time)
|
.map(|race| race.time)
|
||||||
.collect::<Vec<u64>>()
|
.collect::<Vec<u64>>()
|
||||||
|
@ -61,7 +59,7 @@ fn part2(input: &[Race]) -> u64 {
|
||||||
.join("")
|
.join("")
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let distance = input
|
let distance = races
|
||||||
.iter()
|
.iter()
|
||||||
.map(|race| race.distance)
|
.map(|race| race.distance)
|
||||||
.collect::<Vec<u64>>()
|
.collect::<Vec<u64>>()
|
||||||
|
@ -72,21 +70,22 @@ fn part2(input: &[Race]) -> u64 {
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let race = Race { time, distance };
|
let race = Race { time, distance };
|
||||||
race.winning_times().len() as u64
|
Some(race.winning_times().len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"Time: 7 15 30
|
#[test]
|
||||||
Distance: 9 40 200
|
fn test_part_one() {
|
||||||
"#;
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(288));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let races = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&races), 288);
|
assert_eq!(result, Some(71503));
|
||||||
assert_eq!(part2(&races), 71503);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(7);
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -218,7 +219,6 @@ struct Part2Hand {
|
||||||
hand_kind: HandKinds,
|
hand_kind: HandKinds,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day7)]
|
|
||||||
fn parse(input: &str) -> Vec<Hand> {
|
fn parse(input: &str) -> Vec<Hand> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -254,21 +254,19 @@ fn parse(input: &str) -> Vec<Hand> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day7, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[Hand]) -> u64 {
|
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
let mut hands = input.iter().map(|h| h.clone()).collect::<Vec<Hand>>();
|
let mut hands = parse(input).iter().map(|h| h.clone()).collect::<Vec<Hand>>();
|
||||||
hands.sort();
|
hands.sort();
|
||||||
for (index, hand) in hands.iter().rev().enumerate() {
|
for (index, hand) in hands.iter().rev().enumerate() {
|
||||||
sum += hand.bid * (index as u64 + 1);
|
sum += hand.bid * (index as u64 + 1);
|
||||||
}
|
}
|
||||||
sum
|
Some(sum.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day7, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &[Hand]) -> u64 {
|
|
||||||
let mut sum: u64 = 0;
|
let mut sum: u64 = 0;
|
||||||
let mut hands = input
|
let mut hands = parse(input)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| {
|
.map(|h| {
|
||||||
let bid = h.bid;
|
let bid = h.bid;
|
||||||
|
@ -329,24 +327,22 @@ fn part2(input: &[Hand]) -> u64 {
|
||||||
for (index, hand) in hands.iter().rev().enumerate() {
|
for (index, hand) in hands.iter().rev().enumerate() {
|
||||||
sum += hand.bid * (index as u64 + 1);
|
sum += hand.bid * (index as u64 + 1);
|
||||||
}
|
}
|
||||||
sum
|
Some(sum.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"32T3K 765
|
#[test]
|
||||||
T55J5 684
|
fn test_part_one() {
|
||||||
KK677 28
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
KTJJT 220
|
assert_eq!(result, Some(6440));
|
||||||
QQQJA 483
|
}
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let cards = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&cards), 6440);
|
assert_eq!(result, Some(5905));
|
||||||
assert_eq!(part2(&cards), 5905);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::aoc;
|
advent_of_code::solution!(8);
|
||||||
|
|
||||||
use num::integer::lcm;
|
use num::integer::lcm;
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
|
@ -96,62 +97,29 @@ impl<'a> Path<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day8, part1)]
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
fn part1(input: &str) -> u64 {
|
Some(parse(input).traverse())
|
||||||
parse(input).traverse()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day8, part2)]
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
fn part2(input: &str) -> u64 {
|
Some(parse(input).ghost_traverse())
|
||||||
parse(input).ghost_traverse()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA1: &'static str = r#"RL
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
AAA = (BBB, CCC)
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
BBB = (DDD, EEE)
|
assert_eq!(result, Some(2));
|
||||||
CCC = (ZZZ, GGG)
|
}
|
||||||
DDD = (DDD, DDD)
|
|
||||||
EEE = (EEE, EEE)
|
|
||||||
GGG = (GGG, GGG)
|
|
||||||
ZZZ = (ZZZ, ZZZ)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data1() {
|
fn test_part_two() {
|
||||||
assert_eq!(part1(&SAMPLE_DATA1), 2);
|
let result = part_two(&advent_of_code::template::read_file_part(
|
||||||
}
|
"examples", DAY, 2,
|
||||||
|
));
|
||||||
const SAMPLE_DATA2: &'static str = r#"LLR
|
assert_eq!(result, Some(6));
|
||||||
|
|
||||||
AAA = (BBB, BBB)
|
|
||||||
BBB = (AAA, ZZZ)
|
|
||||||
ZZZ = (ZZZ, ZZZ)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sample_data2() {
|
|
||||||
assert_eq!(part1(&SAMPLE_DATA2), 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SAMPLE_DATA_P2: &'static str = r#"LR
|
|
||||||
|
|
||||||
11A = (11B, XXX)
|
|
||||||
11B = (XXX, 11Z)
|
|
||||||
11Z = (11B, XXX)
|
|
||||||
22A = (22B, XXX)
|
|
||||||
22B = (22C, 22C)
|
|
||||||
22C = (22Z, 22Z)
|
|
||||||
22Z = (22B, 22B)
|
|
||||||
XXX = (XXX, XXX)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sample_data_part2() {
|
|
||||||
assert_eq!(part2(&SAMPLE_DATA_P2), 6);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,18 +1,17 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(9);
|
||||||
|
|
||||||
type Seq = Vec<i64>;
|
type Seq = Vec<i32>;
|
||||||
|
|
||||||
fn all_zero(s: &Seq) -> bool {
|
fn all_zero(s: &Seq) -> bool {
|
||||||
s.iter().find(|t| **t != 0).is_none()
|
s.iter().find(|t| **t != 0).is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day9)]
|
|
||||||
fn parse(input: &str) -> Vec<Seq> {
|
fn parse(input: &str) -> Vec<Seq> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|line| {
|
.filter_map(|line| {
|
||||||
if !line.is_empty() {
|
if !line.is_empty() {
|
||||||
let nums = line.split(" ").map(|n| n.parse::<i64>().unwrap()).collect();
|
let nums = line.split(" ").map(|n| n.parse::<i32>().unwrap()).collect();
|
||||||
Some(nums)
|
Some(nums)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -29,7 +28,7 @@ fn get_diffs(s: &Seq) -> Seq {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_number(s: &Seq) -> i64 {
|
fn next_number(s: &Seq) -> i32 {
|
||||||
if all_zero(s) {
|
if all_zero(s) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
@ -38,12 +37,11 @@ fn next_number(s: &Seq) -> i64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day9, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[Seq]) -> i64 {
|
Some(parse(input).iter().map(|s| next_number(&s)).sum::<i32>().try_into().unwrap())
|
||||||
input.iter().map(|s| next_number(&s)).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn previous_number(s: &Seq) -> i64 {
|
fn previous_number(s: &Seq) -> i32 {
|
||||||
if all_zero(s) {
|
if all_zero(s) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,24 +50,23 @@ fn previous_number(s: &Seq) -> i64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day9, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &[Seq]) -> i64 {
|
Some(parse(input).iter().map(|s| previous_number(&s)).sum::<i32>().try_into().unwrap())
|
||||||
input.iter().map(|s| previous_number(&s)).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"0 3 6 9 12 15
|
#[test]
|
||||||
1 3 6 10 15 21
|
fn test_part_one() {
|
||||||
10 13 16 21 30 45
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
"#;
|
assert_eq!(result, Some(114));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 114);
|
assert_eq!(result, Some(2));
|
||||||
assert_eq!(part2(&input), 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(10);
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -41,7 +42,6 @@ impl FromStr for Pieces {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day10)]
|
|
||||||
fn parse(input: &str) -> MetalIsland {
|
fn parse(input: &str) -> MetalIsland {
|
||||||
let mut m: Map = [[Pieces::Empty; 143]; 143];
|
let mut m: Map = [[Pieces::Empty; 143]; 143];
|
||||||
let mut row: usize = 1;
|
let mut row: usize = 1;
|
||||||
|
@ -147,8 +147,8 @@ fn next_step(path: &Vec<Step>, map: &Map) -> Step {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day10, part1)]
|
pub fn part_one(input_str: &str) -> Option<u32> {
|
||||||
fn part1(input: &MetalIsland) -> u64 {
|
let input = parse(input_str);
|
||||||
let next_steps = start_steps(&input.start, &input.map);
|
let next_steps = start_steps(&input.start, &input.map);
|
||||||
let mut left_path: Vec<Step> = vec![];
|
let mut left_path: Vec<Step> = vec![];
|
||||||
left_path.push(input.start);
|
left_path.push(input.start);
|
||||||
|
@ -168,11 +168,11 @@ fn part1(input: &MetalIsland) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(left_path.len(), right_path.len());
|
assert_eq!(left_path.len(), right_path.len());
|
||||||
left_path.len() as u64 - 1
|
Some(left_path.len() as u32 - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day10, part2)]
|
pub fn part_two(input_str: &str) -> Option<u32> {
|
||||||
fn part2(input: &MetalIsland) -> u64 {
|
let input = parse(input_str);
|
||||||
let next_steps = start_steps(&input.start, &input.map);
|
let next_steps = start_steps(&input.start, &input.map);
|
||||||
let mut path: Vec<Step> = vec![];
|
let mut path: Vec<Step> = vec![];
|
||||||
path.push(input.start);
|
path.push(input.start);
|
||||||
|
@ -208,71 +208,40 @@ fn part2(input: &MetalIsland) -> u64 {
|
||||||
// two and not 1. I can't figure out the one-square-duplication.
|
// two and not 1. I can't figure out the one-square-duplication.
|
||||||
count -= 1;
|
count -= 1;
|
||||||
}
|
}
|
||||||
count
|
Some(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#".....
|
#[test]
|
||||||
.S-7.
|
fn test_part_one() {
|
||||||
.|.|.
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
.L-J.
|
assert_eq!(result, Some(4));
|
||||||
.....
|
}
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_one(&advent_of_code::template::read_file_part(
|
||||||
assert_eq!(part1(&input), 4);
|
"examples", DAY, 2,
|
||||||
|
));
|
||||||
|
assert_eq!(result, Some(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAMPLE_DATA2: &'static str = r#"..F7.
|
|
||||||
.FJ|.
|
|
||||||
SJ.L7
|
|
||||||
|F--J
|
|
||||||
LJ...
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data2() {
|
fn test_part_three() {
|
||||||
let input = parse(&SAMPLE_DATA2);
|
let result = part_two(&advent_of_code::template::read_file_part(
|
||||||
assert_eq!(part1(&input), 8);
|
"examples", DAY, 3,
|
||||||
|
));
|
||||||
|
assert_eq!(result, Some(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAMPLE_DATA3: &'static str = r#"..........
|
|
||||||
.S------7.
|
|
||||||
.|F----7|.
|
|
||||||
.||....||.
|
|
||||||
.||....||.
|
|
||||||
.|L-7F-J|.
|
|
||||||
.|..||..|.
|
|
||||||
.L--JL--J.
|
|
||||||
..........
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data3() {
|
fn test_part_four() {
|
||||||
let input = parse(&SAMPLE_DATA3);
|
let result = part_two(&advent_of_code::template::read_file_part(
|
||||||
assert_eq!(part2(&input), 4);
|
"examples", DAY, 4,
|
||||||
}
|
));
|
||||||
|
assert_eq!(result, Some(8));
|
||||||
const SAMPLE_DATA4: &'static str = r#".F----7F7F7F7F-7....
|
|
||||||
.|F--7||||||||FJ....
|
|
||||||
.||.FJ||||||||L7....
|
|
||||||
FJL7L7LJLJ||LJ.L-7..
|
|
||||||
L--J.L7...LJS7F-7L7.
|
|
||||||
....F-J..F7FJ|L7L7L7
|
|
||||||
....L7.F7||L7|.L7L7|
|
|
||||||
.....|FJLJ|FJ|F7|.LJ
|
|
||||||
....FJL-7.||.||||...
|
|
||||||
....L---J.LJ.LJLJ...
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sample_data4() {
|
|
||||||
let input = parse(&SAMPLE_DATA4);
|
|
||||||
assert_eq!(part2(&input), 8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use aoc_runner_derive::aoc;
|
advent_of_code::solution!(11);
|
||||||
|
|
||||||
struct Galaxy(Vec<Coord>);
|
struct Galaxy(Vec<Coord>);
|
||||||
|
|
||||||
|
@ -18,12 +18,12 @@ impl Galaxy {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum_shortest_paths(&self) -> usize {
|
fn sum_shortest_paths(&self) -> u32 {
|
||||||
let mut result: usize = 0;
|
let mut result: usize = 0;
|
||||||
for (src, dest) in self.galaxy_pairs().iter() {
|
for (src, dest) in self.galaxy_pairs().iter() {
|
||||||
result += src.path_len(&dest);
|
result += src.path_len(&dest);
|
||||||
}
|
}
|
||||||
result
|
result as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,35 +123,22 @@ fn parse(input: &str, empty_space: usize) -> Galaxy {
|
||||||
Galaxy(galaxies)
|
Galaxy(galaxies)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day11, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &str) -> usize {
|
Some(parse(&input, 1).sum_shortest_paths())
|
||||||
parse(&input, 1).sum_shortest_paths()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day11, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &str) -> usize {
|
Some(parse(&input, 999999).sum_shortest_paths())
|
||||||
parse(&input, 999999).sum_shortest_paths()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"...#......
|
|
||||||
.......#..
|
|
||||||
#.........
|
|
||||||
..........
|
|
||||||
......#...
|
|
||||||
.#........
|
|
||||||
.........#
|
|
||||||
..........
|
|
||||||
.......#..
|
|
||||||
#...#.....
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_one() {
|
||||||
let input = parse(&SAMPLE_DATA, 1);
|
let input_str = advent_of_code::template::read_file("examples", DAY);
|
||||||
|
let input = parse(&input_str, 1);
|
||||||
assert_eq!(input.0.len(), 9);
|
assert_eq!(input.0.len(), 9);
|
||||||
assert_eq!(input.galaxy_pairs().len(), 36);
|
assert_eq!(input.galaxy_pairs().len(), 36);
|
||||||
assert_eq!(input.0[4].path_len(&input.0[8]), 9);
|
assert_eq!(input.0[4].path_len(&input.0[8]), 9);
|
||||||
|
@ -159,7 +146,7 @@ mod tests {
|
||||||
assert_eq!(input.0[2].path_len(&input.0[5]), 17);
|
assert_eq!(input.0[2].path_len(&input.0[5]), 17);
|
||||||
assert_eq!(input.0[7].path_len(&input.0[8]), 5);
|
assert_eq!(input.0[7].path_len(&input.0[8]), 5);
|
||||||
assert_eq!(input.sum_shortest_paths(), 374);
|
assert_eq!(input.sum_shortest_paths(), 374);
|
||||||
assert_eq!(part1(&SAMPLE_DATA), 374);
|
assert_eq!(part_one(&input_str), Some(374));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -170,8 +157,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2() {
|
fn test_part_two() {
|
||||||
assert_eq!(parse(&SAMPLE_DATA, 9).sum_shortest_paths(), 1030);
|
assert_eq!(
|
||||||
assert_eq!(parse(&SAMPLE_DATA, 99).sum_shortest_paths(), 8410);
|
parse(&advent_of_code::template::read_file("examples", DAY), 9).sum_shortest_paths(),
|
||||||
|
1030
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse(&advent_of_code::template::read_file("examples", DAY), 99).sum_shortest_paths(),
|
||||||
|
8410
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(12);
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -29,7 +30,6 @@ struct Spring {
|
||||||
groups: Vec<usize>,
|
groups: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day12)]
|
|
||||||
fn parse(input: &str) -> Vec<Spring> {
|
fn parse(input: &str) -> Vec<Spring> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -114,18 +114,20 @@ fn calc_damaged_solutions(list: &Vec<Condition>, groups: &Vec<usize>, cache: &mu
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day12, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[Spring]) -> usize {
|
|
||||||
let mut result: usize = 0;
|
let mut result: usize = 0;
|
||||||
for src in input.iter() {
|
for src in parse(input).iter() {
|
||||||
let mut cache: Cache = HashMap::new();
|
let mut cache: Cache = HashMap::new();
|
||||||
result += calc_solutions(&src.list, &src.groups, &mut cache);
|
result += calc_solutions(&src.list, &src.groups, &mut cache);
|
||||||
}
|
}
|
||||||
result
|
Some(result as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day12, part2)]
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
fn part2(input: &[Spring]) -> usize {
|
part2(&parse(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &[Spring]) -> Option<u64> {
|
||||||
let mut result: usize = 0;
|
let mut result: usize = 0;
|
||||||
for src in input.iter() {
|
for src in input.iter() {
|
||||||
let mut l: Vec<Condition> = Vec::with_capacity(src.groups.len() * 5 + 5);
|
let mut l: Vec<Condition> = Vec::with_capacity(src.groups.len() * 5 + 5);
|
||||||
|
@ -141,25 +143,23 @@ fn part2(input: &[Spring]) -> usize {
|
||||||
let s = calc_solutions(&l, &g, &mut cache);
|
let s = calc_solutions(&l, &g, &mut cache);
|
||||||
result += s;
|
result += s;
|
||||||
}
|
}
|
||||||
result
|
Some(result.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"???.### 1,1,3
|
#[test]
|
||||||
.??..??...?##. 1,1,3
|
fn test_part_one() {
|
||||||
?#?#?#?#?#?#?#? 1,3,1,6
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
????.#...#... 4,1,1
|
assert_eq!(result, Some(21));
|
||||||
????.######..#####. 1,6,5
|
}
|
||||||
?###???????? 3,2,1
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data_part1() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 21);
|
assert_eq!(result, Some(525152));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -176,7 +176,7 @@ mod tests {
|
||||||
],
|
],
|
||||||
groups: vec![1, 1, 3],
|
groups: vec![1, 1, 3],
|
||||||
};
|
};
|
||||||
assert_eq!(part2(&[line1]), 1);
|
assert_eq!(part2(&[line1]), Some(1));
|
||||||
|
|
||||||
let line2 = Spring {
|
let line2 = Spring {
|
||||||
list: vec![
|
list: vec![
|
||||||
|
@ -197,12 +197,6 @@ mod tests {
|
||||||
],
|
],
|
||||||
groups: vec![1, 1, 3],
|
groups: vec![1, 1, 3],
|
||||||
};
|
};
|
||||||
assert_eq!(part2(&[line2]), 16384);
|
assert_eq!(part2(&[line2]), Some(16384));
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sample_data_part2() {
|
|
||||||
let input = parse(&SAMPLE_DATA);
|
|
||||||
assert_eq!(part2(&input), 525152);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(13);
|
||||||
|
|
||||||
type Board = Vec<Vec<char>>;
|
type Board = Vec<Vec<char>>;
|
||||||
type Pair = (usize, usize);
|
type Pair = (usize, usize);
|
||||||
|
@ -85,20 +85,20 @@ impl Map {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self) -> usize {
|
fn part1(&self) -> u32 {
|
||||||
if self.width < self.height {
|
if self.width < self.height {
|
||||||
if let Some(n) = self.get_horiz_score() {
|
if let Some(n) = self.get_horiz_score() {
|
||||||
return n;
|
return n as u32;
|
||||||
}
|
}
|
||||||
if let Some(n) = self.get_vert_score() {
|
if let Some(n) = self.get_vert_score() {
|
||||||
return n;
|
return n as u32;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(n) = self.get_vert_score() {
|
if let Some(n) = self.get_vert_score() {
|
||||||
return n;
|
return n as u32;
|
||||||
}
|
}
|
||||||
if let Some(n) = self.get_horiz_score() {
|
if let Some(n) = self.get_horiz_score() {
|
||||||
return n;
|
return n as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
|
@ -165,16 +165,16 @@ impl Map {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self) -> usize {
|
fn part2(&self) -> u32 {
|
||||||
let hscore = self.get_horiz_smudge();
|
let hscore = self.get_horiz_smudge();
|
||||||
let vscore = self.get_vert_smudge();
|
let vscore = self.get_vert_smudge();
|
||||||
if hscore.is_some() && vscore.is_some() {
|
if hscore.is_some() && vscore.is_some() {
|
||||||
panic!("got both scores!");
|
panic!("got both scores!");
|
||||||
}
|
}
|
||||||
if hscore.is_some() {
|
if hscore.is_some() {
|
||||||
hscore.unwrap()
|
hscore.unwrap() as u32
|
||||||
} else if vscore.is_some() {
|
} else if vscore.is_some() {
|
||||||
vscore.unwrap()
|
vscore.unwrap() as u32
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,6 @@ fn gen_map(cur: &Board) -> Map {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't forget to add an extra empty newline
|
// don't forget to add an extra empty newline
|
||||||
#[aoc_generator(day13)]
|
|
||||||
fn parse(input: &str) -> Vec<Map> {
|
fn parse(input: &str) -> Vec<Map> {
|
||||||
let mut result: Vec<Map> = vec![];
|
let mut result: Vec<Map> = vec![];
|
||||||
let mut cur: Board = vec![];
|
let mut cur: Board = vec![];
|
||||||
|
@ -224,91 +223,36 @@ fn parse(input: &str) -> Vec<Map> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day13, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[Map]) -> usize {
|
Some(parse(input).iter().map(|t| t.part1()).sum())
|
||||||
input.iter().map(|t| t.part1()).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day13, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &[Map]) -> usize {
|
Some(parse(input).iter().map(|t| t.part2()).sum())
|
||||||
input.iter().map(|t| t.part2()).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"#.##..##.
|
|
||||||
..#.##.#.
|
|
||||||
##......#
|
|
||||||
##......#
|
|
||||||
..#.##.#.
|
|
||||||
..##..##.
|
|
||||||
#.#.##.#.
|
|
||||||
|
|
||||||
#...##..#
|
|
||||||
#....#..#
|
|
||||||
..##..###
|
|
||||||
#####.##.
|
|
||||||
#####.##.
|
|
||||||
..##..###
|
|
||||||
#....#..#
|
|
||||||
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_one() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 405);
|
assert_eq!(result, Some(405));
|
||||||
assert_eq!(part2(&input), 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAMPLE_DATA2: &'static str = r#"#.##..##.
|
#[test]
|
||||||
..#.##.#.
|
fn test_part_two() {
|
||||||
##......#
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
##......#
|
assert_eq!(result, Some(400));
|
||||||
..#.##.#.
|
}
|
||||||
..##..##.
|
|
||||||
#.#.##.#.
|
|
||||||
|
|
||||||
#...##..#
|
|
||||||
#....#..#
|
|
||||||
..##..###
|
|
||||||
#####.##.
|
|
||||||
#####.##.
|
|
||||||
..##..###
|
|
||||||
#....#..#
|
|
||||||
|
|
||||||
.#.##.#.#
|
|
||||||
.##..##..
|
|
||||||
.#.##.#..
|
|
||||||
#......##
|
|
||||||
#......##
|
|
||||||
.#.##.#..
|
|
||||||
.##..##.#
|
|
||||||
|
|
||||||
#..#....#
|
|
||||||
###..##..
|
|
||||||
.##.#####
|
|
||||||
.##.#####
|
|
||||||
###..##..
|
|
||||||
#..#....#
|
|
||||||
#..##...#
|
|
||||||
|
|
||||||
#.##..##.
|
|
||||||
..#.##.#.
|
|
||||||
##..#...#
|
|
||||||
##...#..#
|
|
||||||
..#.##.#.
|
|
||||||
..##..##.
|
|
||||||
#.#.##.#.
|
|
||||||
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data2() {
|
fn sample_data2() {
|
||||||
let input = parse(&SAMPLE_DATA2);
|
let result = part_one(&advent_of_code::template::read_file_part(
|
||||||
assert_eq!(part1(&input), 709);
|
"examples", DAY, 2,
|
||||||
|
));
|
||||||
|
assert_eq!(result, Some(709));
|
||||||
// assert_eq!(part2(&input), 1400);
|
// assert_eq!(part2(&input), 1400);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(14);
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[aoc_generator(day14)]
|
|
||||||
fn parse(input: &str) -> Vec<String> {
|
fn parse(input: &str) -> Vec<String> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -33,7 +33,7 @@ fn map_to_string(map: &[[char; 100]; 100]) -> String {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn score(input: &str) -> usize {
|
fn score(input: &str) -> u32 {
|
||||||
let lines = input
|
let lines = input
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter_map(|t| {
|
.filter_map(|t| {
|
||||||
|
@ -49,19 +49,18 @@ fn score(input: &str) -> usize {
|
||||||
for (index, line) in lines.iter().enumerate() {
|
for (index, line) in lines.iter().enumerate() {
|
||||||
score += line.matches("O").collect::<Vec<_>>().len() * (total_lines - index);
|
score += line.matches("O").collect::<Vec<_>>().len() * (total_lines - index);
|
||||||
}
|
}
|
||||||
score
|
score.try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day14, part1)]
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
fn part1(input: &[String]) -> usize {
|
|
||||||
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
|
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
|
||||||
for (row, line) in input.iter().enumerate() {
|
for (row, line) in parse(input).iter().enumerate() {
|
||||||
for (col, c) in line.chars().enumerate() {
|
for (col, c) in line.chars().enumerate() {
|
||||||
map[row][col] = c;
|
map[row][col] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tilt_north(&mut map);
|
tilt_north(&mut map);
|
||||||
score(&map_to_string(&map))
|
Some(score(&map_to_string(&map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tilt_north(map: &mut [[char; 100]; 100]) -> String {
|
fn tilt_north(map: &mut [[char; 100]; 100]) -> String {
|
||||||
|
@ -160,10 +159,9 @@ fn tilt_east(map: &mut [[char; 100]; 100]) -> String {
|
||||||
map_to_string(&map)
|
map_to_string(&map)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day14, part2)]
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
fn part2(input: &[String]) -> usize {
|
|
||||||
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
|
let mut map: [[char; 100]; 100] = [[' '; 100]; 100];
|
||||||
for (row, line) in input.iter().enumerate() {
|
for (row, line) in parse(input).iter().enumerate() {
|
||||||
for (col, c) in line.chars().enumerate() {
|
for (col, c) in line.chars().enumerate() {
|
||||||
map[row][col] = c;
|
map[row][col] = c;
|
||||||
}
|
}
|
||||||
|
@ -186,29 +184,22 @@ fn part2(input: &[String]) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
let score = score(&seq[999]);
|
let score = score(&seq[999]);
|
||||||
score
|
Some(score)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"O....#....
|
#[test]
|
||||||
O.OO#....#
|
fn test_part_one() {
|
||||||
.....##...
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
OO.#O....O
|
assert_eq!(result, Some(136));
|
||||||
.O.....O#.
|
}
|
||||||
O.#..O.#.#
|
|
||||||
..O..#O..O
|
|
||||||
.......O..
|
|
||||||
#....###..
|
|
||||||
#OO..#....
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 136);
|
assert_eq!(result, Some(64));
|
||||||
assert_eq!(part2(&input), 64);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,20 +1,18 @@
|
||||||
use aoc_runner_derive::aoc;
|
advent_of_code::solution!(15);
|
||||||
|
|
||||||
fn hash(input: &str) -> usize {
|
fn hash(input: &str) -> usize {
|
||||||
let mut cur: usize = 0;
|
let mut cur: usize = 0;
|
||||||
for c in input.chars() {
|
for c in input.chars() {
|
||||||
cur = ((cur + c as usize) * 17) % 256;
|
cur = ((cur + c as usize) * 17) % 256;
|
||||||
}
|
}
|
||||||
cur
|
cur.try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day15, part1)]
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
fn part1(input: &str) -> usize {
|
Some(input.trim_end().split(",").map(|t| hash(t)).sum())
|
||||||
input.trim_end().split(",").map(|t| hash(t)).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day15, part2)]
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
fn part2(input: &str) -> usize {
|
|
||||||
let mut boxes: Vec<Vec<(&str, usize)>> = vec![];
|
let mut boxes: Vec<Vec<(&str, usize)>> = vec![];
|
||||||
for _ in 0..256 {
|
for _ in 0..256 {
|
||||||
boxes.push(vec![]);
|
boxes.push(vec![]);
|
||||||
|
@ -47,18 +45,22 @@ fn part2(input: &str) -> usize {
|
||||||
sum += (box_index + 1) * (index + 1) * lens.1;
|
sum += (box_index + 1) * (index + 1) * lens.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sum
|
Some(sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7";
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(1320));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
assert_eq!(part1(&SAMPLE_DATA), 1320);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part2(&SAMPLE_DATA), 145);
|
assert_eq!(result, Some(145));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(16);
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[aoc_generator(day16)]
|
|
||||||
fn parse(input: &str) -> Vec<Vec<char>> {
|
fn parse(input: &str) -> Vec<Vec<char>> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -134,22 +134,22 @@ fn energized_squares(visited: &HashSet<(i64, i64, Dir)>) -> usize {
|
||||||
t.len()
|
t.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day16, part1)]
|
pub fn part_one(input_str: &str) -> Option<usize> {
|
||||||
fn part1(input: &[Vec<char>]) -> usize {
|
let input = parse(input_str);
|
||||||
let mut visited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
let mut visited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
||||||
visited_spaces(input, &mut visited, 0, 0, Dir::East, 0);
|
visited_spaces(&input, &mut visited, 0, 0, Dir::East, 0);
|
||||||
energized_squares(&visited)
|
Some(energized_squares(&visited))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day16, part2)]
|
pub fn part_two(input_str: &str) -> Option<usize> {
|
||||||
fn part2(input: &[Vec<char>]) -> usize {
|
let input = parse(input_str);
|
||||||
let mut max = 0;
|
let mut max = 0;
|
||||||
let xlen = input.len() - 1;
|
let xlen = input.len() - 1;
|
||||||
let ylen = input[0].len() - 1;
|
let ylen = input[0].len() - 1;
|
||||||
|
|
||||||
for t in 0..=xlen {
|
for t in 0..=xlen {
|
||||||
let mut evisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
let mut evisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
||||||
visited_spaces(input, &mut evisited, t as i64, 0, Dir::East, 0);
|
visited_spaces(&input, &mut evisited, t as i64, 0, Dir::East, 0);
|
||||||
let emax = energized_squares(&evisited);
|
let emax = energized_squares(&evisited);
|
||||||
if emax > max {
|
if emax > max {
|
||||||
max = emax;
|
max = emax;
|
||||||
|
@ -157,7 +157,7 @@ fn part2(input: &[Vec<char>]) -> usize {
|
||||||
|
|
||||||
let mut wvisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
let mut wvisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
||||||
visited_spaces(
|
visited_spaces(
|
||||||
input,
|
&input,
|
||||||
&mut wvisited,
|
&mut wvisited,
|
||||||
xlen as i64,
|
xlen as i64,
|
||||||
(ylen - t) as i64,
|
(ylen - t) as i64,
|
||||||
|
@ -172,7 +172,7 @@ fn part2(input: &[Vec<char>]) -> usize {
|
||||||
|
|
||||||
for t in 0..=ylen {
|
for t in 0..=ylen {
|
||||||
let mut svisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
let mut svisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
||||||
visited_spaces(input, &mut svisited, 0, t as i64, Dir::South, 0);
|
visited_spaces(&input, &mut svisited, 0, t as i64, Dir::South, 0);
|
||||||
let smax = energized_squares(&svisited);
|
let smax = energized_squares(&svisited);
|
||||||
if smax > max {
|
if smax > max {
|
||||||
max = smax;
|
max = smax;
|
||||||
|
@ -180,7 +180,7 @@ fn part2(input: &[Vec<char>]) -> usize {
|
||||||
|
|
||||||
let mut nvisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
let mut nvisited: HashSet<(i64, i64, Dir)> = HashSet::new();
|
||||||
visited_spaces(
|
visited_spaces(
|
||||||
input,
|
&input,
|
||||||
&mut nvisited,
|
&mut nvisited,
|
||||||
xlen as i64,
|
xlen as i64,
|
||||||
(ylen - t) as i64,
|
(ylen - t) as i64,
|
||||||
|
@ -193,28 +193,22 @@ fn part2(input: &[Vec<char>]) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
max
|
Some(max)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#".|...\....
|
#[test]
|
||||||
|.-.\.....
|
fn test_part_one() {
|
||||||
.....|-...
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
........|.
|
assert_eq!(result, Some(46));
|
||||||
..........
|
}
|
||||||
.........\
|
|
||||||
..../.\\..
|
|
||||||
.-.-/..|..
|
|
||||||
.|....-|.\
|
|
||||||
..//.|...."#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 46);
|
assert_eq!(result, Some(51));
|
||||||
assert_eq!(part2(&input), 51);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(17);
|
||||||
|
|
||||||
use pathfinding::matrix::{directions, Matrix};
|
use pathfinding::matrix::{directions, Matrix};
|
||||||
use pathfinding::prelude::astar;
|
use pathfinding::prelude::astar;
|
||||||
|
|
||||||
#[aoc_generator(day17)]
|
|
||||||
fn parse(input: &str) -> Matrix<usize> {
|
fn parse(input: &str) -> Matrix<usize> {
|
||||||
input
|
input
|
||||||
.lines()
|
.lines()
|
||||||
|
@ -98,38 +98,27 @@ fn continue_in_direction<const MIN: usize>(map: &Matrix<usize>, p: &Path) -> Vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day17, part1)]
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
fn part1(input: &Matrix<usize>) -> usize {
|
Some(find_path::<1, 3>(&parse(input)))
|
||||||
find_path::<1, 3>(&input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day17, part2)]
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
fn part2(input: &Matrix<usize>) -> usize {
|
Some(find_path::<4, 10>(&parse(input)))
|
||||||
find_path::<4, 10>(&input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"2413432311323
|
#[test]
|
||||||
3215453535623
|
fn test_part_one() {
|
||||||
3255245654254
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
3446585845452
|
assert_eq!(result, Some(102));
|
||||||
4546657867536
|
}
|
||||||
1438598798454
|
|
||||||
4457876987766
|
|
||||||
3637877979653
|
|
||||||
4654967986887
|
|
||||||
4564679986453
|
|
||||||
1224686865563
|
|
||||||
2546548887735
|
|
||||||
4322674655533
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 102);
|
assert_eq!(result, Some(94));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
|
advent_of_code::solution!(18);
|
||||||
|
|
||||||
use aoc_parse::{parser, prelude::*};
|
use aoc_parse::{parser, prelude::*};
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
|
||||||
use std::ops::{Add, Div};
|
use std::ops::{Add, Div};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -16,7 +17,6 @@ struct Move {
|
||||||
color: String,
|
color: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day18)]
|
|
||||||
fn parse(input: &str) -> Vec<Move> {
|
fn parse(input: &str) -> Vec<Move> {
|
||||||
let p = parser!(lines({ "U" => Dir::N, "R" => Dir::E, "D" => Dir::S, "L" => Dir::W } " " usize " (#" string(digit_hex+) ")"));
|
let p = parser!(lines({ "U" => Dir::N, "R" => Dir::E, "D" => Dir::S, "L" => Dir::W } " " usize " (#" string(digit_hex+) ")"));
|
||||||
p.parse(input)
|
p.parse(input)
|
||||||
|
@ -66,14 +66,12 @@ fn shoelace(coords: &[(isize, isize)]) -> isize {
|
||||||
area.abs().add(perimeter).div(2).add(1)
|
area.abs().add(perimeter).div(2).add(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day18, part1)]
|
pub fn part_one(input: &str) -> Option<isize> {
|
||||||
fn part1(input: &[Move]) -> isize {
|
Some(shoelace(&get_perimeter(&parse(input))))
|
||||||
shoelace(&get_perimeter(input))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day18, part2)]
|
pub fn part_two(input: &str) -> Option<isize> {
|
||||||
fn part2(input: &[Move]) -> isize {
|
let moves = parse(input)
|
||||||
let moves = input
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| {
|
.map(|m| {
|
||||||
let mut s = m.color.clone();
|
let mut s = m.color.clone();
|
||||||
|
@ -92,32 +90,22 @@ fn part2(input: &[Move]) -> isize {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
shoelace(&get_perimeter(&moves))
|
Some(shoelace(&get_perimeter(&moves)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"R 6 (#70c710)
|
#[test]
|
||||||
D 5 (#0dc571)
|
fn test_part_one() {
|
||||||
L 2 (#5713f0)
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
D 2 (#d2c081)
|
assert_eq!(result, Some(62));
|
||||||
R 2 (#59c680)
|
}
|
||||||
D 2 (#411b91)
|
|
||||||
L 5 (#8ceee2)
|
|
||||||
U 2 (#caa173)
|
|
||||||
L 1 (#1b58a2)
|
|
||||||
U 2 (#caa171)
|
|
||||||
R 2 (#7807d2)
|
|
||||||
U 3 (#a77fa3)
|
|
||||||
L 2 (#015232)
|
|
||||||
U 2 (#7a21e3)"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 62);
|
assert_eq!(result, Some(952408144115));
|
||||||
assert_eq!(part2(&input), 952408144115);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
|
advent_of_code::solution!(19);
|
||||||
|
|
||||||
use aoc_parse::{parser, prelude::*};
|
use aoc_parse::{parser, prelude::*};
|
||||||
use aoc_runner_derive::aoc;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
struct Part {
|
struct Part {
|
||||||
|
@ -138,12 +139,12 @@ fn filter_part(rules: &Rules, part: &Part) -> u64 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day19, part1)]
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
fn part1(input: &str) -> u64 {
|
|
||||||
let (rules, parts) = parse(input);
|
let (rules, parts) = parse(input);
|
||||||
parts.iter().map(|p| filter_part(&rules, &p)).sum()
|
Some(parts.iter().map(|p| filter_part(&rules, &p)).sum())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn combinations(weights: [(u64, u64); 4]) -> u64 {
|
fn combinations(weights: [(u64, u64); 4]) -> u64 {
|
||||||
(0..4)
|
(0..4)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -215,37 +216,24 @@ fn traverse_rules(rules: &Rules, cur: &str, mut weights: [(u64, u64); 4]) -> u64
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day19, part2)]
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
fn part2(input: &str) -> u64 {
|
|
||||||
let (rules, _parts) = parse(input);
|
let (rules, _parts) = parse(input);
|
||||||
traverse_rules(&rules, "in", [(1, 4001); 4])
|
Some(traverse_rules(&rules, "in", [(1, 4001); 4]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"px{a<2006:qkq,m>2090:A,rfg}
|
#[test]
|
||||||
pv{a>1716:R,A}
|
fn test_part_one() {
|
||||||
lnx{m>1548:A,A}
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
rfg{s<537:gd,x>2440:R,A}
|
assert_eq!(result, Some(19114));
|
||||||
qs{s>3448:A,lnx}
|
}
|
||||||
qkq{x<1416:A,crn}
|
|
||||||
crn{x>2662:A,R}
|
|
||||||
in{s<1351:px,qqz}
|
|
||||||
qqz{s>2770:qs,m<1801:hdj,R}
|
|
||||||
gd{a>3333:R,R}
|
|
||||||
hdj{m>838:A,pv}
|
|
||||||
|
|
||||||
{x=787,m=2655,a=1222,s=2876}
|
|
||||||
{x=1679,m=44,a=2067,s=496}
|
|
||||||
{x=2036,m=264,a=79,s=2244}
|
|
||||||
{x=2461,m=1339,a=466,s=291}
|
|
||||||
{x=2127,m=1623,a=2188,s=1013}"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
assert_eq!(part1(&SAMPLE_DATA), 19114);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part2(&SAMPLE_DATA), 167409079868000);
|
assert_eq!(result, Some(167409079868000));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::aoc;
|
advent_of_code::solution!(20);
|
||||||
|
|
||||||
use num::integer::lcm;
|
use num::integer::lcm;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
@ -139,8 +140,7 @@ fn parse<'a>(input: &str) -> HashMap<String, Machine> {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day20, part1)]
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
fn part1(input: &str) -> u64 {
|
|
||||||
let mut parts = parse(input);
|
let mut parts = parse(input);
|
||||||
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
||||||
let mut high = 0;
|
let mut high = 0;
|
||||||
|
@ -159,11 +159,10 @@ fn part1(input: &str) -> u64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
high * low
|
Some(high * low)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day20, part2)]
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
fn part2(input: &str) -> u64 {
|
|
||||||
let mut parts = parse(input);
|
let mut parts = parse(input);
|
||||||
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
||||||
let mut last_cons = vec![
|
let mut last_cons = vec![
|
||||||
|
@ -183,7 +182,7 @@ fn part2(input: &str) -> u64 {
|
||||||
con_indexes.push(i + 1);
|
con_indexes.push(i + 1);
|
||||||
c = lcm(i + 1, c);
|
c = lcm(i + 1, c);
|
||||||
if last_cons.is_empty() {
|
if last_cons.is_empty() {
|
||||||
return c;
|
return Some(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,34 +191,30 @@ fn part2(input: &str) -> u64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"broadcaster -> a, b, c
|
#[test]
|
||||||
%a -> b
|
fn test_part_one() {
|
||||||
%b -> c
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
%c -> inv
|
assert_eq!(result, Some(32000000));
|
||||||
&inv -> a
|
}
|
||||||
"#;
|
|
||||||
|
|
||||||
const SAMPLE_DATA2: &'static str = r#"broadcaster -> a
|
|
||||||
%a -> inv, con
|
|
||||||
&inv -> b
|
|
||||||
%b -> con
|
|
||||||
&con -> output"#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
assert_eq!(part1(&SAMPLE_DATA), 32000000);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
// assert_eq!(part2(&SAMPLE_DATA), 71503);
|
assert_eq!(result, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data2() {
|
fn sample_data2() {
|
||||||
assert_eq!(part1(&SAMPLE_DATA2), 11687500);
|
let result = part_one(&advent_of_code::template::read_file_part(
|
||||||
|
"examples", DAY, 2,
|
||||||
|
));
|
||||||
|
assert_eq!(result, Some(11687500));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
advent_of_code::solution!(21);
|
||||||
|
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
@ -6,7 +7,6 @@ struct Point(usize, usize);
|
||||||
|
|
||||||
type Map = [[char; 131]; 131];
|
type Map = [[char; 131]; 131];
|
||||||
|
|
||||||
#[aoc_generator(day21)]
|
|
||||||
fn parse(input: &str) -> (Point, Map) {
|
fn parse(input: &str) -> (Point, Map) {
|
||||||
let mut m = [['#'; 131]; 131];
|
let mut m = [['#'; 131]; 131];
|
||||||
let mut start: Point = Point(0, 0);
|
let mut start: Point = Point(0, 0);
|
||||||
|
@ -73,17 +73,18 @@ fn infinite_step(start: &Point, map: &Map) -> HashMap<Point, usize> {
|
||||||
visited
|
visited
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day21, part1)]
|
pub fn part_one(input_str: &str) -> Option<usize> {
|
||||||
fn part1(input: &(Point, Map)) -> usize {
|
let input = parse(input_str);
|
||||||
let visited = infinite_step(&input.0, &input.1);
|
let visited = infinite_step(&input.0, &input.1);
|
||||||
visited
|
let r = visited
|
||||||
.values()
|
.values()
|
||||||
.filter(|t| **t <= 64 && **t % 2 == 0)
|
.filter(|t| **t <= 64 && **t % 2 == 0)
|
||||||
.count()
|
.count();
|
||||||
|
Some(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day21, part2)]
|
pub fn part_two(input_str: &str) -> Option<usize> {
|
||||||
fn part2(input: &(Point, Map)) -> usize {
|
let input = parse(input_str);
|
||||||
let visited = infinite_step(&input.0, &input.1);
|
let visited = infinite_step(&input.0, &input.1);
|
||||||
|
|
||||||
let even_corners = visited
|
let even_corners = visited
|
||||||
|
@ -101,28 +102,24 @@ fn part2(input: &(Point, Map)) -> usize {
|
||||||
let n = ((26501365 - (input.1.len() / 2)) / input.1.len()) as usize;
|
let n = ((26501365 - (input.1.len() / 2)) / input.1.len()) as usize;
|
||||||
assert_eq!(n, 202300);
|
assert_eq!(n, 202300);
|
||||||
|
|
||||||
((n + 1) * (n + 1)) * odd_full + (n * n) * even_full - (n + 1) * odd_corners + n * even_corners
|
let r = ((n + 1) * (n + 1)) * odd_full + (n * n) * even_full - (n + 1) * odd_corners
|
||||||
|
+ n * even_corners;
|
||||||
|
Some(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SAMPLE_DATA: &'static str = r#"...........
|
#[test]
|
||||||
.....###.#.
|
fn test_part_one() {
|
||||||
.###.##..#.
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
..#.#...#..
|
assert_eq!(result, Some(42));
|
||||||
....#.#....
|
}
|
||||||
.##..S####.
|
|
||||||
.##..#...#.
|
|
||||||
.......##..
|
|
||||||
.##.#.####.
|
|
||||||
.##..##.##.
|
|
||||||
..........."#;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_data() {
|
fn test_part_two() {
|
||||||
let input = parse(&SAMPLE_DATA);
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
assert_eq!(part1(&input), 42);
|
assert_eq!(result, Some(3314964269439));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
advent_of_code::solution!(22);
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
advent_of_code::solution!(23);
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
advent_of_code::solution!(24);
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
advent_of_code::solution!(25);
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,3 @@
|
||||||
use aoc_runner_derive::aoc_lib;
|
pub mod template;
|
||||||
|
|
||||||
mod day01;
|
// Use this file to add helper functions and additional modules.
|
||||||
mod day02;
|
|
||||||
mod day03;
|
|
||||||
mod day04;
|
|
||||||
mod day05;
|
|
||||||
mod day06;
|
|
||||||
mod day07;
|
|
||||||
mod day08;
|
|
||||||
mod day09;
|
|
||||||
mod day10;
|
|
||||||
mod day11;
|
|
||||||
mod day12;
|
|
||||||
mod day13;
|
|
||||||
mod day14;
|
|
||||||
mod day15;
|
|
||||||
mod day16;
|
|
||||||
mod day17;
|
|
||||||
mod day18;
|
|
||||||
mod day19;
|
|
||||||
mod day20;
|
|
||||||
mod day21;
|
|
||||||
|
|
||||||
aoc_lib! { year = 2023 }
|
|
||||||
|
|
141
2023/src/main.rs
141
2023/src/main.rs
|
@ -1,7 +1,138 @@
|
||||||
extern crate aoc2023;
|
use advent_of_code::template::commands::{all, download, read, scaffold, solve, time};
|
||||||
extern crate aoc_runner;
|
use args::{parse, AppArguments};
|
||||||
extern crate aoc_runner_derive;
|
|
||||||
|
|
||||||
use aoc_runner_derive::aoc_main;
|
#[cfg(feature = "today")]
|
||||||
|
use advent_of_code::template::Day;
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
use std::process;
|
||||||
|
|
||||||
aoc_main! { lib = aoc2023; }
|
mod args {
|
||||||
|
use advent_of_code::template::Day;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
pub enum AppArguments {
|
||||||
|
Download {
|
||||||
|
day: Day,
|
||||||
|
},
|
||||||
|
Read {
|
||||||
|
day: Day,
|
||||||
|
},
|
||||||
|
Scaffold {
|
||||||
|
day: Day,
|
||||||
|
download: bool,
|
||||||
|
},
|
||||||
|
Solve {
|
||||||
|
day: Day,
|
||||||
|
release: bool,
|
||||||
|
dhat: bool,
|
||||||
|
submit: Option<u8>,
|
||||||
|
},
|
||||||
|
All {
|
||||||
|
release: bool,
|
||||||
|
},
|
||||||
|
Time {
|
||||||
|
all: bool,
|
||||||
|
day: Option<Day>,
|
||||||
|
store: bool,
|
||||||
|
},
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
Today,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse() -> Result<AppArguments, Box<dyn std::error::Error>> {
|
||||||
|
let mut args = pico_args::Arguments::from_env();
|
||||||
|
|
||||||
|
let app_args = match args.subcommand()?.as_deref() {
|
||||||
|
Some("all") => AppArguments::All {
|
||||||
|
release: args.contains("--release"),
|
||||||
|
},
|
||||||
|
Some("time") => {
|
||||||
|
let all = args.contains("--all");
|
||||||
|
let store = args.contains("--store");
|
||||||
|
|
||||||
|
AppArguments::Time {
|
||||||
|
all,
|
||||||
|
day: args.opt_free_from_str()?,
|
||||||
|
store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some("download") => AppArguments::Download {
|
||||||
|
day: args.free_from_str()?,
|
||||||
|
},
|
||||||
|
Some("read") => AppArguments::Read {
|
||||||
|
day: args.free_from_str()?,
|
||||||
|
},
|
||||||
|
Some("scaffold") => AppArguments::Scaffold {
|
||||||
|
day: args.free_from_str()?,
|
||||||
|
download: args.contains("--download"),
|
||||||
|
},
|
||||||
|
Some("solve") => AppArguments::Solve {
|
||||||
|
day: args.free_from_str()?,
|
||||||
|
release: args.contains("--release"),
|
||||||
|
submit: args.opt_value_from_str("--submit")?,
|
||||||
|
dhat: args.contains("--dhat"),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
Some("today") => AppArguments::Today,
|
||||||
|
Some(x) => {
|
||||||
|
eprintln!("Unknown command: {x}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("No command specified.");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let remaining = args.finish();
|
||||||
|
if !remaining.is_empty() {
|
||||||
|
eprintln!("Warning: unknown argument(s): {remaining:?}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(app_args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match parse() {
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error: {err}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
Ok(args) => match args {
|
||||||
|
AppArguments::All { release } => all::handle(release),
|
||||||
|
AppArguments::Time { day, all, store } => time::handle(day, all, store),
|
||||||
|
AppArguments::Download { day } => download::handle(day),
|
||||||
|
AppArguments::Read { day } => read::handle(day),
|
||||||
|
AppArguments::Scaffold { day, download } => {
|
||||||
|
scaffold::handle(day);
|
||||||
|
if download {
|
||||||
|
download::handle(day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppArguments::Solve {
|
||||||
|
day,
|
||||||
|
release,
|
||||||
|
dhat,
|
||||||
|
submit,
|
||||||
|
} => solve::handle(day, release, dhat, submit),
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
AppArguments::Today => {
|
||||||
|
match Day::today() {
|
||||||
|
Some(day) => {
|
||||||
|
scaffold::handle(day);
|
||||||
|
download::handle(day);
|
||||||
|
read::handle(day)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!(
|
||||||
|
"`today` command can only be run between the 1st and \
|
||||||
|
the 25th of december. Please use `scaffold` with a specific day."
|
||||||
|
);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
advent_of_code::solution!(%DAY_NUMBER%);
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/// Wrapper module around the "aoc-cli" command-line.
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
process::{Command, Output, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::template::Day;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AocCommandError {
|
||||||
|
CommandNotFound,
|
||||||
|
CommandNotCallable,
|
||||||
|
BadExitStatus(Output),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AocCommandError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
AocCommandError::CommandNotFound => write!(f, "aoc-cli is not present in environment."),
|
||||||
|
AocCommandError::CommandNotCallable => write!(f, "aoc-cli could not be called."),
|
||||||
|
AocCommandError::BadExitStatus(_) => {
|
||||||
|
write!(f, "aoc-cli exited with a non-zero status.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check() -> Result<(), AocCommandError> {
|
||||||
|
Command::new("aoc")
|
||||||
|
.arg("-V")
|
||||||
|
.output()
|
||||||
|
.map_err(|_| AocCommandError::CommandNotFound)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(day: Day) -> Result<Output, AocCommandError> {
|
||||||
|
let puzzle_path = get_puzzle_path(day);
|
||||||
|
|
||||||
|
let args = build_args(
|
||||||
|
"read",
|
||||||
|
&[
|
||||||
|
"--description-only".into(),
|
||||||
|
"--puzzle-file".into(),
|
||||||
|
puzzle_path,
|
||||||
|
],
|
||||||
|
day,
|
||||||
|
);
|
||||||
|
|
||||||
|
call_aoc_cli(&args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn download(day: Day) -> Result<Output, AocCommandError> {
|
||||||
|
let input_path = get_input_path(day);
|
||||||
|
let puzzle_path = get_puzzle_path(day);
|
||||||
|
|
||||||
|
let args = build_args(
|
||||||
|
"download",
|
||||||
|
&[
|
||||||
|
"--overwrite".into(),
|
||||||
|
"--input-file".into(),
|
||||||
|
input_path.to_string(),
|
||||||
|
"--puzzle-file".into(),
|
||||||
|
puzzle_path.to_string(),
|
||||||
|
],
|
||||||
|
day,
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = call_aoc_cli(&args)?;
|
||||||
|
println!("---");
|
||||||
|
println!("🎄 Successfully wrote input to \"{}\".", &input_path);
|
||||||
|
println!("🎄 Successfully wrote puzzle to \"{}\".", &puzzle_path);
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submit(day: Day, part: u8, result: &str) -> Result<Output, AocCommandError> {
|
||||||
|
// workaround: the argument order is inverted for submit.
|
||||||
|
let mut args = build_args("submit", &[], day);
|
||||||
|
args.push(part.to_string());
|
||||||
|
args.push(result.to_string());
|
||||||
|
call_aoc_cli(&args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_input_path(day: Day) -> String {
|
||||||
|
format!("data/inputs/{day}.txt")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_puzzle_path(day: Day) -> String {
|
||||||
|
format!("data/puzzles/{day}.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_year() -> Option<u16> {
|
||||||
|
match std::env::var("AOC_YEAR") {
|
||||||
|
Ok(x) => x.parse().ok().or(None),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_args(command: &str, args: &[String], day: Day) -> Vec<String> {
|
||||||
|
let mut cmd_args = args.to_vec();
|
||||||
|
|
||||||
|
if let Some(year) = get_year() {
|
||||||
|
cmd_args.push("--year".into());
|
||||||
|
cmd_args.push(year.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_args.append(&mut vec!["--day".into(), day.to_string(), command.into()]);
|
||||||
|
|
||||||
|
cmd_args
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_aoc_cli(args: &[String]) -> Result<Output, AocCommandError> {
|
||||||
|
// println!("Calling >aoc with: {}", args.join(" "));
|
||||||
|
let output = Command::new("aoc")
|
||||||
|
.args(args)
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.output()
|
||||||
|
.map_err(|_| AocCommandError::CommandNotCallable)?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(output)
|
||||||
|
} else {
|
||||||
|
Err(AocCommandError::BadExitStatus(output))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
use crate::template::{all_days, run_multi::run_multi};
|
||||||
|
|
||||||
|
pub fn handle(is_release: bool) {
|
||||||
|
run_multi(&all_days().collect(), is_release, false);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::template::{aoc_cli, Day};
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
pub fn handle(day: Day) {
|
||||||
|
if aoc_cli::check().is_err() {
|
||||||
|
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = aoc_cli::download(day) {
|
||||||
|
eprintln!("failed to call aoc-cli: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod all;
|
||||||
|
pub mod download;
|
||||||
|
pub mod read;
|
||||||
|
pub mod scaffold;
|
||||||
|
pub mod solve;
|
||||||
|
pub mod time;
|
|
@ -0,0 +1,15 @@
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use crate::template::{aoc_cli, Day};
|
||||||
|
|
||||||
|
pub fn handle(day: Day) {
|
||||||
|
if aoc_cli::check().is_err() {
|
||||||
|
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = aoc_cli::read(day) {
|
||||||
|
eprintln!("failed to call aoc-cli: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
use std::{
|
||||||
|
fs::{File, OpenOptions},
|
||||||
|
io::Write,
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::template::Day;
|
||||||
|
|
||||||
|
const MODULE_TEMPLATE: &str =
|
||||||
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/template.txt"));
|
||||||
|
|
||||||
|
fn safe_create_file(path: &str) -> Result<File, std::io::Error> {
|
||||||
|
OpenOptions::new().write(true).create_new(true).open(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_file(path: &str) -> Result<File, std::io::Error> {
|
||||||
|
OpenOptions::new().write(true).create(true).open(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(day: Day) {
|
||||||
|
let input_path = format!("data/inputs/{day}.txt");
|
||||||
|
let example_path = format!("data/examples/{day}.txt");
|
||||||
|
let module_path = format!("src/bin/{day}.rs");
|
||||||
|
|
||||||
|
let mut file = match safe_create_file(&module_path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to create module file: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match file.write_all(
|
||||||
|
MODULE_TEMPLATE
|
||||||
|
.replace("%DAY_NUMBER%", &day.into_inner().to_string())
|
||||||
|
.as_bytes(),
|
||||||
|
) {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("Created module file \"{}\"", &module_path);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to write module contents: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match create_file(&input_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Created empty input file \"{}\"", &input_path);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to create input file: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match create_file(&example_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Created empty example file \"{}\"", &example_path);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to create example file: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("---");
|
||||||
|
println!("🎄 Type `cargo solve {day}` to run your solution.");
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
use crate::template::Day;
|
||||||
|
|
||||||
|
pub fn handle(day: Day, release: bool, dhat: bool, submit_part: Option<u8>) {
|
||||||
|
let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day.to_string()];
|
||||||
|
|
||||||
|
if dhat {
|
||||||
|
cmd_args.extend([
|
||||||
|
"--profile".to_string(),
|
||||||
|
"dhat".to_string(),
|
||||||
|
"--features".to_string(),
|
||||||
|
"dhat-heap".to_string(),
|
||||||
|
]);
|
||||||
|
} else if release {
|
||||||
|
cmd_args.push("--release".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_args.push("--".to_string());
|
||||||
|
|
||||||
|
if let Some(submit_part) = submit_part {
|
||||||
|
cmd_args.push("--submit".to_string());
|
||||||
|
cmd_args.push(submit_part.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cmd = Command::new("cargo")
|
||||||
|
.args(&cmd_args)
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cmd.wait().unwrap();
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::template::run_multi::run_multi;
|
||||||
|
use crate::template::timings::Timings;
|
||||||
|
use crate::template::{all_days, readme_benchmarks, Day};
|
||||||
|
|
||||||
|
pub fn handle(day: Option<Day>, run_all: bool, store: bool) {
|
||||||
|
let stored_timings = Timings::read_from_file();
|
||||||
|
|
||||||
|
let days_to_run = day.map_or_else(
|
||||||
|
|| {
|
||||||
|
if run_all {
|
||||||
|
all_days().collect()
|
||||||
|
} else {
|
||||||
|
// when the `--all` flag is not set, filter out days that are fully benched.
|
||||||
|
all_days()
|
||||||
|
.filter(|day| !stored_timings.is_day_complete(*day))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|day| HashSet::from([day]),
|
||||||
|
);
|
||||||
|
|
||||||
|
let timings = run_multi(&days_to_run, true, true).unwrap();
|
||||||
|
|
||||||
|
if store {
|
||||||
|
let merged_timings = stored_timings.merge(&timings);
|
||||||
|
merged_timings.store_file().unwrap();
|
||||||
|
|
||||||
|
println!();
|
||||||
|
match readme_benchmarks::update(merged_timings) {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("Stored updated benchmarks.");
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("Failed to store updated benchmarks.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
use chrono::{Datelike, FixedOffset, Utc};
|
||||||
|
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
const SERVER_UTC_OFFSET: i32 = -5;
|
||||||
|
|
||||||
|
/// A valid day number of advent (i.e. an integer in range 1 to 25).
|
||||||
|
///
|
||||||
|
/// # Display
|
||||||
|
/// This value displays as a two digit number.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use advent_of_code::Day;
|
||||||
|
/// let day = Day::new(8).unwrap();
|
||||||
|
/// assert_eq!(day.to_string(), "08")
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Day(u8);
|
||||||
|
|
||||||
|
impl Day {
|
||||||
|
/// Creates a [`Day`] from the provided value if it's in the valid range,
|
||||||
|
/// returns [`None`] otherwise.
|
||||||
|
pub fn new(day: u8) -> Option<Self> {
|
||||||
|
if day == 0 || day > 25 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self(day))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not part of the public API
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const fn __new_unchecked(day: u8) -> Self {
|
||||||
|
Self(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the [`Day`] into an [`u8`].
|
||||||
|
pub fn into_inner(self) -> u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "today")]
|
||||||
|
impl Day {
|
||||||
|
/// Returns the current day if it's between the 1st and the 25th of december, `None` otherwise.
|
||||||
|
pub fn today() -> Option<Self> {
|
||||||
|
let offset = FixedOffset::east_opt(SERVER_UTC_OFFSET * 3600)?;
|
||||||
|
let today = Utc::now().with_timezone(&offset);
|
||||||
|
if today.month() == 12 && today.day() <= 25 {
|
||||||
|
Self::new(u8::try_from(today.day()).ok()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Day {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:02}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<u8> for Day {
|
||||||
|
fn eq(&self, other: &u8) -> bool {
|
||||||
|
self.0.eq(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<u8> for Day {
|
||||||
|
fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
|
||||||
|
self.0.partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
impl FromStr for Day {
|
||||||
|
type Err = DayFromStrError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let day = s.parse().map_err(|_| DayFromStrError)?;
|
||||||
|
Self::new(day).ok_or(DayFromStrError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error which can be returned when parsing a [`Day`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DayFromStrError;
|
||||||
|
|
||||||
|
impl Error for DayFromStrError {}
|
||||||
|
|
||||||
|
impl Display for DayFromStrError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("expecting a day number between 1 and 25")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/// An iterator that yields every day of advent from the 1st to the 25th.
|
||||||
|
pub fn all_days() -> AllDays {
|
||||||
|
AllDays::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that yields every day of advent from the 1st to the 25th.
|
||||||
|
pub struct AllDays {
|
||||||
|
current: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllDays {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { current: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for AllDays {
|
||||||
|
type Item = Day;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.current > 25 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// NOTE: the iterator starts at 1 and we have verified that the value is not above 25.
|
||||||
|
let day = Day(self.current);
|
||||||
|
self.current += 1;
|
||||||
|
|
||||||
|
Some(day)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/// Creates a [`Day`] value in a const context.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! day {
|
||||||
|
($day:expr) => {{
|
||||||
|
const _ASSERT: () = assert!(
|
||||||
|
$day != 0 && $day <= 25,
|
||||||
|
concat!(
|
||||||
|
"invalid day number `",
|
||||||
|
$day,
|
||||||
|
"`, expecting a value between 1 and 25"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$crate::template::Day::__new_unchecked($day)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
mod tests {
|
||||||
|
use super::{all_days, Day};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_days_iterator() {
|
||||||
|
let mut iter = all_days();
|
||||||
|
|
||||||
|
assert_eq!(iter.next(), Some(Day(1)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(2)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(3)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(4)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(5)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(6)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(7)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(8)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(9)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(10)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(11)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(12)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(13)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(14)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(15)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(16)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(17)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(18)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(19)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(20)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(21)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(22)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(23)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(24)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(25)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
|
@ -0,0 +1,68 @@
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
pub mod aoc_cli;
|
||||||
|
pub mod commands;
|
||||||
|
pub mod runner;
|
||||||
|
|
||||||
|
pub use day::*;
|
||||||
|
|
||||||
|
mod day;
|
||||||
|
mod readme_benchmarks;
|
||||||
|
mod run_multi;
|
||||||
|
mod timings;
|
||||||
|
|
||||||
|
pub const ANSI_ITALIC: &str = "\x1b[3m";
|
||||||
|
pub const ANSI_BOLD: &str = "\x1b[1m";
|
||||||
|
pub const ANSI_RESET: &str = "\x1b[0m";
|
||||||
|
|
||||||
|
/// Helper function that reads a text file to a string.
|
||||||
|
#[must_use]
|
||||||
|
pub fn read_file(folder: &str, day: Day) -> String {
|
||||||
|
let cwd = env::current_dir().unwrap();
|
||||||
|
let filepath = cwd.join("data").join(folder).join(format!("{day}.txt"));
|
||||||
|
let f = fs::read_to_string(filepath);
|
||||||
|
f.expect("could not open input file")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function that reads a text file to string, appending a part suffix. E.g. like `01-2.txt`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn read_file_part(folder: &str, day: Day, part: u8) -> String {
|
||||||
|
let cwd = env::current_dir().unwrap();
|
||||||
|
let filepath = cwd
|
||||||
|
.join("data")
|
||||||
|
.join(folder)
|
||||||
|
.join(format!("{day}-{part}.txt"));
|
||||||
|
let f = fs::read_to_string(filepath);
|
||||||
|
f.expect("could not open input file")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the constant `DAY` and sets up the input and runner for each part.
|
||||||
|
///
|
||||||
|
/// The optional, second parameter (1 or 2) allows you to only run a single part of the solution.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! solution {
|
||||||
|
($day:expr) => {
|
||||||
|
$crate::solution!(@impl $day, [part_one, 1] [part_two, 2]);
|
||||||
|
};
|
||||||
|
($day:expr, 1) => {
|
||||||
|
$crate::solution!(@impl $day, [part_one, 1]);
|
||||||
|
};
|
||||||
|
($day:expr, 2) => {
|
||||||
|
$crate::solution!(@impl $day, [part_two, 2]);
|
||||||
|
};
|
||||||
|
|
||||||
|
(@impl $day:expr, $( [$func:expr, $part:expr] )*) => {
|
||||||
|
/// The current day.
|
||||||
|
const DAY: $crate::template::Day = $crate::day!($day);
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use $crate::template::runner::*;
|
||||||
|
let input = $crate::template::read_file("inputs", DAY);
|
||||||
|
$( run_part($func, &input, DAY, $part); )*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/// Module that updates the readme me with timing information.
|
||||||
|
/// The approach taken is similar to how `aoc-readme-stars` handles this.
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
|
use crate::template::timings::Timings;
|
||||||
|
use crate::template::Day;
|
||||||
|
|
||||||
|
static MARKER: &str = "<!--- benchmarking table --->";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Parser(String),
|
||||||
|
IO(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
Error::IO(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TablePosition {
|
||||||
|
pos_start: usize,
|
||||||
|
pos_end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_path_for_bin(day: Day) -> String {
|
||||||
|
format!("./src/bin/{day}.rs")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locate_table(readme: &str) -> Result<TablePosition, Error> {
|
||||||
|
let matches: Vec<_> = readme.match_indices(MARKER).collect();
|
||||||
|
|
||||||
|
if matches.len() > 2 {
|
||||||
|
return Err(Error::Parser(
|
||||||
|
"{}: too many occurences of marker in README.".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos_start = matches
|
||||||
|
.first()
|
||||||
|
.map(|m| m.0)
|
||||||
|
.ok_or_else(|| Error::Parser("Could not find table start position.".into()))?;
|
||||||
|
|
||||||
|
let pos_end = matches
|
||||||
|
.last()
|
||||||
|
.map(|m| m.0 + m.1.len())
|
||||||
|
.ok_or_else(|| Error::Parser("Could not find table end position.".into()))?;
|
||||||
|
|
||||||
|
Ok(TablePosition { pos_start, pos_end })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_table(prefix: &str, timings: Timings, total_millis: f64) -> String {
|
||||||
|
let header = format!("{prefix} Benchmarks");
|
||||||
|
|
||||||
|
let mut lines: Vec<String> = vec![
|
||||||
|
MARKER.into(),
|
||||||
|
header,
|
||||||
|
String::new(),
|
||||||
|
"| Day | Part 1 | Part 2 |".into(),
|
||||||
|
"| :---: | :---: | :---: |".into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
for timing in timings.data {
|
||||||
|
let path = get_path_for_bin(timing.day);
|
||||||
|
lines.push(format!(
|
||||||
|
"| [Day {}]({}) | `{}` | `{}` |",
|
||||||
|
timing.day.into_inner(),
|
||||||
|
path,
|
||||||
|
timing.part_1.unwrap_or_else(|| "-".into()),
|
||||||
|
timing.part_2.unwrap_or_else(|| "-".into())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(String::new());
|
||||||
|
lines.push(format!("**Total: {total_millis:.2}ms**"));
|
||||||
|
lines.push(MARKER.into());
|
||||||
|
|
||||||
|
lines.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_content(s: &mut String, timings: Timings, total_millis: f64) -> Result<(), Error> {
|
||||||
|
let positions = locate_table(s)?;
|
||||||
|
let table = construct_table("##", timings, total_millis);
|
||||||
|
s.replace_range(positions.pos_start..positions.pos_end, &table);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(timings: Timings) -> Result<(), Error> {
|
||||||
|
let path = "README.md";
|
||||||
|
let mut readme = String::from_utf8_lossy(&fs::read(path)?).to_string();
|
||||||
|
let total_millis = timings.total_millis();
|
||||||
|
update_content(&mut readme, timings, total_millis)?;
|
||||||
|
fs::write(path, &readme)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
mod tests {
|
||||||
|
use super::{update_content, MARKER};
|
||||||
|
use crate::{day, template::timings::Timing, template::timings::Timings};
|
||||||
|
|
||||||
|
fn get_mock_timings() -> Timings {
|
||||||
|
Timings {
|
||||||
|
data: vec![
|
||||||
|
Timing {
|
||||||
|
day: day!(1),
|
||||||
|
part_1: Some("10ms".into()),
|
||||||
|
part_2: Some("20ms".into()),
|
||||||
|
total_nanos: 3e+10,
|
||||||
|
},
|
||||||
|
Timing {
|
||||||
|
day: day!(2),
|
||||||
|
part_1: Some("30ms".into()),
|
||||||
|
part_2: Some("40ms".into()),
|
||||||
|
total_nanos: 7e+10,
|
||||||
|
},
|
||||||
|
Timing {
|
||||||
|
day: day!(4),
|
||||||
|
part_1: Some("40ms".into()),
|
||||||
|
part_2: Some("50ms".into()),
|
||||||
|
total_nanos: 9e+10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn errors_if_marker_not_present() {
|
||||||
|
let mut s = "# readme".to_string();
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn errors_if_too_many_markers_present() {
|
||||||
|
let mut s = format!("{} {} {}", MARKER, MARKER, MARKER);
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn updates_empty_benchmarks() {
|
||||||
|
let mut s = format!("foo\nbar\n{}{}\nbaz", MARKER, MARKER);
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
assert_eq!(s.contains("## Benchmarks"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn updates_existing_benchmarks() {
|
||||||
|
let mut s = format!("foo\nbar\n{}{}\nbaz", MARKER, MARKER);
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
assert_eq!(s.matches(MARKER).collect::<Vec<&str>>().len(), 2);
|
||||||
|
assert_eq!(s.matches("## Benchmarks").collect::<Vec<&str>>().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_benchmarks() {
|
||||||
|
let mut s = format!("foo\nbar\n{}\n{}\nbaz", MARKER, MARKER);
|
||||||
|
update_content(&mut s, get_mock_timings(), 190.0).unwrap();
|
||||||
|
let expected = [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"<!--- benchmarking table --->",
|
||||||
|
"## Benchmarks",
|
||||||
|
"",
|
||||||
|
"| Day | Part 1 | Part 2 |",
|
||||||
|
"| :---: | :---: | :---: |",
|
||||||
|
"| [Day 1](./src/bin/01.rs) | `10ms` | `20ms` |",
|
||||||
|
"| [Day 2](./src/bin/02.rs) | `30ms` | `40ms` |",
|
||||||
|
"| [Day 4](./src/bin/04.rs) | `40ms` | `50ms` |",
|
||||||
|
"",
|
||||||
|
"**Total: 190.00ms**",
|
||||||
|
"<!--- benchmarking table --->",
|
||||||
|
"baz",
|
||||||
|
]
|
||||||
|
.join("\n");
|
||||||
|
assert_eq!(s, expected);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
use std::{collections::HashSet, io};
|
||||||
|
|
||||||
|
use crate::template::{Day, ANSI_BOLD, ANSI_ITALIC, ANSI_RESET};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
all_days,
|
||||||
|
timings::{Timing, Timings},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn run_multi(days_to_run: &HashSet<Day>, is_release: bool, is_timed: bool) -> Option<Timings> {
|
||||||
|
let mut timings: Vec<Timing> = Vec::with_capacity(days_to_run.len());
|
||||||
|
|
||||||
|
let mut need_space = false;
|
||||||
|
|
||||||
|
// NOTE: use non-duplicate, sorted day values.
|
||||||
|
all_days()
|
||||||
|
.filter(|day| days_to_run.contains(day))
|
||||||
|
.for_each(|day| {
|
||||||
|
if need_space {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
need_space = true;
|
||||||
|
|
||||||
|
println!("{ANSI_BOLD}Day {day}{ANSI_RESET}");
|
||||||
|
println!("------");
|
||||||
|
|
||||||
|
let output = child_commands::run_solution(day, is_timed, is_release).unwrap();
|
||||||
|
|
||||||
|
if output.is_empty() {
|
||||||
|
println!("Not solved.");
|
||||||
|
} else {
|
||||||
|
let val = child_commands::parse_exec_time(&output, day);
|
||||||
|
timings.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_timed {
|
||||||
|
let timings = Timings { data: timings };
|
||||||
|
let total_millis = timings.total_millis();
|
||||||
|
println!(
|
||||||
|
"\n{ANSI_BOLD}Total (Run):{ANSI_RESET} {ANSI_ITALIC}{total_millis:.2}ms{ANSI_RESET}"
|
||||||
|
);
|
||||||
|
Some(timings)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
BrokenPipe,
|
||||||
|
IO(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
Error::IO(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_path_for_bin(day: Day) -> String {
|
||||||
|
format!("./src/bin/{day}.rs")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All solutions live in isolated binaries.
|
||||||
|
/// This module encapsulates interaction with these binaries, both invoking them as well as parsing the timing output.
|
||||||
|
pub mod child_commands {
|
||||||
|
use super::{get_path_for_bin, Error};
|
||||||
|
use crate::template::Day;
|
||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
path::Path,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Run the solution bin for a given day
|
||||||
|
pub fn run_solution(day: Day, is_timed: bool, is_release: bool) -> Result<Vec<String>, Error> {
|
||||||
|
// skip command invocation for days that have not been scaffolded yet.
|
||||||
|
if !Path::new(&get_path_for_bin(day)).exists() {
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let day_padded = day.to_string();
|
||||||
|
let mut args = vec!["run", "--quiet", "--bin", &day_padded];
|
||||||
|
|
||||||
|
if is_release {
|
||||||
|
args.push("--release");
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_timed {
|
||||||
|
// mirror `--time` flag to child invocations.
|
||||||
|
args.push("--");
|
||||||
|
args.push("--time");
|
||||||
|
}
|
||||||
|
|
||||||
|
// spawn child command with piped stdout/stderr.
|
||||||
|
// forward output to stdout/stderr while grabbing stdout lines.
|
||||||
|
|
||||||
|
let mut cmd = Command::new("cargo")
|
||||||
|
.args(&args)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let stdout = BufReader::new(cmd.stdout.take().ok_or(super::Error::BrokenPipe)?);
|
||||||
|
let stderr = BufReader::new(cmd.stderr.take().ok_or(super::Error::BrokenPipe)?);
|
||||||
|
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
let thread = thread::spawn(move || {
|
||||||
|
stderr.lines().for_each(|line| {
|
||||||
|
eprintln!("{}", line.unwrap());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for line in stdout.lines() {
|
||||||
|
let line = line.unwrap();
|
||||||
|
println!("{line}");
|
||||||
|
output.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.join().unwrap();
|
||||||
|
cmd.wait()?;
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_exec_time(output: &[String], day: Day) -> super::Timing {
|
||||||
|
let mut timings = super::Timing {
|
||||||
|
day,
|
||||||
|
part_1: None,
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 0_f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
output
|
||||||
|
.iter()
|
||||||
|
.filter_map(|l| {
|
||||||
|
if !l.contains(" samples)") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((timing_str, nanos)) = parse_time(l) else {
|
||||||
|
eprintln!("Could not parse timings from line: {l}");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let part = l.split(':').next()?;
|
||||||
|
Some((part, timing_str, nanos))
|
||||||
|
})
|
||||||
|
.for_each(|(part, timing_str, nanos)| {
|
||||||
|
if part.contains("Part 1") {
|
||||||
|
timings.part_1 = Some(timing_str.into());
|
||||||
|
} else if part.contains("Part 2") {
|
||||||
|
timings.part_2 = Some(timing_str.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
timings.total_nanos += nanos;
|
||||||
|
});
|
||||||
|
|
||||||
|
timings
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_to_float(s: &str, postfix: &str) -> Option<f64> {
|
||||||
|
s.split(postfix).next()?.parse().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_time(line: &str) -> Option<(&str, f64)> {
|
||||||
|
// for possible time formats, see: https://github.com/rust-lang/rust/blob/1.64.0/library/core/src/time.rs#L1176-L1200
|
||||||
|
let str_timing = line
|
||||||
|
.split(" samples)")
|
||||||
|
.next()?
|
||||||
|
.split('(')
|
||||||
|
.last()?
|
||||||
|
.split('@')
|
||||||
|
.next()?
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
let parsed_timing = match str_timing {
|
||||||
|
s if s.contains("ns") => s.split("ns").next()?.parse::<f64>().ok(),
|
||||||
|
s if s.contains("µs") => parse_to_float(s, "µs").map(|x| x * 1000_f64),
|
||||||
|
s if s.contains("ms") => parse_to_float(s, "ms").map(|x| x * 1_000_000_f64),
|
||||||
|
s => parse_to_float(s, "s").map(|x| x * 1_000_000_000_f64),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Some((str_timing, parsed_timing))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// copied from: https://github.com/rust-lang/rust/blob/1.64.0/library/std/src/macros.rs#L328-L333
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
macro_rules! assert_approx_eq {
|
||||||
|
($a:expr, $b:expr) => {{
|
||||||
|
let (a, b) = (&$a, &$b);
|
||||||
|
assert!(
|
||||||
|
(*a - *b).abs() < 1.0e-6,
|
||||||
|
"{} is not approximately equal to {}",
|
||||||
|
*a,
|
||||||
|
*b
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
mod tests {
|
||||||
|
use super::parse_exec_time;
|
||||||
|
|
||||||
|
use crate::day;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_execution_times() {
|
||||||
|
let res = parse_exec_time(
|
||||||
|
&[
|
||||||
|
"Part 1: 0 (74.13ns @ 100000 samples)".into(),
|
||||||
|
"Part 2: 10 (74.13ms @ 99999 samples)".into(),
|
||||||
|
"".into(),
|
||||||
|
],
|
||||||
|
day!(1),
|
||||||
|
);
|
||||||
|
assert_approx_eq!(res.total_nanos, 74130074.13_f64);
|
||||||
|
assert_eq!(res.part_1.unwrap(), "74.13ns");
|
||||||
|
assert_eq!(res.part_2.unwrap(), "74.13ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_with_patterns_in_input() {
|
||||||
|
let res = parse_exec_time(
|
||||||
|
&[
|
||||||
|
"Part 1: @ @ @ ( ) ms (2s @ 5 samples)".into(),
|
||||||
|
"Part 2: 10s (100ms @ 1 samples)".into(),
|
||||||
|
"".into(),
|
||||||
|
],
|
||||||
|
day!(1),
|
||||||
|
);
|
||||||
|
assert_approx_eq!(res.total_nanos, 2100000000_f64);
|
||||||
|
assert_eq!(res.part_1.unwrap(), "2s");
|
||||||
|
assert_eq!(res.part_2.unwrap(), "100ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_missing_parts() {
|
||||||
|
let res = parse_exec_time(
|
||||||
|
&[
|
||||||
|
"Part 1: ✖ ".into(),
|
||||||
|
"Part 2: ✖ ".into(),
|
||||||
|
"".into(),
|
||||||
|
],
|
||||||
|
day!(1),
|
||||||
|
);
|
||||||
|
assert_approx_eq!(res.total_nanos, 0_f64);
|
||||||
|
assert_eq!(res.part_1.is_none(), true);
|
||||||
|
assert_eq!(res.part_2.is_none(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/// Encapsulates code that interacts with solution functions.
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::hint::black_box;
|
||||||
|
use std::io::{stdout, Write};
|
||||||
|
use std::process::Output;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use std::{cmp, env, process};
|
||||||
|
|
||||||
|
use crate::template::ANSI_BOLD;
|
||||||
|
use crate::template::{aoc_cli, Day, ANSI_ITALIC, ANSI_RESET};
|
||||||
|
|
||||||
|
pub fn run_part<I: Clone, T: Display>(func: impl Fn(I) -> Option<T>, input: I, day: Day, part: u8) {
|
||||||
|
let part_str = format!("Part {part}");
|
||||||
|
|
||||||
|
let (result, duration, samples) =
|
||||||
|
run_timed(func, input, |result| print_result(result, &part_str, ""));
|
||||||
|
|
||||||
|
print_result(&result, &part_str, &format_duration(&duration, samples));
|
||||||
|
|
||||||
|
if let Some(result) = result {
|
||||||
|
submit_result(result, day, part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a solution part. The behavior differs depending on whether we are running a release or debug build:
|
||||||
|
/// 1. in debug, the function is executed once.
|
||||||
|
/// 2. in release, the function is benched (approx. 1 second of execution time or 10 samples, whatever take longer.)
|
||||||
|
fn run_timed<I: Clone, T>(
|
||||||
|
func: impl Fn(I) -> T,
|
||||||
|
input: I,
|
||||||
|
hook: impl Fn(&T),
|
||||||
|
) -> (T, Duration, u128) {
|
||||||
|
let timer = Instant::now();
|
||||||
|
let result = {
|
||||||
|
let input = input.clone();
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
|
func(input)
|
||||||
|
};
|
||||||
|
let base_time = timer.elapsed();
|
||||||
|
|
||||||
|
hook(&result);
|
||||||
|
|
||||||
|
let run = if std::env::args().any(|x| x == "--time") {
|
||||||
|
bench(func, input, &base_time)
|
||||||
|
} else {
|
||||||
|
(base_time, 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
(result, run.0, run.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench<I: Clone, T>(func: impl Fn(I) -> T, input: I, base_time: &Duration) -> (Duration, u128) {
|
||||||
|
let mut stdout = stdout();
|
||||||
|
|
||||||
|
print!(" > {ANSI_ITALIC}benching{ANSI_RESET}");
|
||||||
|
let _ = stdout.flush();
|
||||||
|
|
||||||
|
let bench_iterations = cmp::min(
|
||||||
|
10000,
|
||||||
|
cmp::max(
|
||||||
|
Duration::from_secs(1).as_nanos() / cmp::max(base_time.as_nanos(), 10),
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut timers: Vec<Duration> = vec![];
|
||||||
|
|
||||||
|
for _ in 0..bench_iterations {
|
||||||
|
// need a clone here to make the borrow checker happy.
|
||||||
|
let cloned = input.clone();
|
||||||
|
let timer = Instant::now();
|
||||||
|
black_box(func(black_box(cloned)));
|
||||||
|
timers.push(timer.elapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
Duration::from_nanos(average_duration(&timers) as u64),
|
||||||
|
bench_iterations,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn average_duration(numbers: &[Duration]) -> u128 {
|
||||||
|
numbers
|
||||||
|
.iter()
|
||||||
|
.map(std::time::Duration::as_nanos)
|
||||||
|
.sum::<u128>()
|
||||||
|
/ numbers.len() as u128
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_duration(duration: &Duration, samples: u128) -> String {
|
||||||
|
if samples == 1 {
|
||||||
|
format!(" ({duration:.1?})")
|
||||||
|
} else {
|
||||||
|
format!(" ({duration:.1?} @ {samples} samples)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_result<T: Display>(result: &Option<T>, part: &str, duration_str: &str) {
|
||||||
|
let is_intermediate_result = duration_str.is_empty();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(result) => {
|
||||||
|
if result.to_string().contains('\n') {
|
||||||
|
let str = format!("{part}: ▼ {duration_str}");
|
||||||
|
if is_intermediate_result {
|
||||||
|
print!("{str}");
|
||||||
|
} else {
|
||||||
|
print!("\r");
|
||||||
|
println!("{str}");
|
||||||
|
println!("{result}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let str = format!("{part}: {ANSI_BOLD}{result}{ANSI_RESET}{duration_str}");
|
||||||
|
if is_intermediate_result {
|
||||||
|
print!("{str}");
|
||||||
|
} else {
|
||||||
|
print!("\r");
|
||||||
|
println!("{str}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if is_intermediate_result {
|
||||||
|
print!("{part}: ✖");
|
||||||
|
} else {
|
||||||
|
print!("\r");
|
||||||
|
println!("{part}: ✖ ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the arguments passed to `solve` and try to submit one part of the solution if:
|
||||||
|
/// 1. we are in `--release` mode.
|
||||||
|
/// 2. aoc-cli is installed.
|
||||||
|
fn submit_result<T: Display>(
|
||||||
|
result: T,
|
||||||
|
day: Day,
|
||||||
|
part: u8,
|
||||||
|
) -> Option<Result<Output, aoc_cli::AocCommandError>> {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
if !args.contains(&"--submit".into()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.len() < 3 {
|
||||||
|
eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let part_index = args.iter().position(|x| x == "--submit").unwrap() + 1;
|
||||||
|
|
||||||
|
let Ok(part_submit) = args[part_index].parse::<u8>() else {
|
||||||
|
eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
if part_submit != part {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if aoc_cli::check().is_err() {
|
||||||
|
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Submitting result via aoc-cli...");
|
||||||
|
Some(aoc_cli::submit(day, part, &result.to_string()))
|
||||||
|
}
|
|
@ -0,0 +1,391 @@
|
||||||
|
use std::{collections::HashMap, fs, io::Error, str::FromStr};
|
||||||
|
use tinyjson::JsonValue;
|
||||||
|
|
||||||
|
use crate::template::Day;
|
||||||
|
|
||||||
|
static TIMINGS_FILE_PATH: &str = "./data/timings.json";
|
||||||
|
|
||||||
|
/// Represents benchmark times for a single day.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Timing {
|
||||||
|
pub day: Day,
|
||||||
|
pub part_1: Option<String>,
|
||||||
|
pub part_2: Option<String>,
|
||||||
|
pub total_nanos: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents benchmark times for a set of days.
|
||||||
|
/// Can be serialized from / to JSON.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Timings {
|
||||||
|
pub data: Vec<Timing>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timings {
|
||||||
|
/// Dehydrate timings to a JSON file.
|
||||||
|
pub fn store_file(&self) -> Result<(), Error> {
|
||||||
|
let json = JsonValue::from(self.clone());
|
||||||
|
let mut file = fs::File::create(TIMINGS_FILE_PATH)?;
|
||||||
|
json.format_to(&mut file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rehydrate timings from a JSON file. If not present, returns empty timings.
|
||||||
|
pub fn read_from_file() -> Self {
|
||||||
|
let s = fs::read_to_string(TIMINGS_FILE_PATH)
|
||||||
|
.map_err(|x| x.to_string())
|
||||||
|
.and_then(Timings::try_from);
|
||||||
|
|
||||||
|
match s {
|
||||||
|
Ok(timings) => timings,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
Timings::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Merge two sets of timings, overwriting `self` with `other` if present.
|
||||||
|
pub fn merge(&self, new: &Self) -> Self {
|
||||||
|
let mut data: Vec<Timing> = vec![];
|
||||||
|
|
||||||
|
for timing in &new.data {
|
||||||
|
data.push(timing.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for timing in &self.data {
|
||||||
|
if !data.iter().any(|t| t.day == timing.day) {
|
||||||
|
data.push(timing.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.sort_unstable_by(|a, b| a.day.cmp(&b.day));
|
||||||
|
Timings { data }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sum up total duration of timings as millis.
|
||||||
|
pub fn total_millis(&self) -> f64 {
|
||||||
|
self.data.iter().map(|x| x.total_nanos).sum::<f64>() / 1_000_000_f64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_day_complete(&self, day: Day) -> bool {
|
||||||
|
self.data
|
||||||
|
.iter()
|
||||||
|
.any(|t| t.day == day && t.part_1.is_some() && t.part_2.is_some())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
impl From<Timings> for JsonValue {
|
||||||
|
fn from(value: Timings) -> Self {
|
||||||
|
let mut map: HashMap<String, JsonValue> = HashMap::new();
|
||||||
|
|
||||||
|
map.insert(
|
||||||
|
"data".into(),
|
||||||
|
JsonValue::Array(value.data.iter().map(JsonValue::from).collect()),
|
||||||
|
);
|
||||||
|
|
||||||
|
JsonValue::Object(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Timings {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
let json = JsonValue::from_str(&value).or(Err("not valid JSON file."))?;
|
||||||
|
|
||||||
|
let json_data = json
|
||||||
|
.get::<HashMap<String, JsonValue>>()
|
||||||
|
.ok_or("expected JSON document to be an object.")?
|
||||||
|
.get("data")
|
||||||
|
.ok_or("expected JSON document to have key `data`.")?
|
||||||
|
.get::<Vec<JsonValue>>()
|
||||||
|
.ok_or("expected `json.data` to be an array.")?;
|
||||||
|
|
||||||
|
Ok(Timings {
|
||||||
|
data: json_data
|
||||||
|
.iter()
|
||||||
|
.map(Timing::try_from)
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
impl From<&Timing> for JsonValue {
|
||||||
|
fn from(value: &Timing) -> Self {
|
||||||
|
let mut map: HashMap<String, JsonValue> = HashMap::new();
|
||||||
|
|
||||||
|
map.insert("day".into(), JsonValue::String(value.day.to_string()));
|
||||||
|
map.insert("total_nanos".into(), JsonValue::Number(value.total_nanos));
|
||||||
|
|
||||||
|
let part_1 = value.part_1.clone().map(JsonValue::String);
|
||||||
|
let part_2 = value.part_2.clone().map(JsonValue::String);
|
||||||
|
|
||||||
|
map.insert(
|
||||||
|
"part_1".into(),
|
||||||
|
match part_1 {
|
||||||
|
Some(x) => x,
|
||||||
|
None => JsonValue::Null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
map.insert(
|
||||||
|
"part_2".into(),
|
||||||
|
match part_2 {
|
||||||
|
Some(x) => x,
|
||||||
|
None => JsonValue::Null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
JsonValue::Object(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&JsonValue> for Timing {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from(value: &JsonValue) -> Result<Self, Self::Error> {
|
||||||
|
let json = value
|
||||||
|
.get::<HashMap<String, JsonValue>>()
|
||||||
|
.ok_or("Expected timing to be a JSON object.")?;
|
||||||
|
|
||||||
|
let day = json
|
||||||
|
.get("day")
|
||||||
|
.and_then(|v| v.get::<String>())
|
||||||
|
.and_then(|day| Day::from_str(day).ok())
|
||||||
|
.ok_or("Expected timing.day to be a Day struct.")?;
|
||||||
|
|
||||||
|
let part_1 = json
|
||||||
|
.get("part_1")
|
||||||
|
.map(|v| if v.is_null() { None } else { v.get::<String>() })
|
||||||
|
.ok_or("Expected timing.part_1 to be null or string.")?;
|
||||||
|
|
||||||
|
let part_2 = json
|
||||||
|
.get("part_2")
|
||||||
|
.map(|v| if v.is_null() { None } else { v.get::<String>() })
|
||||||
|
.ok_or("Expected timing.part_2 to be null or string.")?;
|
||||||
|
|
||||||
|
let total_nanos = json
|
||||||
|
.get("total_nanos")
|
||||||
|
.and_then(|v| v.get::<f64>().copied())
|
||||||
|
.ok_or("Expected timing.total_nanos to be a number.")?;
|
||||||
|
|
||||||
|
Ok(Timing {
|
||||||
|
day,
|
||||||
|
part_1: part_1.cloned(),
|
||||||
|
part_2: part_2.cloned(),
|
||||||
|
total_nanos,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
mod tests {
|
||||||
|
use crate::day;
|
||||||
|
|
||||||
|
use super::{Timing, Timings};
|
||||||
|
|
||||||
|
fn get_mock_timings() -> Timings {
|
||||||
|
Timings {
|
||||||
|
data: vec![
|
||||||
|
Timing {
|
||||||
|
day: day!(1),
|
||||||
|
part_1: Some("10ms".into()),
|
||||||
|
part_2: Some("20ms".into()),
|
||||||
|
total_nanos: 3e+10,
|
||||||
|
},
|
||||||
|
Timing {
|
||||||
|
day: day!(2),
|
||||||
|
part_1: Some("30ms".into()),
|
||||||
|
part_2: Some("40ms".into()),
|
||||||
|
total_nanos: 7e+10,
|
||||||
|
},
|
||||||
|
Timing {
|
||||||
|
day: day!(4),
|
||||||
|
part_1: Some("40ms".into()),
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 4e+10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod deserialization {
|
||||||
|
use crate::{day, template::timings::Timings};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_json_timings() {
|
||||||
|
let json = r#"{ "data": [{ "day": "01", "part_1": "1ms", "part_2": null, "total_nanos": 1000000000 }] }"#.to_string();
|
||||||
|
let timings = Timings::try_from(json).unwrap();
|
||||||
|
assert_eq!(timings.data.len(), 1);
|
||||||
|
let timing = timings.data.first().unwrap();
|
||||||
|
assert_eq!(timing.day, day!(1));
|
||||||
|
assert_eq!(timing.part_1, Some("1ms".to_string()));
|
||||||
|
assert_eq!(timing.part_2, None);
|
||||||
|
assert_eq!(timing.total_nanos, 1_000_000_000_f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_empty_timings() {
|
||||||
|
let json = r#"{ "data": [] }"#.to_string();
|
||||||
|
let timings = Timings::try_from(json).unwrap();
|
||||||
|
assert_eq!(timings.data.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn panics_for_invalid_json() {
|
||||||
|
let json = r#"{}"#.to_string();
|
||||||
|
Timings::try_from(json).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn panics_for_malformed_timings() {
|
||||||
|
let json = r#"{ "data": [{ "day": "01" }, { "day": "26" }, { "day": "02", "part_2": null, "total_nanos": 0 }] }"#.to_string();
|
||||||
|
Timings::try_from(json).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod serialization {
|
||||||
|
use super::get_mock_timings;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use tinyjson::JsonValue;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serializes_timings() {
|
||||||
|
let timings = get_mock_timings();
|
||||||
|
let value = JsonValue::try_from(timings).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
value
|
||||||
|
.get::<HashMap<String, JsonValue>>()
|
||||||
|
.unwrap()
|
||||||
|
.get("data")
|
||||||
|
.unwrap()
|
||||||
|
.get::<Vec<JsonValue>>()
|
||||||
|
.unwrap()
|
||||||
|
.len(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod is_day_complete {
|
||||||
|
use crate::{
|
||||||
|
day,
|
||||||
|
template::timings::{Timing, Timings},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_completed_days() {
|
||||||
|
let timings = Timings {
|
||||||
|
data: vec![Timing {
|
||||||
|
day: day!(1),
|
||||||
|
part_1: Some("1ms".into()),
|
||||||
|
part_2: Some("2ms".into()),
|
||||||
|
total_nanos: 3_000_000_000_f64,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(timings.is_day_complete(&day!(1)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_partial_days() {
|
||||||
|
let timings = Timings {
|
||||||
|
data: vec![Timing {
|
||||||
|
day: day!(1),
|
||||||
|
part_1: Some("1ms".into()),
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 1_000_000_000_f64,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(timings.is_day_complete(&day!(1)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_uncompleted_days() {
|
||||||
|
let timings = Timings {
|
||||||
|
data: vec![Timing {
|
||||||
|
day: day!(1),
|
||||||
|
part_1: None,
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 0.0,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(timings.is_day_complete(&day!(1)), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod merge {
|
||||||
|
use crate::{
|
||||||
|
day,
|
||||||
|
template::timings::{Timing, Timings},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::get_mock_timings;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_disjunct_timings() {
|
||||||
|
let timings = get_mock_timings();
|
||||||
|
let other = Timings {
|
||||||
|
data: vec![Timing {
|
||||||
|
day: day!(3),
|
||||||
|
part_1: None,
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 0_f64,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let merged = timings.merge(&other);
|
||||||
|
assert_eq!(merged.data.len(), 4);
|
||||||
|
assert_eq!(merged.data[0].day, day!(1));
|
||||||
|
assert_eq!(merged.data[1].day, day!(2));
|
||||||
|
assert_eq!(merged.data[2].day, day!(3));
|
||||||
|
assert_eq!(merged.data[3].day, day!(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_overlapping_timings() {
|
||||||
|
let timings = get_mock_timings();
|
||||||
|
|
||||||
|
let other = Timings {
|
||||||
|
data: vec![Timing {
|
||||||
|
day: day!(2),
|
||||||
|
part_1: None,
|
||||||
|
part_2: None,
|
||||||
|
total_nanos: 0_f64,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let merged = timings.merge(&other);
|
||||||
|
|
||||||
|
assert_eq!(merged.data.len(), 3);
|
||||||
|
assert_eq!(merged.data[0].day, day!(1));
|
||||||
|
assert_eq!(merged.data[1].day, day!(2));
|
||||||
|
assert_eq!(merged.data[1].total_nanos, 0_f64);
|
||||||
|
assert_eq!(merged.data[2].day, day!(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_empty_timings() {
|
||||||
|
let timings = Timings::default();
|
||||||
|
let other = get_mock_timings();
|
||||||
|
let merged = timings.merge(&other);
|
||||||
|
assert_eq!(merged.data.len(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_empty_other_timings() {
|
||||||
|
let timings = get_mock_timings();
|
||||||
|
let other = Timings::default();
|
||||||
|
let merged = timings.merge(&other);
|
||||||
|
assert_eq!(merged.data.len(), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue