Merge remote-tracking branch 'temp_merge/master'

This commit is contained in:
Dylan Thies
2023-12-12 09:02:46 -05:00
65 changed files with 4522 additions and 0 deletions

2
.gitignore vendored
View File

@@ -46,3 +46,5 @@ tags
# Persistent undo
[._]*.un~
.vscode/settings.json
input.txt

50
2023/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
pub mod part1;
pub mod part2;

12
2023/day-1/src/main.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 }

View File

View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

View 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}");
}

View 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());
}
}

View 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());
}
}