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
|
# Persistent undo
|
||||||
[._]*.un~
|
[._]*.un~
|
||||||
.vscode/settings.json
|
.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