Merge remote-tracking branch 'temp_merge/master'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -46,3 +46,5 @@ tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
.vscode/settings.json
|
||||
|
||||
input.txt
|
||||
|
||||
50
2023/.gitignore
vendored
Normal file
50
2023/.gitignore
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
#Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
.vscode/settings.json
|
||||
|
||||
input.txt
|
||||
904
2023/Cargo.lock
generated
Normal file
904
2023/Cargo.lock
generated
Normal file
@@ -0,0 +1,904 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
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 = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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 = "bytecount"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
|
||||
|
||||
[[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 = "day-1"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"derive-getters",
|
||||
"error-stack",
|
||||
"itertools",
|
||||
"log",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-10"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"nom",
|
||||
"nom_locate",
|
||||
"rstest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-11"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"nom",
|
||||
"nom_locate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-2"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"log",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-3"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-4"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"rstest",
|
||||
"rstest_reuse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-5"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-6"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-7"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"dhat",
|
||||
"itertools",
|
||||
"nom",
|
||||
"num",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-8"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"rstest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-9"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"rstest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-getters"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "error-stack"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27a72baa257b5e0e2de241967bc5ee8f855d6072351042688621081d66b2a76b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[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]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom_locate"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"memchr",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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 = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca"
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"rstest_macros",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"glob",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn 2.0.39",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_reuse"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"rand",
|
||||
"rustc_version",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sys-info"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thousands"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[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"
|
||||
26
2023/Cargo.toml
Normal file
26
2023/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [ "day-*" ]
|
||||
|
||||
[workspace.package]
|
||||
version = "2023.0.0"
|
||||
edition = "2021"
|
||||
authors = [ "Dylan Thies" ]
|
||||
repository = "https://github.com/smellyfis/AOC-2023.git"
|
||||
|
||||
[workspace.dependencies]
|
||||
derive-getters = "0.3.0"
|
||||
error-stack = "0.4.1"
|
||||
itertools = "0.12.0"
|
||||
log = "0.4.20"
|
||||
nom = "7.1.3"
|
||||
nom_locate= "4.2.0"
|
||||
rstest = "0.18.2"
|
||||
rstest_reuse = "0.6.0"
|
||||
dhat = "0.3.2"
|
||||
glam = "0.24.2"
|
||||
|
||||
|
||||
[profile.dhat]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
16
2023/day-1/Cargo.toml
Normal file
16
2023/day-1/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "day-1"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
derive-getters.workspace = true
|
||||
error-stack.workspace = true
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
nom.workspace = true
|
||||
2
2023/day-1/src/lib.rs
Normal file
2
2023/day-1/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod part1;
|
||||
pub mod part2;
|
||||
12
2023/day-1/src/main.rs
Normal file
12
2023/day-1/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_1::part1::part1;
|
||||
use day_1::part2::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let (_, part1_result) = part1(input).unwrap();
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
59
2023/day-1/src/part1.rs
Normal file
59
2023/day-1/src/part1.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
self,
|
||||
character::complete::{alphanumeric1, newline},
|
||||
multi::separated_list1,
|
||||
};
|
||||
|
||||
/// Day-1 part 1 of AC2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for day1 as a string
|
||||
///
|
||||
/// # Panics
|
||||
/// This panics whenever a number isn't present in a line of the input
|
||||
///
|
||||
/// # Errors
|
||||
/// errors when can't parse the input
|
||||
pub fn part1(input: &str) -> nom::IResult<&str, String> {
|
||||
let (_, values) = parse_input(input)?;
|
||||
println!("{values:?}");
|
||||
Ok((
|
||||
"",
|
||||
values
|
||||
.iter()
|
||||
.map(|v| v.first().expect("always at least one number") * 10 + v.last().expect("always atleast one number"))
|
||||
.sum::<u32>()
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> nom::IResult<&str, Vec<Vec<u32>>> {
|
||||
let (i, j) = separated_list1(newline, alphanumeric1)(input)?;
|
||||
let res = j
|
||||
.iter()
|
||||
.map(|v| {
|
||||
v.chars()
|
||||
.filter_map(|x| x.to_digit(10))
|
||||
.collect::<Vec<u32>>()
|
||||
})
|
||||
.collect::<Vec<Vec<u32>>>();
|
||||
Ok((i, res))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "1abc2
|
||||
pqr3stu8vwx
|
||||
a1b2c3d4e5f
|
||||
treb7uchet";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let (_, result) = part1(INPUT).unwrap();
|
||||
assert_eq!(result, "142".to_string());
|
||||
}
|
||||
}
|
||||
70
2023/day-1/src/part2.rs
Normal file
70
2023/day-1/src/part2.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
/// Day 1 Part 2 of AOC2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - puzzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// this panics if there is no numbers in a line
|
||||
pub fn part2(input: &str) -> String {
|
||||
let values = input.lines().map(parse_line).collect::<Vec<Vec<u32>>>();
|
||||
println!("{values:?}");
|
||||
values
|
||||
.iter()
|
||||
.map(|v| v.first().expect("There is always at least one number") * 10 + v.last().expect("there is always at least one number"))
|
||||
.sum::<u32>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_line(line: &str) -> Vec<u32> {
|
||||
(0..line.len())
|
||||
.filter_map(|index| {
|
||||
let reduced_line = &line[index..];
|
||||
let result = if reduced_line.starts_with("one") {
|
||||
Some(1)
|
||||
} else if reduced_line.starts_with("two") {
|
||||
Some(2)
|
||||
} else if reduced_line.starts_with("three") {
|
||||
Some(3)
|
||||
} else if reduced_line.starts_with("four") {
|
||||
Some(4)
|
||||
} else if reduced_line.starts_with("five") {
|
||||
Some(5)
|
||||
} else if reduced_line.starts_with("six") {
|
||||
Some(6)
|
||||
} else if reduced_line.starts_with("seven") {
|
||||
Some(7)
|
||||
} else if reduced_line.starts_with("eight") {
|
||||
Some(8)
|
||||
} else if reduced_line.starts_with("nine") {
|
||||
Some(9)
|
||||
} else if reduced_line.starts_with("zero") {
|
||||
Some(0)
|
||||
} else {
|
||||
reduced_line.chars().next().expect("there is alwayss a character").to_digit(10)
|
||||
};
|
||||
|
||||
result
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "two1nine
|
||||
eightwothree
|
||||
abcone2threexyz
|
||||
xtwone3four
|
||||
4nineeightseven2
|
||||
zoneight234
|
||||
7pqrstsixteen";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "281".to_string());
|
||||
}
|
||||
}
|
||||
17
2023/day-10/Cargo.toml
Normal file
17
2023/day-10/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "day-10"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
nom_locate = {workspace = true }
|
||||
itertools.workspace = true
|
||||
glam = "0.24.2"
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = {workspace = true}
|
||||
4
2023/day-10/src/lib.rs
Normal file
4
2023/day-10/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-10/src/main.rs
Normal file
12
2023/day-10/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_10::part1;
|
||||
use day_10::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
267
2023/day-10/src/part1.rs
Normal file
267
2023/day-10/src/part1.rs
Normal file
@@ -0,0 +1,267 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use std::{collections::HashMap, iter::successors};
|
||||
|
||||
use glam::IVec2;
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
combinator::eof,
|
||||
multi::{fold_many1, many1},
|
||||
sequence::terminated,
|
||||
IResult, Parser,
|
||||
};
|
||||
use nom_locate::LocatedSpan;
|
||||
|
||||
type Span<'a> = LocatedSpan<&'a str>;
|
||||
type SpanIVec2<'a> = LocatedSpan<&'a str, IVec2>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
||||
enum PipeFrom {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
impl PipeFrom {
|
||||
fn from_ivecs(a: IVec2, b: IVec2) -> Option<Self> {
|
||||
match (a - b).into() {
|
||||
(0, 1) => Some(Self::Down),
|
||||
(0, -1) => Some(Self::Up),
|
||||
(1, 0) => Some(Self::Right),
|
||||
(-1, 0) => Some(Self::Left),
|
||||
_ => None,
|
||||
//value => unimplemented!("this can't be {a:?} - {b:?} = {value:?}"),
|
||||
}
|
||||
}
|
||||
fn to_ivec(self) -> IVec2 {
|
||||
match self {
|
||||
PipeFrom::Up => (0, -1).into(),
|
||||
PipeFrom::Down => (0, 1).into(),
|
||||
PipeFrom::Left => (-1, 0).into(),
|
||||
PipeFrom::Right => (1, 0).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
||||
enum PipeType {
|
||||
// 'S'
|
||||
Start,
|
||||
// '-'
|
||||
Horizontal,
|
||||
// '|'
|
||||
Vertical,
|
||||
// 'F'
|
||||
DownRight,
|
||||
// '7'
|
||||
DownLeft,
|
||||
// 'L'
|
||||
UpRight,
|
||||
// 'J'
|
||||
UpLeft,
|
||||
// '.' -this is so we can parse but should be discarded
|
||||
None,
|
||||
}
|
||||
|
||||
impl PipeType {
|
||||
fn get_adjacents(self) -> Vec<IVec2> {
|
||||
match self {
|
||||
PipeType::Start => vec![(-1, 0).into(), (0, -1).into(), (0, 1).into(), (1, 0).into()],
|
||||
PipeType::Horizontal => vec![(1, 0).into(), (-1, 0).into()],
|
||||
PipeType::Vertical => vec![(0, 1).into(), (0, -1).into()],
|
||||
PipeType::DownRight => vec![(0, 1).into(), (1, 0).into()],
|
||||
PipeType::DownLeft => vec![(0, 1).into(), (-1, 0).into()],
|
||||
PipeType::UpRight => vec![(0, -1).into(), (1, 0).into()],
|
||||
PipeType::UpLeft => vec![(0, -1).into(), (-1, 0).into()],
|
||||
PipeType::None => unimplemented!("this should never have been called"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Pipe {
|
||||
pub pipe_type: PipeType,
|
||||
pub position: IVec2,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
fn get_adjacent(&self) -> Vec<(IVec2, PipeFrom)> {
|
||||
self.pipe_type
|
||||
.get_adjacents()
|
||||
.into_iter()
|
||||
.map(|x| x + self.position)
|
||||
.filter_map(|x| PipeFrom::from_ivecs(self.position, x).map(|y| (x, y)))
|
||||
.collect()
|
||||
}
|
||||
fn next(&self, from: PipeFrom) -> IVec2 {
|
||||
use PipeFrom::{Down, Left, Right, Up};
|
||||
use PipeType::{DownLeft, DownRight, Horizontal, UpLeft, UpRight, Vertical};
|
||||
match (from, self.pipe_type) {
|
||||
(Up, Vertical) | (Left, DownLeft) | (Right, DownRight) => Down,
|
||||
(Up, UpLeft) | (Down, DownLeft) | (Right, Horizontal) => Left,
|
||||
(Up, UpRight) | (Down, DownRight) | (Left, Horizontal) => Right,
|
||||
(Down, Vertical) | (Left, UpLeft) | (Right, UpRight) => Up,
|
||||
_ => unimplemented!("no"),
|
||||
}
|
||||
.to_ivec()
|
||||
+ self.position
|
||||
}
|
||||
}
|
||||
|
||||
/// day 10 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let input = Span::new(input);
|
||||
let (_, grid) = parse_input(input).expect("aoc always parse");
|
||||
let start_node = grid
|
||||
.values()
|
||||
.find(|x| x.pipe_type == PipeType::Start)
|
||||
.expect("has a start");
|
||||
|
||||
(successors(
|
||||
Some(
|
||||
start_node
|
||||
.get_adjacent()
|
||||
.iter()
|
||||
.filter_map(|(x, from)| grid.get(x).map(|y| (y, *from)))
|
||||
.filter(|(x, _)| {
|
||||
x.get_adjacent()
|
||||
.iter()
|
||||
.map(|(y, _)| y)
|
||||
.contains(&start_node.position)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
|front_nodes| {
|
||||
Some(
|
||||
front_nodes
|
||||
.iter()
|
||||
.filter_map(|(pipe, from)| {
|
||||
grid.get(&pipe.next(*from))
|
||||
.map(|x| (x, PipeFrom::from_ivecs(pipe.position, x.position).unwrap()))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
},
|
||||
)
|
||||
.filter(|x| !x.is_empty())
|
||||
.position(|a| a[0].0 == a[1].0)
|
||||
.unwrap()
|
||||
+ 1)
|
||||
.to_string()
|
||||
//todo!()
|
||||
}
|
||||
|
||||
fn with_xy(span: Span) -> SpanIVec2 {
|
||||
let x = i32::try_from(span.get_column()).expect("overflow") - 1;
|
||||
let y = i32::try_from(span.location_line()).expect("wrap around") - 1;
|
||||
span.map_extra(|()| IVec2::new(x, y))
|
||||
}
|
||||
|
||||
fn parse_pipe(input: Span) -> IResult<Span, Pipe> {
|
||||
alt((
|
||||
tag("S").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Start,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("-").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Horizontal,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("|").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Vertical,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("F").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::DownRight,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("7").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::DownLeft,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("L").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::UpRight,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("J").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::UpLeft,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag(".").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::None,
|
||||
position: position.extra,
|
||||
}),
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn parse_input(input: Span) -> IResult<Span, HashMap<IVec2, Pipe>> {
|
||||
fold_many1(
|
||||
terminated(many1(parse_pipe), alt((complete::line_ending, eof))),
|
||||
HashMap::new,
|
||||
|mut acc, x| {
|
||||
x.into_iter()
|
||||
.filter(|x| x.pipe_type != PipeType::None)
|
||||
.for_each(|x| {
|
||||
acc.insert(x.position, x);
|
||||
});
|
||||
acc
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
".....
|
||||
.S-7.
|
||||
.|.|.
|
||||
.L-J.
|
||||
.....",
|
||||
"4"
|
||||
)]
|
||||
#[case(
|
||||
"-L|F7
|
||||
7S-7|
|
||||
L|7||
|
||||
-L-J|
|
||||
L|-JF",
|
||||
"4"
|
||||
)]
|
||||
#[case(
|
||||
"..F7.
|
||||
.FJ|.
|
||||
SJ.L7
|
||||
|F--J
|
||||
LJ...",
|
||||
"8"
|
||||
)]
|
||||
#[case(
|
||||
"7-F7-
|
||||
.FJ|7
|
||||
SJLL7
|
||||
|F--J
|
||||
LJ.LJ",
|
||||
"8"
|
||||
)]
|
||||
|
||||
fn part1_works(#[case] input: &str, #[case] expected: &str) {
|
||||
let result = part1(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
375
2023/day-10/src/part2.rs
Normal file
375
2023/day-10/src/part2.rs
Normal file
@@ -0,0 +1,375 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use std::{collections::HashMap, fmt::Display, iter::successors};
|
||||
|
||||
use glam::IVec2;
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
combinator::eof,
|
||||
multi::{fold_many1, many1},
|
||||
sequence::terminated,
|
||||
IResult, Parser,
|
||||
};
|
||||
use nom_locate::LocatedSpan;
|
||||
|
||||
type Span<'a> = LocatedSpan<&'a str>;
|
||||
type SpanIVec2<'a> = LocatedSpan<&'a str, IVec2>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
||||
enum PipeFrom {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
impl PipeFrom {
|
||||
fn from_ivecs(a: IVec2, b: IVec2) -> Option<Self> {
|
||||
match (a - b).into() {
|
||||
(0, 1) => Some(Self::Down),
|
||||
(0, -1) => Some(Self::Up),
|
||||
(1, 0) => Some(Self::Right),
|
||||
(-1, 0) => Some(Self::Left),
|
||||
_ => None,
|
||||
//value => unimplemented!("this can't be {a:?} - {b:?} = {value:?}"),
|
||||
}
|
||||
}
|
||||
fn to_ivec(self) -> IVec2 {
|
||||
match self {
|
||||
PipeFrom::Up => (0, -1).into(),
|
||||
PipeFrom::Down => (0, 1).into(),
|
||||
PipeFrom::Left => (-1, 0).into(),
|
||||
PipeFrom::Right => (1, 0).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
||||
enum PipeType {
|
||||
// 'S'
|
||||
Start,
|
||||
// '-'
|
||||
Horizontal,
|
||||
// '|'
|
||||
Vertical,
|
||||
// 'F'
|
||||
DownRight,
|
||||
// '7'
|
||||
DownLeft,
|
||||
// 'L'
|
||||
UpRight,
|
||||
// 'J'
|
||||
UpLeft,
|
||||
// '.' -this is so we can parse but should be discarded
|
||||
None,
|
||||
Outer,
|
||||
Inner,
|
||||
}
|
||||
|
||||
impl PipeType {
|
||||
fn get_adjacents(self) -> Vec<IVec2> {
|
||||
match self {
|
||||
PipeType::Start => vec![(-1, 0).into(), (0, -1).into(), (0, 1).into(), (1, 0).into()],
|
||||
PipeType::Horizontal => vec![(1, 0).into(), (-1, 0).into()],
|
||||
PipeType::Vertical => vec![(0, 1).into(), (0, -1).into()],
|
||||
PipeType::DownRight => vec![(0, 1).into(), (1, 0).into()],
|
||||
PipeType::DownLeft => vec![(0, 1).into(), (-1, 0).into()],
|
||||
PipeType::UpRight => vec![(0, -1).into(), (1, 0).into()],
|
||||
PipeType::UpLeft => vec![(0, -1).into(), (-1, 0).into()],
|
||||
value => unimplemented!("this should never have been called for type {value:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for PipeType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Start => "S",
|
||||
Self::Horizontal => "-",
|
||||
Self::Vertical => "|",
|
||||
Self::DownRight => "F",
|
||||
Self::DownLeft => "7",
|
||||
Self::UpRight => "L",
|
||||
Self::UpLeft => "J",
|
||||
Self::None => ".",
|
||||
Self::Outer => "O",
|
||||
Self::Inner => "I",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Pipe {
|
||||
pub pipe_type: PipeType,
|
||||
pub position: IVec2,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
fn get_adjacent(&self) -> Vec<(IVec2, PipeFrom)> {
|
||||
self.pipe_type
|
||||
.get_adjacents()
|
||||
.into_iter()
|
||||
.map(|x| x + self.position)
|
||||
.filter_map(|x| PipeFrom::from_ivecs(self.position, x).map(|y| (x, y)))
|
||||
.collect()
|
||||
}
|
||||
fn next(&self, from: PipeFrom) -> IVec2 {
|
||||
use PipeFrom::{Down, Left, Right, Up};
|
||||
use PipeType::{DownLeft, DownRight, Horizontal, UpLeft, UpRight, Vertical};
|
||||
match (from, self.pipe_type) {
|
||||
(Up, Vertical) | (Left, DownLeft) | (Right, DownRight) => Down,
|
||||
(Up, UpLeft) | (Down, DownLeft) | (Right, Horizontal) => Left,
|
||||
(Up, UpRight) | (Down, DownRight) | (Left, Horizontal) => Right,
|
||||
(Down, Vertical) | (Left, UpLeft) | (Right, UpRight) => Up,
|
||||
_ => unimplemented!("no"),
|
||||
}
|
||||
.to_ivec()
|
||||
+ self.position
|
||||
}
|
||||
}
|
||||
|
||||
/// day 10 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let input = Span::new(input);
|
||||
let (_, grid) = parse_input(input).expect("aoc always parse");
|
||||
let start_node = grid
|
||||
.values()
|
||||
.find(|x| x.pipe_type == PipeType::Start)
|
||||
.expect("has a start");
|
||||
let start_node_true_type = match &start_node
|
||||
.get_adjacent()
|
||||
.iter()
|
||||
.filter_map(|(x, from)| grid.get(x).map(|y| (y, *from)))
|
||||
.filter_map(|(x, from)| {
|
||||
x.get_adjacent()
|
||||
.iter()
|
||||
.map(|(y, _)| y)
|
||||
.contains(&start_node.position)
|
||||
.then_some(from)
|
||||
})
|
||||
.collect::<Vec<_>>()[..]
|
||||
{
|
||||
[PipeFrom::Up, PipeFrom::Left] | [PipeFrom::Left, PipeFrom::Up] => PipeType::DownRight,
|
||||
[PipeFrom::Up, PipeFrom::Right] | [PipeFrom::Right, PipeFrom::Up] => PipeType::DownLeft,
|
||||
[PipeFrom::Down, PipeFrom::Left] | [PipeFrom::Left, PipeFrom::Down] => PipeType::UpRight,
|
||||
[PipeFrom::Down, PipeFrom::Right] | [PipeFrom::Right, PipeFrom::Down] => PipeType::UpLeft,
|
||||
[PipeFrom::Up, PipeFrom::Down] | [PipeFrom::Down, PipeFrom::Up] => PipeType::Vertical,
|
||||
[PipeFrom::Right, PipeFrom::Left] | [PipeFrom::Left, PipeFrom::Right] => {
|
||||
PipeType::Horizontal
|
||||
}
|
||||
_ => PipeType::Start,
|
||||
};
|
||||
|
||||
let mut pieces = HashMap::new();
|
||||
pieces.insert(start_node.position, start_node_true_type);
|
||||
|
||||
successors(
|
||||
Some(
|
||||
start_node
|
||||
.get_adjacent()
|
||||
.iter()
|
||||
.filter_map(|(x, from)| grid.get(x).map(|y| (y, *from)))
|
||||
.filter(|(x, _)| {
|
||||
x.get_adjacent()
|
||||
.iter()
|
||||
.map(|(y, _)| y)
|
||||
.contains(&start_node.position)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
|front_nodes| {
|
||||
if front_nodes[0].0 == front_nodes[1].0 {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
front_nodes
|
||||
.iter()
|
||||
.filter_map(|(pipe, from)| {
|
||||
grid.get(&pipe.next(*from))
|
||||
.map(|x| (x, PipeFrom::from_ivecs(pipe.position, x.position).unwrap()))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
},
|
||||
)
|
||||
.filter(|x| !x.is_empty())
|
||||
.for_each(|x| {
|
||||
for (pipe, _) in &x {
|
||||
pieces.insert(pipe.position, pipe.pipe_type);
|
||||
}
|
||||
});
|
||||
let corners = pieces.keys().fold(
|
||||
((i32::MAX, i32::MAX), (i32::MIN, i32::MIN)),
|
||||
|((minimum_x, min_y), (maximal_x, max_y)), pos| {
|
||||
let minimum_x = minimum_x.min(pos.x);
|
||||
let miny = min_y.min(pos.y);
|
||||
let maximal_x = maximal_x.max(pos.x);
|
||||
let maxy = max_y.max(pos.y);
|
||||
((minimum_x, miny), (maximal_x, maxy))
|
||||
},
|
||||
);
|
||||
/* Debug
|
||||
(corners.0 .1..=corners.1 .1).for_each(|y| {
|
||||
(corners.0.0..=corners.1.0).for_each(|x| {
|
||||
let p = pieces.get(&(x,y).into()).unwrap_or(&PipeType::None);
|
||||
print!("{p}");
|
||||
});
|
||||
print!("\n");
|
||||
});
|
||||
*/
|
||||
(corners.0 .1..=corners.1 .1).for_each(|y| {
|
||||
let mut status = false;
|
||||
(corners.0 .0..=corners.1 .0)
|
||||
.map(|x| IVec2::new(x, y))
|
||||
.for_each(|pos| {
|
||||
if let Some(piece) = pieces.get(&pos) {
|
||||
status = match piece {
|
||||
PipeType::Vertical | PipeType::DownRight | PipeType::DownLeft => !status,
|
||||
_ => status,
|
||||
};
|
||||
} else if status {
|
||||
pieces.insert(pos, PipeType::Inner);
|
||||
} else {
|
||||
pieces.insert(pos, PipeType::Outer);
|
||||
}
|
||||
});
|
||||
});
|
||||
/* Debug
|
||||
println!();
|
||||
(corners.0 .1..=corners.1 .1).for_each(|y| {
|
||||
(corners.0.0..=corners.1.0).for_each(|x| {
|
||||
let p = pieces.get(&(x,y).into()).unwrap();
|
||||
print!("{p}");
|
||||
});
|
||||
print!("\n");
|
||||
});
|
||||
*/
|
||||
pieces
|
||||
.values()
|
||||
.filter(|x| **x == PipeType::Inner)
|
||||
.count()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn with_xy(span: Span) -> SpanIVec2 {
|
||||
let x = i32::try_from(span.get_column()).expect("overflow") - 1;
|
||||
let y = i32::try_from(span.location_line()).expect("wrap around") - 1;
|
||||
span.map_extra(|()| IVec2::new(x, y))
|
||||
}
|
||||
|
||||
fn parse_pipe(input: Span) -> IResult<Span, Pipe> {
|
||||
alt((
|
||||
tag("S").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Start,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("-").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Horizontal,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("|").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::Vertical,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("F").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::DownRight,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("7").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::DownLeft,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("L").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::UpRight,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag("J").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::UpLeft,
|
||||
position: position.extra,
|
||||
}),
|
||||
tag(".").map(with_xy).map(|position| Pipe {
|
||||
pipe_type: PipeType::None,
|
||||
position: position.extra,
|
||||
}),
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn parse_input(input: Span) -> IResult<Span, HashMap<IVec2, Pipe>> {
|
||||
fold_many1(
|
||||
terminated(many1(parse_pipe), alt((complete::line_ending, eof))),
|
||||
HashMap::new,
|
||||
|mut acc, x| {
|
||||
x.into_iter()
|
||||
.filter(|x| x.pipe_type != PipeType::None)
|
||||
.for_each(|x| {
|
||||
acc.insert(x.position, x);
|
||||
});
|
||||
acc
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"...........
|
||||
.S-------7.
|
||||
.|F-----7|.
|
||||
.||.....||.
|
||||
.||.....||.
|
||||
.|L-7.F-J|.
|
||||
.|..|.|..|.
|
||||
.L--J.L--J.
|
||||
...........",
|
||||
"4"
|
||||
)]
|
||||
#[case(
|
||||
".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...",
|
||||
"8"
|
||||
)]
|
||||
#[case(
|
||||
"FF7FSF7F7F7F7F7F---7
|
||||
L|LJ||||||||||||F--J
|
||||
FL-7LJLJ||||||LJL-77
|
||||
F--JF--7||LJLJ7F7FJ-
|
||||
L---JF-JLJ.||-FJLJJ7
|
||||
|F|F-JF---7F7-L7L|7|
|
||||
|FFJF7L7F-JF7|JL---7
|
||||
7-L-JL7||F7|L7F-7F7|
|
||||
L.L7LFJ|||||FJL7||LJ
|
||||
L7JLJL-JLJLJL--JLJ.L",
|
||||
"10"
|
||||
)]
|
||||
|
||||
fn part2_works(#[case] input: &str, #[case] expected: &str) {
|
||||
let result = part2(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
14
2023/day-11/Cargo.toml
Normal file
14
2023/day-11/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "day-11"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = { workspace = true }
|
||||
itertools = {workspace = true }
|
||||
glam = {workspace = true}
|
||||
nom_locate = {workspace = true }
|
||||
4
2023/day-11/src/lib.rs
Normal file
4
2023/day-11/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-11/src/main.rs
Normal file
12
2023/day-11/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_11::part1;
|
||||
use day_11::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input, 1_000_000);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
94
2023/day-11/src/part1.rs
Normal file
94
2023/day-11/src/part1.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use glam::IVec2;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let points = parse_input(input);
|
||||
let ((min_x, min_y), (mut max_x, /*mut*/ max_y)) = points.iter().fold(
|
||||
((i32::MAX, i32::MAX), (i32::MIN, i32::MIN)),
|
||||
|((min_x, min_y), (max_x, max_y)), pos| {
|
||||
let min_x = min_x.min(pos.x);
|
||||
let min_y = min_y.min(pos.y);
|
||||
let max_x = max_x.max(pos.x);
|
||||
let max_y = max_y.max(pos.y);
|
||||
((min_x, min_y), (max_x, max_y))
|
||||
},
|
||||
);
|
||||
let mut modifier = 0;
|
||||
let mut adjusted_points = HashSet::new();
|
||||
for x in min_x..=max_x {
|
||||
let column = (min_y..=max_y)
|
||||
.filter_map(|y| points.get(&(x, y).into()))
|
||||
.collect::<Vec<_>>();
|
||||
if column.is_empty() {
|
||||
modifier += 1;
|
||||
}
|
||||
for point in column {
|
||||
adjusted_points.insert(*point + IVec2::new(modifier, 0));
|
||||
}
|
||||
}
|
||||
max_x += modifier;
|
||||
|
||||
let mut modifier = 0;
|
||||
let mut points = HashSet::new();
|
||||
for y in min_y..=max_y {
|
||||
let row = (min_x..=max_x)
|
||||
.filter_map(|x| adjusted_points.get(&(x, y).into()))
|
||||
.collect::<Vec<_>>();
|
||||
if row.is_empty() {
|
||||
modifier += 1;
|
||||
}
|
||||
for point in row {
|
||||
points.insert(*point + IVec2::new(0, modifier));
|
||||
}
|
||||
}
|
||||
//max_y += modifier;
|
||||
(points
|
||||
.iter()
|
||||
.cartesian_product(points.iter())
|
||||
.filter_map(|(a, b)| (a != b).then_some(*a - *b))
|
||||
.map(|pos| pos.x.abs() + pos.y.abs())
|
||||
.sum::<i32>()
|
||||
/ 2)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> HashSet<IVec2> {
|
||||
input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
line.chars().enumerate().filter_map(move |(x, c)| {
|
||||
(c != '.').then_some(IVec2::new(
|
||||
i32::try_from(x).unwrap(),
|
||||
i32::try_from(y).unwrap(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "...#......
|
||||
.......#..
|
||||
#.........
|
||||
..........
|
||||
......#...
|
||||
.#........
|
||||
.........#
|
||||
..........
|
||||
.......#..
|
||||
#...#.....";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "374".to_string());
|
||||
}
|
||||
}
|
||||
102
2023/day-11/src/part2.rs
Normal file
102
2023/day-11/src/part2.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use glam::I64Vec2;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// day 11 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part2(input: &str, modr: i64) -> String {
|
||||
let points = parse_input(input);
|
||||
let ((min_x, min_y), (mut max_x, /*mut*/ max_y)) = points.iter().fold(
|
||||
((i64::MAX, i64::MAX), (i64::MIN, i64::MIN)),
|
||||
|((min_x, min_y), (max_x, max_y)), pos| {
|
||||
let min_x = min_x.min(pos.x);
|
||||
let min_y = min_y.min(pos.y);
|
||||
let max_x = max_x.max(pos.x);
|
||||
let max_y = max_y.max(pos.y);
|
||||
((min_x, min_y), (max_x, max_y))
|
||||
},
|
||||
);
|
||||
let mut modifier = 0;
|
||||
let mut adjusted_points = HashSet::new();
|
||||
for x in min_x..=max_x {
|
||||
let column = (min_y..=max_y)
|
||||
.filter_map(|y| points.get(&(x, y).into()))
|
||||
.collect::<Vec<_>>();
|
||||
if column.is_empty() {
|
||||
modifier += modr - 1;
|
||||
}
|
||||
for point in column {
|
||||
adjusted_points.insert(*point + I64Vec2::new(modifier, 0));
|
||||
}
|
||||
}
|
||||
max_x += modifier;
|
||||
|
||||
let mut modifier = 0;
|
||||
let mut points = HashSet::new();
|
||||
for y in min_y..=max_y {
|
||||
let row = (min_x..=max_x)
|
||||
.filter_map(|x| adjusted_points.get(&(x, y).into()))
|
||||
.collect::<Vec<_>>();
|
||||
if row.is_empty() {
|
||||
modifier += modr - 1;
|
||||
}
|
||||
for point in row {
|
||||
points.insert(*point + I64Vec2::new(0, modifier));
|
||||
}
|
||||
}
|
||||
//max_y += modifier;
|
||||
(points
|
||||
.iter()
|
||||
.cartesian_product(points.iter())
|
||||
.filter_map(|(a, b)| (*a != *b).then_some(*a - *b))
|
||||
.map(|a| u64::try_from(a.x.abs() + a.y.abs()).unwrap())
|
||||
.sum::<u64>()
|
||||
/ 2)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> HashSet<I64Vec2> {
|
||||
input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
line.chars().enumerate().filter_map(move |(x, c)| {
|
||||
(c != '.').then_some(I64Vec2::new(
|
||||
i64::try_from(x).unwrap(),
|
||||
i64::try_from(y).unwrap(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "...#......
|
||||
.......#..
|
||||
#.........
|
||||
..........
|
||||
......#...
|
||||
.#........
|
||||
.........#
|
||||
..........
|
||||
.......#..
|
||||
#...#.....";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT, 10);
|
||||
assert_eq!(result, "1030".to_string());
|
||||
}
|
||||
}
|
||||
13
2023/day-2/Cargo.toml
Normal file
13
2023/day-2/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "day-2"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
4
2023/day-2/src/lib.rs
Normal file
4
2023/day-2/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::part1;
|
||||
pub mod part2;
|
||||
pub use crate::part2::part2;
|
||||
12
2023/day-2/src/main.rs
Normal file
12
2023/day-2/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_2::part1;
|
||||
use day_2::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
115
2023/day-2/src/part1.rs
Normal file
115
2023/day-2/src/part1.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use log::debug;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{self, newline},
|
||||
multi::separated_list1,
|
||||
sequence::{preceded, separated_pair},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Round {
|
||||
pub red_n: u32,
|
||||
pub green_n: u32,
|
||||
pub blue_n: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Game {
|
||||
pub id: u32,
|
||||
pub rounds: Vec<Round>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn to_part1(&self) -> Option<u32> {
|
||||
if self
|
||||
.rounds
|
||||
.iter()
|
||||
.find_map(|r| {
|
||||
//TODO if inverted use find_map
|
||||
if r.red_n > 12 || r.green_n > 13 || r.blue_n > 14 {
|
||||
Some(self.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(self.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// part2 of day 2 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, games) = process_input(input).expect("there should be input");
|
||||
debug!("{games:?}");
|
||||
games
|
||||
.iter()
|
||||
.filter_map(Game::to_part1)
|
||||
.sum::<u32>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
|
||||
let (i, (cnt, color)) =
|
||||
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
|
||||
Ok((i, (cnt, color.to_owned())))
|
||||
}
|
||||
|
||||
fn process_round(input: &str) -> nom::IResult<&str, Round> {
|
||||
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
|
||||
let mut round = Round {
|
||||
red_n: 0,
|
||||
green_n: 0,
|
||||
blue_n: 0,
|
||||
};
|
||||
for (cnt, color) in blocks {
|
||||
match color.as_str() {
|
||||
"red" => round.red_n = cnt,
|
||||
"green" => round.green_n = cnt,
|
||||
"blue" => round.blue_n = cnt,
|
||||
_ => panic!("this should be a color name"),
|
||||
};
|
||||
}
|
||||
Ok((i, round))
|
||||
}
|
||||
|
||||
fn process_game(input: &str) -> nom::IResult<&str, Game> {
|
||||
let (i, (id, rounds)) = separated_pair(
|
||||
preceded(tag("Game "), complete::u32),
|
||||
tag(": "),
|
||||
separated_list1(tag("; "), process_round),
|
||||
)(input)?;
|
||||
Ok((i, Game { id, rounds }))
|
||||
}
|
||||
|
||||
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
|
||||
separated_list1(newline, process_game)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "8".to_string());
|
||||
}
|
||||
}
|
||||
106
2023/day-2/src/part2.rs
Normal file
106
2023/day-2/src/part2.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{self, newline},
|
||||
multi::separated_list1,
|
||||
sequence::{preceded, separated_pair},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Round {
|
||||
pub red_n: u32,
|
||||
pub green_n: u32,
|
||||
pub blue_n: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Game {
|
||||
pub _id: u32,
|
||||
pub rounds: Vec<Round>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn to_power(&self) -> u64 {
|
||||
let (r, g, b) = self.rounds.iter().fold((0_u64, 0_u64, 0_u64), |acc, x| {
|
||||
let (mut val_r, mut val_g, mut val_b) = acc;
|
||||
if u64::from(x.red_n) > acc.0 {
|
||||
val_r = x.red_n.into();
|
||||
}
|
||||
if u64::from(x.green_n) > acc.1 {
|
||||
val_g = x.green_n.into();
|
||||
}
|
||||
if u64::from(x.blue_n) > acc.2 {
|
||||
val_b = x.blue_n.into();
|
||||
}
|
||||
(val_r, val_g, val_b)
|
||||
});
|
||||
r * g * b
|
||||
}
|
||||
}
|
||||
|
||||
/// part2 of day 2 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, games) = process_input(input).expect("there should be input");
|
||||
games.iter().map(Game::to_power).sum::<u64>().to_string()
|
||||
}
|
||||
|
||||
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
|
||||
let (i, (cnt, color)) =
|
||||
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
|
||||
Ok((i, (cnt, color.to_owned())))
|
||||
}
|
||||
|
||||
fn process_round(input: &str) -> nom::IResult<&str, Round> {
|
||||
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
|
||||
let mut round = Round {
|
||||
red_n: 0,
|
||||
green_n: 0,
|
||||
blue_n: 0,
|
||||
};
|
||||
for (cnt, color) in blocks {
|
||||
match color.as_str() {
|
||||
"red" => round.red_n = cnt,
|
||||
"green" => round.green_n = cnt,
|
||||
"blue" => round.blue_n = cnt,
|
||||
_ => panic!("this should be a color name"),
|
||||
};
|
||||
}
|
||||
Ok((i, round))
|
||||
}
|
||||
|
||||
fn process_game(input: &str) -> nom::IResult<&str, Game> {
|
||||
let (i, (id, rounds)) = separated_pair(
|
||||
preceded(tag("Game "), complete::u32),
|
||||
tag(": "),
|
||||
separated_list1(tag("; "), process_round),
|
||||
)(input)?;
|
||||
Ok((i, Game { _id: id, rounds }))
|
||||
}
|
||||
|
||||
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
|
||||
separated_list1(newline, process_game)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "2286".to_string());
|
||||
}
|
||||
}
|
||||
12
2023/day-3/Cargo.toml
Normal file
12
2023/day-3/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "day-3"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
4
2023/day-3/src/lib.rs
Normal file
4
2023/day-3/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-3/src/main.rs
Normal file
12
2023/day-3/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_3::part1;
|
||||
use day_3::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
129
2023/day-3/src/part1.rs
Normal file
129
2023/day-3/src/part1.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SerialNumber {
|
||||
pub no: u64,
|
||||
pub start: (usize, usize),
|
||||
pub end: (usize, usize),
|
||||
}
|
||||
|
||||
impl SerialNumber {
|
||||
fn generate_adjacent(&self) -> Vec<(usize, usize)> {
|
||||
let start_row = if self.start.0 == 0 {
|
||||
0
|
||||
} else {
|
||||
self.start.0 - 1
|
||||
};
|
||||
let start_line = if self.start.1 == 0 {
|
||||
0
|
||||
} else {
|
||||
self.start.1 - 1
|
||||
};
|
||||
(start_row..=(self.end.0 + 1))
|
||||
.flat_map(|x| (start_line..=(self.end.1 + 1)).map(move |y| (x, y)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (serials, symbols) = parse_input(input);
|
||||
serials
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
x.generate_adjacent()
|
||||
.iter()
|
||||
.any(|t| symbols.get(t).is_some())
|
||||
})
|
||||
.map(|x| x.no)
|
||||
.sum::<u64>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> (Vec<SerialNumber>, BTreeMap<(usize, usize), char>) {
|
||||
let mut numbers = Vec::new();
|
||||
let mut symbols = BTreeMap::new();
|
||||
for (line_no, line) in input.lines().enumerate() {
|
||||
let mut prev_char = None;
|
||||
let mut cur_no = 0_u64;
|
||||
let mut cur_no_row_start = 0_usize;
|
||||
for (row_no, c) in line.chars().enumerate() {
|
||||
if let Some(d) = c.to_digit(10) {
|
||||
if prev_char.is_some() {
|
||||
cur_no = cur_no * 10 + u64::from(d);
|
||||
} else {
|
||||
cur_no = u64::from(d);
|
||||
cur_no_row_start = row_no;
|
||||
}
|
||||
prev_char = Some(c);
|
||||
} else {
|
||||
if prev_char.is_some() {
|
||||
//handle saving number off
|
||||
numbers.push(SerialNumber {
|
||||
no: cur_no,
|
||||
start: (cur_no_row_start, line_no),
|
||||
end: (row_no - 1, line_no),
|
||||
});
|
||||
}
|
||||
prev_char = None;
|
||||
if c == '.' {
|
||||
//move along space
|
||||
continue;
|
||||
}
|
||||
//store symbol
|
||||
let _ = symbols.insert((row_no, line_no), c);
|
||||
}
|
||||
}
|
||||
//need to account for new line numbers
|
||||
if prev_char.is_some() {
|
||||
numbers.push(SerialNumber {
|
||||
no: cur_no,
|
||||
start: (cur_no_row_start, line_no),
|
||||
end: (line.len() - 1, line_no),
|
||||
});
|
||||
}
|
||||
}
|
||||
(numbers, symbols)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "467..114..
|
||||
...*......
|
||||
..35..633.
|
||||
......#...
|
||||
617*......
|
||||
.....+.58.
|
||||
..592.....
|
||||
......755.
|
||||
...$.*....
|
||||
.664.598..";
|
||||
|
||||
const INPUT2: &str = "12.......*..
|
||||
+.........34
|
||||
.......-12..
|
||||
..78........
|
||||
..*....60...
|
||||
78.........9
|
||||
.5.....23..$
|
||||
8...90*12...
|
||||
............
|
||||
2.2......12.
|
||||
.*.........*
|
||||
1.1..503+.56";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "4361".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_works_more() {
|
||||
let result = part1(INPUT2);
|
||||
assert_eq!(result, "925".to_string());
|
||||
}
|
||||
}
|
||||
113
2023/day-3/src/part2.rs
Normal file
113
2023/day-3/src/part2.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SerialNumber {
|
||||
pub no: u64,
|
||||
pub start: (usize, usize),
|
||||
pub end: (usize, usize),
|
||||
}
|
||||
|
||||
impl SerialNumber {
|
||||
fn is_adjacent(&self, pos: (usize, usize)) -> bool {
|
||||
usize::abs_diff(self.start.1, pos.1) < 2
|
||||
&& self.start.0 < 2 + pos.0
|
||||
&& pos.0 < 2 + self.end.0
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (serials, symbols) = parse_input(input);
|
||||
symbols
|
||||
.iter()
|
||||
.filter_map(|(key, value)| if *value == '*' { Some(*key) } else { None })
|
||||
.filter_map(|pos| {
|
||||
let serials = serials
|
||||
.iter()
|
||||
.filter_map(|serial| {
|
||||
if serial.is_adjacent(pos) {
|
||||
Some(serial.no)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<u64>>();
|
||||
if serials.len() == 2 {
|
||||
Some(serials[0] * serials[1])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sum::<u64>()
|
||||
.to_string()
|
||||
//find all serials next to '*' and map with '*' location
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> (Vec<SerialNumber>, BTreeMap<(usize, usize), char>) {
|
||||
let mut numbers = Vec::new();
|
||||
let mut symbols = BTreeMap::new();
|
||||
for (line_no, line) in input.lines().enumerate() {
|
||||
let mut prev_char = None;
|
||||
let mut cur_no = 0_u64;
|
||||
let mut cur_no_row_start = 0_usize;
|
||||
for (row_no, c) in line.chars().enumerate() {
|
||||
if let Some(d) = c.to_digit(10) {
|
||||
if prev_char.is_some() {
|
||||
cur_no = cur_no * 10 + u64::from(d);
|
||||
} else {
|
||||
cur_no = u64::from(d);
|
||||
cur_no_row_start = row_no;
|
||||
}
|
||||
prev_char = Some(c);
|
||||
} else {
|
||||
if prev_char.is_some() {
|
||||
//handle saving number off
|
||||
numbers.push(SerialNumber {
|
||||
no: cur_no,
|
||||
start: (cur_no_row_start, line_no),
|
||||
end: (row_no - 1, line_no),
|
||||
});
|
||||
}
|
||||
prev_char = None;
|
||||
if c == '.' {
|
||||
//move along space
|
||||
continue;
|
||||
}
|
||||
//store symbol
|
||||
let _ = symbols.insert((row_no, line_no), c);
|
||||
}
|
||||
}
|
||||
//need to account for new line numbers
|
||||
if prev_char.is_some() {
|
||||
numbers.push(SerialNumber {
|
||||
no: cur_no,
|
||||
start: (cur_no_row_start, line_no),
|
||||
end: (line.len() - 1, line_no),
|
||||
});
|
||||
}
|
||||
}
|
||||
(numbers, symbols)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "467..114..
|
||||
...*......
|
||||
..35..633.
|
||||
......#...
|
||||
617*......
|
||||
.....+.58.
|
||||
..592.....
|
||||
......755.
|
||||
...$.*....
|
||||
.664.598..";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "467835".to_string());
|
||||
}
|
||||
}
|
||||
14
2023/day-4/Cargo.toml
Normal file
14
2023/day-4/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "day-4"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
rstest = {workspace = true}
|
||||
rstest_reuse = {workspace = true}
|
||||
4
2023/day-4/src/lib.rs
Normal file
4
2023/day-4/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-4/src/main.rs
Normal file
12
2023/day-4/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_4::part1;
|
||||
use day_4::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
121
2023/day-4/src/part1.rs
Normal file
121
2023/day-4/src/part1.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::{fold_many1, separated_list1},
|
||||
sequence::{preceded, separated_pair, tuple},
|
||||
IResult,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
struct Card {
|
||||
pub _id: u8,
|
||||
pub game_numbers: HashSet<u8>,
|
||||
pub my_numbers: HashSet<u8>,
|
||||
}
|
||||
|
||||
impl Card {
|
||||
fn get_win_count(&self) -> usize {
|
||||
self.my_numbers.intersection(&self.game_numbers).count()
|
||||
}
|
||||
fn get_score(&self) -> Option<usize> {
|
||||
let count = self.get_win_count();
|
||||
if count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(2_usize.pow(u32::try_from(count).expect("shouldn't have a lot of cards") - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// day 4 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, cards) = parse_input(input).expect("there should be input");
|
||||
cards
|
||||
.iter()
|
||||
.filter_map(Card::get_score)
|
||||
.sum::<usize>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_num_list(input: &str) -> IResult<&str, HashSet<u8>> {
|
||||
fold_many1(
|
||||
tuple((complete::u8, complete::space0)),
|
||||
HashSet::new,
|
||||
|mut acc, (x, _)| {
|
||||
acc.insert(x);
|
||||
acc
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_numbers(input: &str) -> IResult<&str, (HashSet<u8>, HashSet<u8>)> {
|
||||
separated_pair(
|
||||
parse_num_list,
|
||||
tuple((tag("|"), complete::space1)),
|
||||
parse_num_list,
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_card(input: &str) -> IResult<&str, Card> {
|
||||
let (input, (id, (my_numbers, game_numbers))) = separated_pair(
|
||||
preceded(tuple((tag("Card"), complete::space1)), complete::u8),
|
||||
tuple((tag(":"), complete::space1)),
|
||||
parse_numbers,
|
||||
)(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Card {
|
||||
_id: id,
|
||||
game_numbers,
|
||||
my_numbers,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
|
||||
separated_list1(complete::line_ending, parse_card)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case("Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53", Some(8))]
|
||||
#[case("Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19", Some(2))]
|
||||
#[case("Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1", Some(2))]
|
||||
#[case("Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83", Some(1))]
|
||||
#[case("Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36", None)]
|
||||
#[case("Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", None)]
|
||||
fn line_test(#[case] line: &str, #[case] expected: Option<usize>) {
|
||||
let (input, card) = parse_card(line).expect("card should be parsed");
|
||||
assert_eq!(input, "");
|
||||
assert_eq!(card.get_score(), expected);
|
||||
|
||||
}
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "13".to_string());
|
||||
}
|
||||
}
|
||||
121
2023/day-4/src/part2.rs
Normal file
121
2023/day-4/src/part2.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
combinator::map,
|
||||
multi::{fold_many1, separated_list1},
|
||||
sequence::{preceded, separated_pair, tuple},
|
||||
IResult,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
struct Card {
|
||||
pub id: usize,
|
||||
pub game_numbers: HashSet<u8>,
|
||||
pub my_numbers: HashSet<u8>,
|
||||
}
|
||||
impl Card {
|
||||
fn get_win_count(&self) -> usize {
|
||||
self.my_numbers.intersection(&self.game_numbers).count()
|
||||
}
|
||||
}
|
||||
|
||||
/// day 4 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, cards) = parse_input(input).expect("there should be input");
|
||||
let mut cards_had = BTreeMap::new();
|
||||
for card in cards {
|
||||
if let Some(x) = cards_had.get_mut(&card.id) {
|
||||
*x += 1;
|
||||
} else {
|
||||
cards_had.insert(card.id, 1);
|
||||
}
|
||||
let next_id = card.id + 1;
|
||||
let last_winner = card.id + card.get_win_count();
|
||||
//println!("{} - {next_id} {last_winner}", card.id);
|
||||
if last_winner < next_id {
|
||||
continue;
|
||||
}
|
||||
let card_count = *cards_had.get(&card.id).expect("already should have cards");
|
||||
for id in next_id..=last_winner {
|
||||
if let Some(x) = cards_had.get_mut(&id) {
|
||||
*x += card_count;
|
||||
} else {
|
||||
cards_had.insert(id, card_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
//println!("{cards_had:#?}");
|
||||
|
||||
cards_had.values().sum::<usize>().to_string()
|
||||
}
|
||||
|
||||
fn parse_num_list(input: &str) -> IResult<&str, HashSet<u8>> {
|
||||
fold_many1(
|
||||
tuple((complete::u8, complete::space0)),
|
||||
HashSet::new,
|
||||
|mut acc, (x, _)| {
|
||||
acc.insert(x);
|
||||
acc
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_numbers(input: &str) -> IResult<&str, (HashSet<u8>, HashSet<u8>)> {
|
||||
separated_pair(
|
||||
parse_num_list,
|
||||
tuple((tag("|"), complete::space1)),
|
||||
parse_num_list,
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_card(input: &str) -> IResult<&str, Card> {
|
||||
let (input, (id, (my_numbers, game_numbers))) = separated_pair(
|
||||
preceded(
|
||||
tuple((tag("Card"), complete::space1)),
|
||||
map(complete::u8, usize::from),
|
||||
),
|
||||
tuple((tag(":"), complete::space1)),
|
||||
parse_numbers,
|
||||
)(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Card {
|
||||
id,
|
||||
game_numbers,
|
||||
my_numbers,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
|
||||
separated_list1(complete::line_ending, parse_card)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "30".to_string());
|
||||
}
|
||||
}
|
||||
12
2023/day-5/Cargo.toml
Normal file
12
2023/day-5/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "day-5"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
4
2023/day-5/src/lib.rs
Normal file
4
2023/day-5/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-5/src/main.rs
Normal file
12
2023/day-5/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_5::part1;
|
||||
use day_5::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
203
2023/day-5/src/part1.rs
Normal file
203
2023/day-5/src/part1.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
combinator::opt,
|
||||
multi::separated_list1,
|
||||
sequence::{separated_pair, terminated, tuple},
|
||||
IResult,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct ParseTypeError;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum Type {
|
||||
Seed,
|
||||
Soil,
|
||||
Fertilizer,
|
||||
Water,
|
||||
Light,
|
||||
Temperature,
|
||||
Humidity,
|
||||
Location,
|
||||
}
|
||||
impl FromStr for Type {
|
||||
type Err = ParseTypeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"seed" => Ok(Self::Seed),
|
||||
"soil" => Ok(Self::Soil),
|
||||
"fertilizer" => Ok(Self::Fertilizer),
|
||||
"water" => Ok(Self::Water),
|
||||
"light" => Ok(Self::Light),
|
||||
"temperature" => Ok(Self::Temperature),
|
||||
"humidity" => Ok(Self::Humidity),
|
||||
"location" => Ok(Self::Location),
|
||||
_ => Err(ParseTypeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemMapEntry {
|
||||
pub to: u64,
|
||||
pub from: u64,
|
||||
pub count: u64,
|
||||
}
|
||||
|
||||
impl ItemMapEntry {
|
||||
fn to_out(&self, from: u64) -> Option<u64> {
|
||||
if from < self.from || self.from + self.count < from {
|
||||
None
|
||||
} else {
|
||||
Some(self.to + (from - self.from))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemMap {
|
||||
pub from_type: Type,
|
||||
pub to_type: Type,
|
||||
pub mapping: Vec<ItemMapEntry>,
|
||||
}
|
||||
|
||||
impl ItemMap {
|
||||
fn map(&self, from: u64) -> u64 {
|
||||
self.mapping
|
||||
.iter()
|
||||
.find_map(|x| x.to_out(from))
|
||||
.or(Some(from))
|
||||
.expect("always")
|
||||
}
|
||||
}
|
||||
/// part1 of day 5 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_input, (mut to_process, maps)) = parse_input(input).expect("aoc always has input");
|
||||
//println!("{_input}");
|
||||
let mut from_type = Type::Seed;
|
||||
while from_type != Type::Location {
|
||||
let current_map = maps
|
||||
.iter()
|
||||
.find(|x| x.from_type == from_type)
|
||||
.expect("should always find");
|
||||
to_process = to_process
|
||||
.iter()
|
||||
.map(|x| current_map.map(*x))
|
||||
.collect::<Vec<_>>();
|
||||
//println!("{to_process:#?}");
|
||||
from_type = current_map.to_type;
|
||||
}
|
||||
//println!("{to_process:#?}");
|
||||
to_process
|
||||
.iter()
|
||||
.min()
|
||||
.expect("always a number")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_item_map_entry(input: &str) -> IResult<&str, ItemMapEntry> {
|
||||
let (input, to) = complete::u64(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
let (input, from) = complete::u64(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
let (input, count) = complete::u64(input)?;
|
||||
Ok((input, ItemMapEntry { to, from, count }))
|
||||
}
|
||||
|
||||
fn parse_to_from(input: &str) -> IResult<&str, (Type, Type)> {
|
||||
let (input, (to_type, from_type)) =
|
||||
separated_pair(complete::alpha1, tag("-to-"), complete::alpha1)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
(
|
||||
to_type.parse().expect("there will be a to type"),
|
||||
from_type.parse().expect("there will be a from type"),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_map(input: &str) -> IResult<&str, ItemMap> {
|
||||
let (input, (from_type, to_type)) =
|
||||
terminated(parse_to_from, tuple((complete::space1, tag("map:"))))(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, mapping) = separated_list1(complete::line_ending, parse_item_map_entry)(input)?;
|
||||
let (input, _) = opt(complete::line_ending)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
ItemMap {
|
||||
from_type,
|
||||
to_type,
|
||||
mapping,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_seeds(input: &str) -> IResult<&str, Vec<u64>> {
|
||||
let (input, _) = tag("seeds:")(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
separated_list1(complete::space1, complete::u64)(input)
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, (Vec<u64>, Vec<ItemMap>)> {
|
||||
let (input, seeds) = terminated(parse_seeds, complete::line_ending)(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, maps) = separated_list1(complete::line_ending, parse_map)(input)?;
|
||||
Ok((input, (seeds, maps)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "35".to_string());
|
||||
}
|
||||
}
|
||||
284
2023/day-5/src/part2.rs
Normal file
284
2023/day-5/src/part2.rs
Normal file
@@ -0,0 +1,284 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use core::ops::Range;
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
combinator::opt,
|
||||
multi::separated_list1,
|
||||
sequence::{separated_pair, terminated, tuple},
|
||||
IResult,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct ParseTypeError;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum Type {
|
||||
Seed,
|
||||
Soil,
|
||||
Fertilizer,
|
||||
Water,
|
||||
Light,
|
||||
Temperature,
|
||||
Humidity,
|
||||
Location,
|
||||
}
|
||||
impl FromStr for Type {
|
||||
type Err = ParseTypeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"seed" => Ok(Self::Seed),
|
||||
"soil" => Ok(Self::Soil),
|
||||
"fertilizer" => Ok(Self::Fertilizer),
|
||||
"water" => Ok(Self::Water),
|
||||
"light" => Ok(Self::Light),
|
||||
"temperature" => Ok(Self::Temperature),
|
||||
"humidity" => Ok(Self::Humidity),
|
||||
"location" => Ok(Self::Location),
|
||||
_ => Err(ParseTypeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemMapEntry {
|
||||
pub to: Range<u64>,
|
||||
pub from: Range<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemMap {
|
||||
pub from_type: Type,
|
||||
pub to_type: Type,
|
||||
pub mapping: Vec<ItemMapEntry>,
|
||||
}
|
||||
|
||||
impl ItemMap {
|
||||
fn input_to_output(&self, input: &Range<u64>) -> Vec<Range<u64>> {
|
||||
/*if let Some(within) = self
|
||||
.mapping
|
||||
.iter()
|
||||
.find(|x| x.from.contains(&input.start) && x.from.contains(&input.end))
|
||||
{
|
||||
//fully contained
|
||||
let offset = input.start - within.from.start;
|
||||
let to_start = within.to.start + offset;
|
||||
let to_end = to_start + u64::abs_diff(input.end, input.start);
|
||||
if to_start == 0 {
|
||||
println!("{input:#?}");
|
||||
}
|
||||
return vec![to_start..to_end];
|
||||
}*/
|
||||
let mut output = Vec::new();
|
||||
let mut input = input.start..input.end;
|
||||
loop {
|
||||
input =
|
||||
if let Some(within) = self.mapping.iter().find(|x| x.from.contains(&input.start)) {
|
||||
//println!("front - {input:?} - {within:#?} - {:?}", self.from_type);
|
||||
let (to_start, to_end) = if within.to.start > within.from.start {
|
||||
let offset = within.to.start - within.from.start;
|
||||
let end = if input.end + offset > within.to.end {
|
||||
within.to.end
|
||||
} else {
|
||||
input.end + offset
|
||||
};
|
||||
(input.start + offset, end)
|
||||
} else {
|
||||
let offset = within.from.start - within.to.start;
|
||||
let end = if input.end - offset > within.to.end {
|
||||
within.to.end
|
||||
} else {
|
||||
input.end - offset
|
||||
};
|
||||
(input.start - offset, end)
|
||||
};
|
||||
output.push(to_start..to_end);
|
||||
if input.end <= within.from.end {
|
||||
break;
|
||||
}
|
||||
within.from.end..input.end
|
||||
} else if let Some(within) = self
|
||||
.mapping
|
||||
.iter()
|
||||
.find(|x| x.from.contains(&(input.end - 1)))
|
||||
{
|
||||
//println!("end - {input:?} - {within:#?} - {:?}", self.from_type);
|
||||
let (to_start, to_end) = if within.to.start > within.from.start {
|
||||
let offset = within.to.start - within.from.start;
|
||||
let start = if input.start + offset < within.to.start {
|
||||
within.to.start
|
||||
} else {
|
||||
input.start + offset
|
||||
};
|
||||
(start, input.end + offset)
|
||||
} else {
|
||||
let offset = within.from.start - within.to.start;
|
||||
let start = if input.start + offset < within.to.start {
|
||||
within.to.start
|
||||
} else {
|
||||
input.start + offset
|
||||
};
|
||||
(start, input.end - offset)
|
||||
};
|
||||
output.push(to_start..to_end);
|
||||
if input.start >= within.from.start {
|
||||
break;
|
||||
}
|
||||
input.start..within.from.start
|
||||
} else {
|
||||
//println!("else - {input:#?} - {:?}", self.from_type);
|
||||
output.push(input.clone());
|
||||
break;
|
||||
};
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
/// part2 of day 5 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_input, (mut to_process, maps)) = parse_input(input).expect("aoc always has input");
|
||||
//println!("{_input}");
|
||||
let mut from_type = Type::Seed;
|
||||
while from_type != Type::Location {
|
||||
let current_map = maps
|
||||
.iter()
|
||||
.find(|x| x.from_type == from_type)
|
||||
.expect("should always find");
|
||||
to_process = to_process
|
||||
.iter()
|
||||
.flat_map(|x| current_map.input_to_output(x))
|
||||
.unique()
|
||||
.collect::<Vec<_>>();
|
||||
//println!("{to_process:#?}");
|
||||
from_type = current_map.to_type;
|
||||
}
|
||||
//println!("{to_process:#?}");
|
||||
to_process
|
||||
.iter()
|
||||
.map(|x| x.start)
|
||||
.min()
|
||||
.expect("always a number")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_item_map_entry(input: &str) -> IResult<&str, ItemMapEntry> {
|
||||
let (input, to) = complete::u64(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
let (input, from) = complete::u64(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
let (input, count) = complete::u64(input)?;
|
||||
Ok((
|
||||
input,
|
||||
ItemMapEntry {
|
||||
to: to..(to + count),
|
||||
from: from..(from + count),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_to_from(input: &str) -> IResult<&str, (Type, Type)> {
|
||||
let (input, (to_type, from_type)) =
|
||||
separated_pair(complete::alpha1, tag("-to-"), complete::alpha1)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
(
|
||||
to_type.parse().expect("there will be a to type"),
|
||||
from_type.parse().expect("there will be a from type"),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_map(input: &str) -> IResult<&str, ItemMap> {
|
||||
let (input, (from_type, to_type)) =
|
||||
terminated(parse_to_from, tuple((complete::space1, tag("map:"))))(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, mapping) = separated_list1(complete::line_ending, parse_item_map_entry)(input)?;
|
||||
let (input, _) = opt(complete::line_ending)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
ItemMap {
|
||||
from_type,
|
||||
to_type,
|
||||
mapping,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_seed_range(input: &str) -> IResult<&str, Range<u64>> {
|
||||
let (input, (seed, count)) =
|
||||
separated_pair(complete::u64, complete::space1, complete::u64)(input)?;
|
||||
Ok((input, seed..(seed + count)))
|
||||
}
|
||||
|
||||
//TODO need to change so that it operates on the ranges and not on the actual numbers
|
||||
|
||||
fn parse_seeds(input: &str) -> IResult<&str, Vec<Range<u64>>> {
|
||||
let (input, _) = tag("seeds:")(input)?;
|
||||
let (input, _) = complete::space1(input)?;
|
||||
separated_list1(complete::space1, parse_seed_range)(input)
|
||||
//println!("{seed_ranges:?}");
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, (Vec<Range<u64>>, Vec<ItemMap>)> {
|
||||
let (input, seeds) = terminated(parse_seeds, complete::line_ending)(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, maps) = separated_list1(complete::line_ending, parse_map)(input)?;
|
||||
//println!("{seeds:?}");
|
||||
Ok((input, (seeds, maps)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "46".to_string());
|
||||
}
|
||||
}
|
||||
12
2023/day-6/Cargo.toml
Normal file
12
2023/day-6/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "day-6"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
4
2023/day-6/src/lib.rs
Normal file
4
2023/day-6/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-6/src/main.rs
Normal file
12
2023/day-6/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_6::part1;
|
||||
use day_6::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
72
2023/day-6/src/part1.rs
Normal file
72
2023/day-6/src/part1.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::separated_list1,
|
||||
sequence::{pair, preceded},
|
||||
IResult,
|
||||
};
|
||||
|
||||
/// part6 of day 1 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, races) = parse_input(input).expect("input expected");
|
||||
races
|
||||
.iter()
|
||||
.map(|(time, distance)| {
|
||||
(0..=*time)
|
||||
.filter_map(|x| {
|
||||
if (time - x) * x > *distance {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.product::<usize>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<(u64, u64)>> {
|
||||
let (input, time) = preceded(
|
||||
pair(tag("Time:"), complete::space1),
|
||||
separated_list1(complete::space1, complete::u64),
|
||||
)(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, distance) = preceded(
|
||||
pair(tag("Distance:"), complete::space1),
|
||||
separated_list1(complete::space1, complete::u64),
|
||||
)(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
time.iter()
|
||||
.interleave(distance.iter())
|
||||
.copied()
|
||||
.tuples()
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "Time: 7 15 30
|
||||
Distance: 9 40 200";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "288".to_string());
|
||||
}
|
||||
}
|
||||
72
2023/day-6/src/part2.rs
Normal file
72
2023/day-6/src/part2.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::separated_list1,
|
||||
sequence::{pair, preceded},
|
||||
IResult,
|
||||
};
|
||||
|
||||
/// part2 of day 2 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, race) = parse_input(input).expect("input expected");
|
||||
(0..=race.0)
|
||||
.filter_map(|x| {
|
||||
if (race.0 - x) * x > race.1 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.count()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, (u64, u64)> {
|
||||
let (input, time) = preceded(
|
||||
pair(tag("Time:"), complete::space1),
|
||||
separated_list1(complete::space1, complete::u64),
|
||||
)(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
let (input, distance) = preceded(
|
||||
pair(tag("Distance:"), complete::space1),
|
||||
separated_list1(complete::space1, complete::u64),
|
||||
)(input)?;
|
||||
let distance = distance
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.join("")
|
||||
.parse::<u64>()
|
||||
.expect("is a number");
|
||||
let time = time
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.join("")
|
||||
.parse::<u64>()
|
||||
.expect("is a number");
|
||||
|
||||
Ok((input, (time, distance)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "Time: 7 15 30
|
||||
Distance: 9 40 200";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "71503".to_string());
|
||||
}
|
||||
}
|
||||
18
2023/day-7/Cargo.toml
Normal file
18
2023/day-7/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "day-7"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
num = "0.4.1"
|
||||
num-traits = "0.2.17"
|
||||
dhat = { workspace = true }
|
||||
|
||||
[features]
|
||||
dhat-heap = []
|
||||
4
2023/day-7/src/lib.rs
Normal file
4
2023/day-7/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
19
2023/day-7/src/main.rs
Normal file
19
2023/day-7/src/main.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
use day_7::part1;
|
||||
use day_7::part2;
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let _profiler = dhat::Profiler::new_heap();
|
||||
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
190
2023/day-7/src/part1.rs
Normal file
190
2023/day-7/src/part1.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use itertools::Itertools;
|
||||
use nom::{character::complete, multi::separated_list1, sequence::separated_pair, IResult};
|
||||
use std::{
|
||||
cmp::{Ord, Ordering, PartialOrd},
|
||||
collections::BTreeMap,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Day1Part1Error;
|
||||
|
||||
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd)]
|
||||
enum Card {
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Ten,
|
||||
Jack,
|
||||
Queen,
|
||||
King,
|
||||
Ace,
|
||||
}
|
||||
impl FromStr for Card {
|
||||
type Err = Day1Part1Error;
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
match input {
|
||||
"2" => Ok(Self::Two),
|
||||
"3" => Ok(Self::Three),
|
||||
"4" => Ok(Self::Four),
|
||||
"5" => Ok(Self::Five),
|
||||
"6" => Ok(Self::Six),
|
||||
"7" => Ok(Self::Seven),
|
||||
"8" => Ok(Self::Eight),
|
||||
"9" => Ok(Self::Nine),
|
||||
"T" => Ok(Self::Ten),
|
||||
"J" => Ok(Self::Jack),
|
||||
"Q" => Ok(Self::Queen),
|
||||
"K" => Ok(Self::King),
|
||||
"A" => Ok(Self::Ace),
|
||||
_ => Err(Day1Part1Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Card> for &u32 {
|
||||
fn from(value: &Card) -> Self {
|
||||
match value {
|
||||
Card::Two => &2,
|
||||
Card::Three => &3,
|
||||
Card::Four => &4,
|
||||
Card::Five => &5,
|
||||
Card::Six => &6,
|
||||
Card::Seven => &7,
|
||||
Card::Eight => &8,
|
||||
Card::Nine => &9,
|
||||
Card::Ten => &10,
|
||||
Card::Jack => &11,
|
||||
Card::Queen => &12,
|
||||
Card::King => &13,
|
||||
Card::Ace => &14,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
enum HandType {
|
||||
HighCard,
|
||||
OnePair,
|
||||
TwoPair,
|
||||
ThreeOfAKind,
|
||||
FullHouse,
|
||||
FourOfAKind,
|
||||
FiveOfAKind,
|
||||
}
|
||||
|
||||
impl From<&Hand> for HandType {
|
||||
fn from(value: &Hand) -> Self {
|
||||
let map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| {
|
||||
if let Some(c) = acc.get_mut(card) {
|
||||
*c += 1;
|
||||
} else {
|
||||
acc.insert(card, 1);
|
||||
}
|
||||
acc
|
||||
});
|
||||
match map
|
||||
.iter()
|
||||
.sorted_by(|a, b| b.1.cmp(a.1))
|
||||
.collect::<Vec<_>>()[..]
|
||||
{
|
||||
[(_, 5), ..] => Self::FiveOfAKind,
|
||||
[(_, 4), ..] => Self::FourOfAKind,
|
||||
[(_, 3), (_, 2), ..] => Self::FullHouse,
|
||||
[(_, 3), ..] => Self::ThreeOfAKind,
|
||||
[(_, 2), (_, 2), ..] => Self::TwoPair,
|
||||
[(_, 2), ..] => Self::OnePair,
|
||||
_ => Self::HighCard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct Hand {
|
||||
pub cards: [Card; 5],
|
||||
pub bet: u32,
|
||||
}
|
||||
impl PartialOrd for Hand {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for Hand {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let a = HandType::from(self);
|
||||
let b = HandType::from(other);
|
||||
let c = a.cmp(&b);
|
||||
match c {
|
||||
Ordering::Equal => self
|
||||
.cards
|
||||
.iter()
|
||||
.interleave(other.cards.iter())
|
||||
.tuples::<(_, _)>()
|
||||
.find_map(|(a, b)| match a.cmp(b) {
|
||||
Ordering::Equal => None,
|
||||
x => Some(x),
|
||||
})
|
||||
.unwrap_or(Ordering::Equal),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// part1 of day 7 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, mut hands) = parse_input(input).expect("always valid input");
|
||||
hands.sort();
|
||||
|
||||
hands
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, hand)| (i + 1) * hand.bet as usize)
|
||||
.sum::<usize>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_hand(input: &str) -> IResult<&str, Hand> {
|
||||
let (input, (cards, bet)) =
|
||||
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
||||
let cards = cards
|
||||
.chars()
|
||||
.filter_map(|c| c.to_string().parse().ok())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.expect("should work");
|
||||
Ok((input, Hand { cards, bet }))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Hand>> {
|
||||
separated_list1(complete::line_ending, parse_hand)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "6440".to_string());
|
||||
}
|
||||
}
|
||||
224
2023/day-7/src/part2.rs
Normal file
224
2023/day-7/src/part2.rs
Normal file
@@ -0,0 +1,224 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use itertools::Itertools;
|
||||
use nom::{character::complete, multi::separated_list1, sequence::separated_pair, IResult};
|
||||
use std::{
|
||||
cmp::{Ord, Ordering, PartialOrd},
|
||||
collections::BTreeMap,
|
||||
str::FromStr,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Day1Part2Error;
|
||||
|
||||
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Copy, Clone)]
|
||||
enum Card {
|
||||
Joker = 1,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Ten,
|
||||
Queen,
|
||||
King,
|
||||
Ace,
|
||||
}
|
||||
impl FromStr for Card {
|
||||
type Err = Day1Part2Error;
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
match input {
|
||||
"2" => Ok(Self::Two),
|
||||
"3" => Ok(Self::Three),
|
||||
"4" => Ok(Self::Four),
|
||||
"5" => Ok(Self::Five),
|
||||
"6" => Ok(Self::Six),
|
||||
"7" => Ok(Self::Seven),
|
||||
"8" => Ok(Self::Eight),
|
||||
"9" => Ok(Self::Nine),
|
||||
"T" => Ok(Self::Ten),
|
||||
"J" => Ok(Self::Joker),
|
||||
"Q" => Ok(Self::Queen),
|
||||
"K" => Ok(Self::King),
|
||||
"A" => Ok(Self::Ace),
|
||||
_ => Err(Day1Part2Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Card> for &u32 {
|
||||
fn from(value: &Card) -> Self {
|
||||
match value {
|
||||
Card::Two => &2,
|
||||
Card::Three => &3,
|
||||
Card::Four => &4,
|
||||
Card::Five => &5,
|
||||
Card::Six => &6,
|
||||
Card::Seven => &7,
|
||||
Card::Eight => &8,
|
||||
Card::Nine => &9,
|
||||
Card::Ten => &10,
|
||||
Card::Joker => &1,
|
||||
Card::Queen => &12,
|
||||
Card::King => &13,
|
||||
Card::Ace => &14,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for Card {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||
let c = match self {
|
||||
Card::Joker => 'J',
|
||||
Card::Two => '2',
|
||||
Card::Three => '3',
|
||||
Card::Four => '4',
|
||||
Card::Five => '5',
|
||||
Card::Six => '6',
|
||||
Card::Seven => '7',
|
||||
Card::Eight => '8',
|
||||
Card::Nine => '9',
|
||||
Card::Ten => 'T',
|
||||
Card::Queen => 'Q',
|
||||
Card::King => 'K',
|
||||
Card::Ace => 'A',
|
||||
};
|
||||
write!(f, "{c}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
enum HandType {
|
||||
HighCard,
|
||||
OnePair,
|
||||
TwoPair,
|
||||
ThreeOfAKind,
|
||||
FullHouse,
|
||||
FourOfAKind,
|
||||
FiveOfAKind,
|
||||
}
|
||||
|
||||
impl From<&Hand> for HandType {
|
||||
fn from(value: &Hand) -> Self {
|
||||
let mut map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| {
|
||||
if let Some(c) = acc.get_mut(card) {
|
||||
*c += 1;
|
||||
} else {
|
||||
acc.insert(card, 1);
|
||||
}
|
||||
acc
|
||||
});
|
||||
let jokers = map.remove(&Card::Joker).unwrap_or(0);
|
||||
match map
|
||||
.iter()
|
||||
.sorted_by(|a, b| b.1.cmp(a.1))
|
||||
.collect::<Vec<_>>()[..]
|
||||
{
|
||||
[(_, x), ..] if jokers + x == 5 => Self::FiveOfAKind,
|
||||
[] if jokers == 5 => Self::FiveOfAKind,
|
||||
[(_, x), ..] if jokers + x == 4 => Self::FourOfAKind,
|
||||
[(_, 3), (_, 2)] => Self::FullHouse,
|
||||
[(_, 2), (_, 2)] if jokers == 1 => Self::FullHouse,
|
||||
[(_, x), ..] if jokers + x == 3 => Self::ThreeOfAKind,
|
||||
[(_, 2), (_, 2), ..] => Self::TwoPair,
|
||||
[(_, x), ..] if jokers + x == 2 => Self::OnePair,
|
||||
_ => Self::HighCard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
struct Hand {
|
||||
pub cards: [Card; 5],
|
||||
pub bet: u32,
|
||||
}
|
||||
impl PartialOrd for Hand {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for Hand {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let a = HandType::from(self);
|
||||
let b = HandType::from(other);
|
||||
let c = a.cmp(&b);
|
||||
match c {
|
||||
Ordering::Equal => self
|
||||
.cards
|
||||
.iter()
|
||||
.interleave(other.cards.iter())
|
||||
.tuples::<(_, _)>()
|
||||
.find_map(|(a, b)| match a.cmp(b) {
|
||||
Ordering::Equal => None,
|
||||
x => Some(x),
|
||||
})
|
||||
.unwrap_or(Ordering::Equal),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// part2 of day 7 of AOC 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the puszzle input
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whenever the input isn't parsable
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, mut hands) = parse_input(input).expect("always valid input");
|
||||
hands.sort();
|
||||
|
||||
hands
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, hand)| (i + 1) * hand.bet as usize)
|
||||
.sum::<usize>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_hand(input: &str) -> IResult<&str, Hand> {
|
||||
let (input, (cards, bet)) =
|
||||
separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?;
|
||||
let cards = cards
|
||||
.chars()
|
||||
.filter_map(|c| c.to_string().parse().ok())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.expect("should work");
|
||||
Ok((input, Hand { cards, bet }))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Hand>> {
|
||||
separated_list1(complete::line_ending, parse_hand)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn qtest() {
|
||||
let a_str = "JKKK2 9";
|
||||
let b_str = "QQQQ2 8";
|
||||
let (_, a_hand) = parse_hand(a_str).expect("shoould parse a");
|
||||
let (_, b_hand) = parse_hand(b_str).expect("should parse b");
|
||||
let c = a_hand.cmp(&b_hand);
|
||||
assert_eq!(c, Ordering::Less);
|
||||
}
|
||||
|
||||
const INPUT: &str = "32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "5905".to_string());
|
||||
}
|
||||
}
|
||||
13
2023/day-8/Cargo.toml
Normal file
13
2023/day-8/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "day-8"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
rstest = {workspace = true}
|
||||
4
2023/day-8/src/lib.rs
Normal file
4
2023/day-8/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-8/src/main.rs
Normal file
12
2023/day-8/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_8::part1;
|
||||
use day_8::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
134
2023/day-8/src/part1.rs
Normal file
134
2023/day-8/src/part1.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::{many1, separated_list1},
|
||||
sequence::{delimited, pair, separated_pair, tuple},
|
||||
IResult, Parser,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Branches {
|
||||
pub left: String,
|
||||
pub right: String,
|
||||
}
|
||||
|
||||
impl Branches {
|
||||
fn choose(&self, direction: Direction) -> &str {
|
||||
match direction {
|
||||
Direction::Left => &self.left,
|
||||
Direction::Right => &self.right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// day 8 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, (steps, branches)) = parse_input(input).expect("aoc expects valid input");
|
||||
|
||||
let mut current = "AAA";
|
||||
let mut count = 0_usize;
|
||||
for x in steps.iter().cycle() {
|
||||
if current == "ZZZ" {
|
||||
break;
|
||||
}
|
||||
current = branches.get(current).expect("aoc").choose(*x);
|
||||
count += 1;
|
||||
}
|
||||
count.to_string()
|
||||
}
|
||||
|
||||
fn parse_directions(input: &str) -> IResult<&str, Vec<Direction>> {
|
||||
let (input, directions) = many1(alt((
|
||||
tag("L").map(|_| Direction::Left),
|
||||
tag("R").map(|_| Direction::Right),
|
||||
)))(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
Ok((input, directions))
|
||||
}
|
||||
|
||||
fn parse_branches(input: &str) -> IResult<&str, Branches> {
|
||||
let (input, (left, right)) = delimited(
|
||||
pair(tag("("), complete::space0),
|
||||
separated_pair(
|
||||
complete::alpha1,
|
||||
pair(tag(","), complete::space1),
|
||||
complete::alpha1,
|
||||
),
|
||||
pair(complete::space0, tag(")")),
|
||||
)(input)?;
|
||||
let left = left.to_string();
|
||||
let right = right.to_string();
|
||||
Ok((input, Branches { left, right }))
|
||||
}
|
||||
|
||||
fn parse_nodes(input: &str) -> IResult<&str, (String, Branches)> {
|
||||
let (input, (node, branches)) = separated_pair(
|
||||
complete::alpha1,
|
||||
tuple((complete::space1, tag("="), complete::space1)),
|
||||
parse_branches,
|
||||
)(input)?;
|
||||
Ok((input, (node.to_string(), branches)))
|
||||
}
|
||||
|
||||
fn parse_node_tree(input: &str) -> IResult<&str, BTreeMap<String, Branches>> {
|
||||
let (input, map) = separated_list1(complete::line_ending, parse_nodes)(input)?;
|
||||
let map = map.into_iter().collect();
|
||||
Ok((input, map))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, (Vec<Direction>, BTreeMap<String, Branches>)> {
|
||||
let (input, x) =
|
||||
separated_pair(parse_directions, complete::line_ending, parse_node_tree)(input)?;
|
||||
Ok((input, x))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"RL
|
||||
|
||||
AAA = (BBB, CCC)
|
||||
BBB = (DDD, EEE)
|
||||
CCC = (ZZZ, GGG)
|
||||
DDD = (DDD, DDD)
|
||||
EEE = (EEE, EEE)
|
||||
GGG = (GGG, GGG)
|
||||
ZZZ = (ZZZ, ZZZ)",
|
||||
"2"
|
||||
)]
|
||||
#[case(
|
||||
"LLR
|
||||
|
||||
AAA = (BBB, BBB)
|
||||
BBB = (AAA, ZZZ)
|
||||
ZZZ = (ZZZ, ZZZ)",
|
||||
"6"
|
||||
)]
|
||||
|
||||
fn part1_works(#[case] input: &str, #[case] expected: &str) {
|
||||
let result = part1(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
160
2023/day-8/src/part2.rs
Normal file
160
2023/day-8/src/part2.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete,
|
||||
multi::{many1, separated_list1},
|
||||
sequence::{delimited, pair, separated_pair, tuple},
|
||||
IResult, Parser,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
struct Branches {
|
||||
pub left: String,
|
||||
pub right: String,
|
||||
}
|
||||
|
||||
impl Branches {
|
||||
fn choose(&self, direction: Direction) -> &str {
|
||||
match direction {
|
||||
Direction::Left => &self.left,
|
||||
Direction::Right => &self.right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// day 8 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, (steps, branches)) = parse_input(input).expect("aoc expects valid input");
|
||||
|
||||
let starting_node: Vec<&str> = branches
|
||||
.keys()
|
||||
.map(String::as_str)
|
||||
.filter(|x| x.ends_with('A'))
|
||||
.collect();
|
||||
|
||||
let cycles = starting_node
|
||||
.iter()
|
||||
.map(|node| {
|
||||
let mut visited_nodes = vec![*node];
|
||||
let mut current = *node;
|
||||
steps
|
||||
.iter()
|
||||
.cycle()
|
||||
.enumerate()
|
||||
.find_map(|(i, direction)| {
|
||||
let new = branches.get(current).expect("aoc1").choose(*direction);
|
||||
if new.ends_with('Z') {
|
||||
return Some(i + 1);
|
||||
}
|
||||
visited_nodes.push(new);
|
||||
dbg!(current = new);
|
||||
None
|
||||
})
|
||||
.expect("aoc4")
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
lcm(&cycles).to_string()
|
||||
}
|
||||
|
||||
fn lcm(nums: &[usize]) -> usize {
|
||||
if nums.len() == 1 {
|
||||
return nums[0];
|
||||
}
|
||||
let a = nums[0];
|
||||
let b = lcm(&nums[1..]);
|
||||
a * b / gcd(a, b)
|
||||
}
|
||||
|
||||
fn gcd(a: usize, b: usize) -> usize {
|
||||
if b == 0 {
|
||||
return a;
|
||||
}
|
||||
gcd(b, a % b)
|
||||
}
|
||||
|
||||
fn parse_directions(input: &str) -> IResult<&str, Vec<Direction>> {
|
||||
let (input, directions) = many1(alt((
|
||||
tag("L").map(|_| Direction::Left),
|
||||
tag("R").map(|_| Direction::Right),
|
||||
)))(input)?;
|
||||
let (input, _) = complete::line_ending(input)?;
|
||||
Ok((input, directions))
|
||||
}
|
||||
|
||||
fn parse_branches(input: &str) -> IResult<&str, Branches> {
|
||||
let (input, (left, right)) = delimited(
|
||||
pair(tag("("), complete::space0),
|
||||
separated_pair(
|
||||
complete::alphanumeric1,
|
||||
pair(tag(","), complete::space1),
|
||||
complete::alphanumeric1,
|
||||
),
|
||||
pair(complete::space0, tag(")")),
|
||||
)(input)?;
|
||||
let left = left.to_string();
|
||||
let right = right.to_string();
|
||||
Ok((input, Branches { left, right }))
|
||||
}
|
||||
|
||||
fn parse_nodes(input: &str) -> IResult<&str, (String, Branches)> {
|
||||
let (input, (node, branches)) = separated_pair(
|
||||
complete::alphanumeric1,
|
||||
tuple((complete::space1, tag("="), complete::space1)),
|
||||
parse_branches,
|
||||
)(input)?;
|
||||
Ok((input, (node.to_string(), branches)))
|
||||
}
|
||||
|
||||
fn parse_node_tree(input: &str) -> IResult<&str, BTreeMap<String, Branches>> {
|
||||
let (input, map) = separated_list1(complete::line_ending, parse_nodes)(input)?;
|
||||
let map = map.into_iter().collect();
|
||||
Ok((input, map))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, (Vec<Direction>, BTreeMap<String, Branches>)> {
|
||||
let (input, x) =
|
||||
separated_pair(parse_directions, complete::line_ending, parse_node_tree)(input)?;
|
||||
Ok((input, x))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"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)",
|
||||
"6"
|
||||
)]
|
||||
|
||||
fn part2_works(#[case] input: &str, #[case] expected: &str) {
|
||||
let result = part2(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
13
2023/day-9/Cargo.toml
Normal file
13
2023/day-9/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "day-9"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
rstest = {workspace = true}
|
||||
4
2023/day-9/src/lib.rs
Normal file
4
2023/day-9/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-9/src/main.rs
Normal file
12
2023/day-9/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_9::part1;
|
||||
use day_9::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
62
2023/day-9/src/part1.rs
Normal file
62
2023/day-9/src/part1.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{character::complete, multi::separated_list1, IResult};
|
||||
use std::{iter::successors, ops::Not};
|
||||
|
||||
/// day 9 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, report) = parse_input(input).expect("should have valid input for aoc");
|
||||
report.iter().map(|x| get_next(x)).sum::<i64>().to_string()
|
||||
}
|
||||
|
||||
fn get_next(array: &[i64]) -> i64 {
|
||||
let array = Vec::from(array);
|
||||
successors(Some(array), |a| {
|
||||
a.iter()
|
||||
.all(|x| x == &0)
|
||||
.not()
|
||||
.then_some(a.windows(2).map(|a| a[1] - a[0]).collect::<Vec<_>>())
|
||||
})
|
||||
.map(|x| *x.last().unwrap())
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Vec<i64>>> {
|
||||
separated_list1(
|
||||
complete::line_ending,
|
||||
separated_list1(complete::space1, complete::i64),
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(vec![0,3,6,9,12,15], 18)]
|
||||
#[case(vec![1,3,6,10,15,21], 28)]
|
||||
#[case(vec![10,13,16,21,30,45], 68)]
|
||||
fn part1_next(#[case] array: Vec<i64>, #[case] expected: i64) {
|
||||
assert_eq!(get_next(&array), expected);
|
||||
}
|
||||
|
||||
const INPUT: &str = "0 3 6 9 12 15
|
||||
1 3 6 10 15 21
|
||||
10 13 16 21 30 45";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "114".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
64
2023/day-9/src/part2.rs
Normal file
64
2023/day-9/src/part2.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use nom::{character::complete, multi::separated_list1, IResult};
|
||||
use std::{iter::successors, ops::Not};
|
||||
|
||||
/// day 9 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
/// usize
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, report) = parse_input(input).expect("should have valid input for aoc");
|
||||
report.iter().map(|x| get_next(x)).sum::<i64>().to_string()
|
||||
}
|
||||
|
||||
fn get_next(array: &[i64]) -> i64 {
|
||||
let array = Vec::from(array);
|
||||
let mut a = successors(Some(array.clone()), |a| {
|
||||
a.iter()
|
||||
.all(|x| x == &0)
|
||||
.not()
|
||||
.then_some(a.windows(2).map(|a| a[1] - a[0]).collect::<Vec<_>>())
|
||||
})
|
||||
.map(|x| *x.first().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
a.reverse();
|
||||
a.iter().fold(0, |acc, x| x - acc)
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Vec<i64>>> {
|
||||
separated_list1(
|
||||
complete::line_ending,
|
||||
separated_list1(complete::space1, complete::i64),
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(vec![0,3,6,9,12,15], -3)]
|
||||
#[case(vec![1,3,6,10,15,21], 0)]
|
||||
#[case(vec![10,13,16,21,30,45], 5)]
|
||||
fn part2_next(#[case] array: Vec<i64>, #[case] expected: i64) {
|
||||
assert_eq!(get_next(&array), expected);
|
||||
}
|
||||
|
||||
const INPUT: &str = "0 3 6 9 12 15
|
||||
1 3 6 10 15 21
|
||||
10 13 16 21 30 45";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "2".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
12
template/day-n/Cargo.toml
Normal file
12
template/day-n/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "{{project-name}}"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = { workspace = true }
|
||||
itertools = {workspace = true }
|
||||
0
template/day-n/src/input.txt
Normal file
0
template/day-n/src/input.txt
Normal file
4
template/day-n/src/lib.rs
Normal file
4
template/day-n/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
template/day-n/src/main.rs
Normal file
12
template/day-n/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use {{crate_name}}::part1;
|
||||
use {{crate_name}}::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
20
template/day-n/src/part1.rs
Normal file
20
template/day-n/src/part1.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
#[must_use]
|
||||
pub fn part1 (_input: &str) -> String {
|
||||
"Not Finished".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "Not Finished".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
20
template/day-n/src/part2.rs
Normal file
20
template/day-n/src/part2.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
#[must_use]
|
||||
pub fn part2 (_input: &str) -> String {
|
||||
"Not Finished".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "Not Finished".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user