1
Fork 0

Big ugly commit, rewrite a bunch of stuff.

This commit is contained in:
Bauke 2022-10-02 22:20:09 +02:00
parent b039579f00
commit 639fe1a3b8
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
28 changed files with 1435 additions and 278 deletions

838
Cargo.lock generated
View File

@ -21,9 +21,75 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
name = "advent-of-code"
version = "0.0.0"
dependencies = [
"askama",
"clap",
"color-eyre",
"derivative",
"dialoguer",
"emojis",
"itertools",
"pathfinding",
"rand",
"ureq",
]
[[package]]
name = "askama"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
dependencies = [
"askama_derive",
"askama_escape",
"askama_shared",
]
[[package]]
name = "askama_derive"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
dependencies = [
"askama_shared",
"proc-macro2",
"syn",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "askama_shared"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
dependencies = [
"askama_escape",
"humansize",
"mime",
"mime_guess",
"nom",
"num-traits",
"percent-encoding",
"proc-macro2",
"quote",
"serde",
"syn",
"toml",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
@ -47,6 +113,24 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "cc"
version = "1.0.73"
@ -59,6 +143,49 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chunked_transfer"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "clap"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f78ad8e84aa8e8aa3e821857be40eb4b925ff232de430d4dd2ae6aa058cbd92"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "color-eyre"
version = "0.6.2"
@ -86,12 +213,97 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "console"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"terminal_size",
"unicode-width",
"winapi",
]
[[package]]
name = "cookie"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd"
dependencies = [
"cookie",
"idna 0.2.3",
"indexmap",
"log",
"publicsuffix",
"serde",
"serde_json",
"time",
"url",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dialoguer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
dependencies = [
"console",
"tempfile",
"zeroize",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "emojis"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdc0036e5881a30eec779c55ac7b4bf54eedc3c72ddc8d02c2a580c43b1c9ba"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "eyre"
version = "0.6.8"
@ -102,12 +314,51 @@ dependencies = [
"once_cell",
]
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.26.2"
@ -120,6 +371,48 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "humansize"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indenter"
version = "0.3.3"
@ -136,6 +429,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "integer-sqrt"
version = "0.1.5"
@ -154,6 +456,21 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -166,12 +483,49 @@ version = "0.2.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[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.5.4"
@ -181,6 +535,16 @@ dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -190,6 +554,15 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.29.0"
@ -205,6 +578,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "owo-colors"
version = "3.5.0"
@ -226,12 +605,48 @@ dependencies = [
"thiserror",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.46"
@ -241,6 +656,22 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "psl-types"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
[[package]]
name = "publicsuffix"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeeedb0b429dc462f30ad27ef3de97058b060016f47790c066757be38ef792b4"
dependencies = [
"idna 0.2.3",
"psl-types",
]
[[package]]
name = "quote"
version = "1.0.21"
@ -250,6 +681,69 @@ 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.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
@ -262,6 +756,65 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustls"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
dependencies = [
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -271,6 +824,18 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.101"
@ -282,6 +847,39 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "thiserror"
version = "1.0.37"
@ -311,6 +909,48 @@ dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa",
"libc",
"num_threads",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "tracing"
version = "0.1.36"
@ -353,14 +993,212 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "ureq"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
dependencies = [
"base64",
"chunked_transfer",
"cookie",
"cookie_store",
"flate2",
"log",
"once_cell",
"rustls",
"url",
"webpki",
"webpki-roots",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna 0.3.0",
"percent-encoding",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zeroize"
version = "1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"

View File

@ -15,6 +15,13 @@ name = "advent-of-code"
path = "source/main.rs"
[dependencies]
askama = "0.11.1"
clap = { version = "4.0.4", features = ["derive"] }
color-eyre = "0.6.2"
dialoguer = "0.10.2"
derivative = "2.2.0"
emojis = "0.4.0"
itertools = "0.10.5"
pathfinding = "3.0.13"
rand = "0.8.5"
ureq = { version = "2.5.0", features = ["cookie", "cookie_store"] }

2
askama.toml Normal file
View File

@ -0,0 +1,2 @@
[general]
dirs = ["source/templates"]

View File

@ -1,27 +0,0 @@
use std::str::FromStr;
use color_eyre::Result;
mod bingo;
mod board;
use bingo::Bingo;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_04.txt").trim();
println!("Day 04 Part 1: {}", part_1(input_data)?);
println!("Day 04 Part 2: {}", part_2(input_data)?);
Ok(())
}
fn part_1(input: &str) -> Result<i32> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_first_win();
Ok(winning_board.sum_unmarked() * latest_number)
}
fn part_2(input: &str) -> Result<i32> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_last_win();
Ok(winning_board.sum_unmarked() * latest_number)
}

View File

@ -1,54 +1,73 @@
use std::time::{Duration, Instant};
//! # Advent of Code
//!
//! > **Advent of Code solutions in Rust.**
use color_eyre::Result;
#![forbid(unsafe_code)]
mod day_01;
mod day_02;
mod day_03;
mod day_04;
mod day_05;
mod day_06;
mod day_07;
mod day_08;
mod day_09;
mod day_10;
mod day_13;
mod day_14;
mod day_15;
use std::{fs::read_to_string, path::PathBuf};
fn main() -> Result<()> {
use {askama::Template, clap::Parser, color_eyre::Result};
use crate::utilities::{get_solutions, session_cookie, write_file};
pub mod prelude;
pub mod solution;
pub mod templates;
pub mod utilities;
pub mod year_2021;
#[derive(Debug, Parser)]
#[clap(about, author, version)]
pub struct Args {
/// Filter to only a specific year and/or day, in `YEAR::DAY` format.
pub filter: Option<String>,
}
pub fn main() -> Result<()> {
color_eyre::install()?;
println!("Advent of Code 2021\n");
let mut runtimes = vec![];
let days: Vec<fn() -> Result<()>> = vec![
day_01::solve,
day_02::solve,
day_03::solve,
day_04::solve,
day_05::solve,
day_06::solve,
day_07::solve,
day_08::solve,
day_09::solve,
day_10::solve,
day_13::solve,
day_14::solve,
day_15::solve,
];
let args = Args::parse();
let filter = args.filter.unwrap_or_default();
let session_cookie = session_cookie()?;
let mut years = vec![];
for day in days {
let start = Instant::now();
day()?;
let runtime = Instant::now() - start;
runtimes.push(runtime);
println!("- Runtime: {:#?}\n", runtime);
for year in get_solutions() {
let mut solutions = vec![];
for solution in year {
if !solution.day.filter_string().starts_with(&filter) {
continue;
}
let year = solution.day.year;
let day = solution.day.day;
let data_path = PathBuf::from(format!("aoc/{year}/{day:02}.txt"));
let data = if !data_path.exists() {
println!("{data_path:?} doesn't exist, downloading");
std::thread::sleep(std::time::Duration::from_secs(1));
write_file(
data_path,
ureq::get(&format!(
"https://adventofcode.com/{year}/day/{day}/input"
))
.set("Cookie", &session_cookie)
.call()?
.into_string()?,
)?
} else {
read_to_string(data_path)?
};
solutions.push(solution.solve(data.trim())?);
}
years.append(&mut vec![solutions]);
}
println!(
"Total runtime: {:#?}",
runtimes.into_iter().sum::<Duration>()
);
write_file(
"aoc/solutions.html".into(),
templates::SolutionsTemplate { years }.render()?,
)?;
Ok(())
}

17
source/prelude.rs Normal file
View File

@ -0,0 +1,17 @@
//! Commonly used imports.
pub use std::{
collections::{HashMap, HashSet, VecDeque},
str::FromStr,
};
pub use {
color_eyre::{
eyre::{eyre, Error},
Result,
},
itertools::Itertools,
pathfinding::prelude::astar,
};
pub use crate::solution::{Day, Solution};

83
source/solution.rs Normal file
View File

@ -0,0 +1,83 @@
use {color_eyre::Result, derivative::Derivative};
#[derive(Debug)]
pub struct Day {
pub day: i32,
pub year: i32,
}
impl Day {
pub fn new(day: i32, year: i32) -> Self {
Self { day, year }
}
pub fn filter_string(&self) -> String {
format!("{}::{:02}", self.year, self.day)
}
pub fn day_link(&self) -> String {
format!("{}/day/{}", self.year_link(), self.day)
}
pub fn year_link(&self) -> String {
format!("https://adventofcode.com/{}", self.year)
}
}
pub type DayFunction = fn(input: &str) -> Result<String>;
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Solution {
pub day: Day,
pub part_1: String,
pub part_2: String,
pub part_1_expected: String,
pub part_2_expected: String,
#[derivative(Debug = "ignore")]
pub part_1_fn: DayFunction,
#[derivative(Debug = "ignore")]
pub part_2_fn: DayFunction,
}
impl Solution {
pub fn new(day: Day, part_1_fn: DayFunction, part_2_fn: DayFunction) -> Self {
Self {
day,
part_1: String::new(),
part_2: String::new(),
part_1_expected: String::new(),
part_2_expected: String::new(),
part_1_fn,
part_2_fn,
}
}
pub fn with_expected<A: std::fmt::Display, B: std::fmt::Display>(
self,
part_1_expected: A,
part_2_expected: B,
) -> Self {
Self {
part_1_expected: format!("{part_1_expected}"),
part_2_expected: format!("{part_2_expected}"),
..self
}
}
pub fn solve(self, data: &str) -> Result<Self> {
let (part_1, part_2) = ((self.part_1_fn)(data)?, (self.part_2_fn)(data)?);
Ok(Self {
part_1,
part_2,
..self
})
}
}

15
source/templates/mod.rs Normal file
View File

@ -0,0 +1,15 @@
use askama::Template;
use crate::solution::Solution;
#[derive(Template)]
#[template(path = "solutions.html")]
pub struct SolutionsTemplate {
pub years: Vec<Vec<Solution>>,
}
pub mod filters {
pub fn random_emoji(s: &str) -> askama::Result<String> {
Ok(format!("{s} {}", crate::utilities::random_emoji()))
}
}

View File

@ -0,0 +1,156 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advent of Code Solutions</title>
<link rel="stylesheet"
href="https://unpkg.com/modern-normalize@1.1.0/modern-normalize.css">
<style>
h1,
h2,
h3,
p,
pre {
margin: 0;
}
body {
--accent-1: #3395ff;
--accent-2: #495057;
--accent-3: #2bb34b;
--background-1: #212529;
--background-2: #131618;
--foreground-1: #f8f9fa;
--spacing-1: 4px;
--spacing-2: 8px;
background-color: var(--background-1);
color: var(--foreground-1);
}
a {
color: var(--accent-1);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.page-header,
.page-main {
margin: 0 auto;
width: 1200px;
}
.page-header {
margin-bottom: var(--spacing-2);
padding: var(--spacing-2);
text-align: center;
}
.year {
display: grid;
gap: var(--spacing-2);
grid-template-columns: repeat(5, 1fr);
padding: var(--spacing-2);
}
.year>.title {
border-bottom: 1px solid var(--accent-2);
grid-column: 1 / 6;
}
.day {
background-color: var(--background-2);
padding: var(--spacing-2);
}
.day>.title {
margin-bottom: var(--spacing-2);
}
.parts {
display: grid;
gap: var(--spacing-1);
grid-template-columns: repeat(1, 1fr);
}
.part {
border-left: 2px solid;
overflow: auto;
padding: var(--spacing-1);
}
.part.correct {
color: var(--accent-3);
}
</style>
</head>
<body>
<header class="page-header">
<h1>Advent of Code</h1>
</header>
<main class="page-main">
{% for year in years %}
<div class="year">
{% for solution in year %}
{% if loop.first %}
<h2 class="title">
<a href="{{ solution.day.year_link() }}">{{ solution.day.year }}</a>
</h2>
{% endif %}
<div class="day">
<h3 class="title">
<a href="{{ solution.day.day_link() }}">
Day {{ format!("{:02}", solution.day.day)|random_emoji }}
</a>
</h3>
<div class="parts">
{% let part_1_correct %}
{% if solution.part_1 == solution.part_1_expected %}
{% let part_1_correct = true %}
{% else %}
{% let part_1_correct = false %}
{% endif %}
{% if solution.part_1.contains("\n") %}
<details class="{% if part_1_correct %}correct{% endif %} part">
<summary>Click to expand.</summary>
<pre><code>{{ solution.part_1 }}</code></pre>
</details>
{% else %}
<pre class="{% if part_1_correct %}correct{% endif %} part"><code>{{ solution.part_1 }}</code></pre>
{% endif %}
{% let part_2_correct %}
{% if solution.part_2 == solution.part_2_expected %}
{% let part_2_correct = true %}
{% else %}
{% let part_2_correct = false %}
{% endif %}
{% if solution.part_2.contains("\n") %}
<details class="{% if part_2_correct %}correct{% endif %} part">
<summary>Click to expand.</summary>
<pre><code>{{ solution.part_2 }}</code></pre>
</details>
{% else %}
<pre class="{% if part_2_correct %}correct{% endif %} part"><code>{{ solution.part_2 }}</code></pre>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</main>
</body>
</html>

40
source/utilities.rs Normal file
View File

@ -0,0 +1,40 @@
use std::{
fs::{create_dir_all, read_to_string, write},
path::PathBuf,
};
use {color_eyre::Result, dialoguer::Password, rand::seq::IteratorRandom};
use crate::{solution::Solution, year_2021};
pub fn get_solutions() -> Vec<Vec<Solution>> {
vec![year_2021::get_solutions()]
}
pub fn random_emoji() -> String {
emojis::iter()
.filter(|emoji| emoji.group() == emojis::Group::AnimalsAndNature)
.choose(&mut rand::thread_rng())
.unwrap()
.to_string()
}
pub fn session_cookie() -> Result<String> {
let session_cookie_path = PathBuf::from("aoc/session.txt");
if session_cookie_path.exists() {
read_to_string(session_cookie_path).map_err(Into::into)
} else {
let session_cookie = Password::new()
.with_prompt("Advent of Code Session Cookie")
.interact()?;
write_file(session_cookie_path, session_cookie)
}
}
pub fn write_file(path: PathBuf, contents: String) -> Result<String> {
create_dir_all(path.parent().unwrap())?;
write(path, &contents)?;
Ok(contents)
}

View File

@ -1,30 +1,28 @@
use color_eyre::Result;
use itertools::Itertools;
use crate::prelude::*;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_01.txt").trim();
println!("Day 01 Part 1: {}", part_1(input_data)?);
println!("Day 01 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(1, 2021), part_1, part_2).with_expected(1451, 1395)
}
fn part_1(input: &str) -> Result<usize> {
fn part_1(input: &str) -> Result<String> {
Ok(
parse_measurements(input)?
.into_iter()
.tuple_windows()
.filter(|(previous, next)| next > previous)
.count(),
.count()
.to_string(),
)
}
fn part_2(input: &str) -> Result<usize> {
fn part_2(input: &str) -> Result<String> {
Ok(
parse_measurements(input)?
.into_iter()
.tuple_windows()
.filter(|(a, b, c, d)| b + c + d > a + b + c)
.count(),
.count()
.to_string(),
)
}

View File

@ -1,15 +1,8 @@
use std::str::FromStr;
use crate::prelude::*;
use color_eyre::{
eyre::{eyre, Error},
Result,
};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_02.txt").trim();
println!("Day 02 Part 1: {}", part_1(input_data)?);
println!("Day 02 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(2, 2021), part_1, part_2)
.with_expected(1727835, 1544000595)
}
#[derive(Debug)]
@ -23,13 +16,13 @@ impl FromStr for Command {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split = s.split(" ");
let mut split = s.split(' ');
let command = split
.next()
.ok_or(eyre!("Command not found in line: {}", s))?;
.ok_or_else(|| eyre!("Command not found in line: {}", s))?;
let amount = split
.next()
.ok_or(eyre!("Amount not found in line: {}", s))?
.ok_or_else(|| eyre!("Amount not found in line: {}", s))?
.parse()?;
match command {
@ -77,22 +70,22 @@ fn parse_commands(input: &str) -> Result<Vec<Command>> {
input.lines().map(Command::from_str).collect::<Result<_>>()
}
fn part_1(input: &str) -> Result<i32> {
fn part_1(input: &str) -> Result<String> {
let mut submarine = Submarine::default();
parse_commands(input)?
.into_iter()
.for_each(|command| submarine.execute_command_1(command));
Ok(submarine.final_result())
Ok(submarine.final_result().to_string())
}
fn part_2(input: &str) -> Result<i32> {
fn part_2(input: &str) -> Result<String> {
let mut submarine = Submarine::default();
parse_commands(input)?
.into_iter()
.for_each(|command| submarine.execute_command_2(command));
Ok(submarine.final_result())
Ok(submarine.final_result().to_string())
}

View File

@ -1,13 +1,8 @@
use std::collections::HashMap;
use crate::prelude::*;
use color_eyre::{eyre::eyre, Result};
use itertools::Itertools;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_03.txt").trim();
println!("Day 03 Part 1: {}", part_1(input_data)?);
println!("Day 03 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(3, 2021), part_1, part_2)
.with_expected(1092896, 4672151)
}
fn count_bits(input: &str) -> Result<Vec<(usize, i32)>> {
@ -33,7 +28,7 @@ fn count_bits(input: &str) -> Result<Vec<(usize, i32)>> {
)
}
fn part_1(input: &str) -> Result<i32> {
fn part_1(input: &str) -> Result<String> {
let bits = count_bits(input)?;
let gamma_rate = i32::from_str_radix(
@ -53,10 +48,10 @@ fn part_1(input: &str) -> Result<i32> {
2,
)?;
Ok(gamma_rate * epsilon_rate)
Ok((gamma_rate * epsilon_rate).to_string())
}
fn part_2(input: &str) -> Result<i32> {
fn part_2(input: &str) -> Result<String> {
let mut most_common_lines = input.lines().collect::<Vec<_>>();
let mut least_common_lines = input.lines().collect::<Vec<_>>();
@ -67,7 +62,7 @@ fn part_2(input: &str) -> Result<i32> {
if most_common_lines.len() > 1 {
let (index, bit) = most_common_bits
.get(index)
.ok_or(eyre!("Could not find most common bit"))?;
.ok_or_else(|| eyre!("Could not find most common bit"))?;
let most_common = if bit >= &0 { '1' } else { '0' };
most_common_lines = most_common_lines
.into_iter()
@ -79,7 +74,7 @@ fn part_2(input: &str) -> Result<i32> {
if least_common_lines.len() > 1 {
let (index, bit) = least_common_bits
.get(index)
.ok_or(eyre!("Could not find least common bit"))?;
.ok_or_else(|| eyre!("Could not find least common bit"))?;
let least_common = if bit < &0 { '1' } else { '0' };
least_common_lines = least_common_lines
.into_iter()
@ -92,15 +87,15 @@ fn part_2(input: &str) -> Result<i32> {
let oxygen_generator_rating = i32::from_str_radix(
most_common_lines
.first()
.ok_or(eyre!("Didn't find an oxygen generator rating"))?,
.ok_or_else(|| eyre!("Didn't find an oxygen generator rating"))?,
2,
)?;
let co2_scrubber_rating = i32::from_str_radix(
least_common_lines
.first()
.ok_or(eyre!("Didn't find a CO2 scrubber rating"))?,
.ok_or_else(|| eyre!("Didn't find a CO2 scrubber rating"))?,
2,
)?;
Ok(oxygen_generator_rating * co2_scrubber_rating)
Ok((oxygen_generator_rating * co2_scrubber_rating).to_string())
}

View File

@ -59,10 +59,10 @@ impl FromStr for Bingo {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let numbers = s
.split("\n")
.split('\n')
.next()
.ok_or(eyre!("Didn't find bingo numbers on the first line"))?
.split(",")
.ok_or_else(|| eyre!("Didn't find bingo numbers on the first line"))?
.split(',')
.map(str::parse)
.collect::<Result<_, _>>()?;

View File

@ -5,17 +5,17 @@ use color_eyre::eyre::Error;
/// A matrix of all the indexes that constitute a win.
const WIN_MATRIX: &[&[usize]] = &[
// Horizontal lines
&[00, 01, 02, 03, 04],
&[05, 06, 07, 08, 09],
&[0, 1, 2, 3, 4],
&[5, 6, 7, 8, 9],
&[10, 11, 12, 13, 14],
&[15, 16, 17, 18, 19],
&[20, 21, 22, 23, 24],
// Vertical lines
&[00, 05, 10, 15, 20],
&[01, 06, 11, 16, 21],
&[02, 07, 12, 17, 22],
&[03, 08, 13, 18, 23],
&[04, 09, 14, 19, 24],
&[0, 5, 10, 15, 20],
&[1, 6, 11, 16, 21],
&[2, 7, 12, 17, 22],
&[3, 8, 13, 18, 23],
&[4, 9, 14, 19, 24],
];
#[derive(Debug, Clone)]
@ -36,7 +36,7 @@ impl Board {
/// Check whether a board has won.
pub fn check(&self) -> bool {
for indexes in WIN_MATRIX {
if indexes.into_iter().all(|index| {
if indexes.iter().all(|index| {
*self
.state
.get(*index)
@ -78,8 +78,8 @@ impl FromStr for Board {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let numbers = s
.replace("\n", " ")
.split(" ")
.replace('\n', " ")
.split(' ')
.filter(|s| s != &"")
.map(str::parse)
.collect::<Result<_, _>>()?;

View File

@ -0,0 +1,22 @@
mod bingo;
mod board;
use bingo::Bingo;
use crate::prelude::*;
pub fn solution() -> Solution {
Solution::new(Day::new(4, 2021), part_1, part_2).with_expected(8580, 9576)
}
fn part_1(input: &str) -> Result<String> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_first_win();
Ok((winning_board.sum_unmarked() * latest_number).to_string())
}
fn part_2(input: &str) -> Result<String> {
let (winning_board, latest_number) =
Bingo::from_str(input)?.play_until_last_win();
Ok((winning_board.sum_unmarked() * latest_number).to_string())
}

View File

@ -1,15 +1,7 @@
use std::{collections::HashMap, str::FromStr};
use crate::prelude::*;
use color_eyre::{
eyre::{eyre, Error},
Result,
};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_05.txt").trim();
println!("Day 05 Part 1: {}", part_1(input_data)?);
println!("Day 05 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(5, 2021), part_1, part_2).with_expected(6687, 19851)
}
#[derive(Debug)]
@ -84,7 +76,7 @@ impl FromStr for Vector {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let numbers = s
.replace(" -> ", ",")
.split(",")
.split(',')
.map(str::parse)
.collect::<Result<Vec<_>, _>>()?;
@ -99,7 +91,7 @@ impl FromStr for Vector {
}
}
fn part_1(input: &str) -> Result<usize> {
fn part_1(input: &str) -> Result<String> {
let vectors = input
.lines()
.filter_map(|line| {
@ -127,10 +119,10 @@ fn part_1(input: &str) -> Result<usize> {
}
}
Ok(count_result(travelled_coordinates))
Ok(count_result(travelled_coordinates).to_string())
}
fn part_2(input: &str) -> Result<usize> {
fn part_2(input: &str) -> Result<String> {
let vectors = input
.lines()
.map(str::parse)
@ -152,7 +144,7 @@ fn part_2(input: &str) -> Result<usize> {
}
}
Ok(count_result(travelled_coordinates))
Ok(count_result(travelled_coordinates).to_string())
}
fn count_result(coordinates: HashMap<(isize, isize), isize>) -> usize {

View File

@ -1,40 +1,36 @@
use std::collections::HashMap;
use color_eyre::Result;
use crate::prelude::*;
type FishMap = HashMap<isize, isize>;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_06.txt").trim();
println!("Day 06 Part 1: {}", part_1(input_data)?);
println!("Day 06 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(6, 2021), part_1, part_2)
.with_expected(343441, 1569108373832_i64)
}
fn part_1(input: &str) -> Result<isize> {
fn part_1(input: &str) -> Result<String> {
let mut fishes = parse_fishes(input)?;
for _ in 0..80 {
fishes = simulate_fishes(fishes);
}
Ok(count_fishes(fishes))
Ok(count_fishes(fishes).to_string())
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
let mut fishes = parse_fishes(input)?;
for _ in 0..256 {
fishes = simulate_fishes(fishes);
}
Ok(count_fishes(fishes))
Ok(count_fishes(fishes).to_string())
}
fn parse_fishes(input: &str) -> Result<FishMap> {
let mut fishes = FishMap::new();
let individual_fishes = input
.split(",")
.split(',')
.map(str::parse)
.collect::<Result<Vec<_>, _>>()?;

View File

@ -1,40 +1,39 @@
use color_eyre::{eyre::eyre, Result};
use crate::prelude::*;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_07.txt").trim();
println!("Day 07 Part 1: {}", part_1(input_data)?);
println!("Day 07 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(7, 2021), part_1, part_2)
.with_expected(328318, 89791146)
}
fn parse_crabs(input: &str) -> Result<(Vec<isize>, isize)> {
let crabs = input
.split(",")
.split(',')
.map(str::parse)
.collect::<Result<Vec<_>, _>>()?;
let highest_crab = *crabs
.iter()
.max()
.ok_or(eyre!("Unable to find highest crab"))?;
.ok_or_else(|| eyre!("Unable to find highest crab"))?;
Ok((crabs, highest_crab))
}
fn part_1(input: &str) -> Result<isize> {
fn part_1(input: &str) -> Result<String> {
let (crabs, highest_crab) = parse_crabs(input)?;
(0..=highest_crab)
.map(|target_position| {
crabs
.iter()
.map(|crab| (target_position - crab).abs())
.sum()
.sum::<isize>()
})
.min()
.ok_or(eyre!("Unable to find lowest fuel"))
.map(|result| result.to_string())
.ok_or_else(|| eyre!("Unable to find lowest fuel"))
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
let (crabs, highest_crab) = parse_crabs(input)?;
(0..=highest_crab)
.map(|target_position| {
@ -42,8 +41,9 @@ fn part_2(input: &str) -> Result<isize> {
.iter()
.map(|crab| (target_position - crab).abs())
.map(|steps| (steps * (steps + 1)) / 2)
.sum()
.sum::<isize>()
})
.min()
.ok_or(eyre!("Unable to find lowest fuel"))
.map(|result| result.to_string())
.ok_or_else(|| eyre!("Unable to find lowest fuel"))
}

View File

@ -1,17 +1,14 @@
use color_eyre::{eyre::eyre, Result};
mod display;
use display::{CharSet, Display};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_08.txt").trim();
println!("Day 08 Part 1: {}", part_1(input_data)?);
println!("Day 08 Part 2: {}", part_2(input_data)?);
Ok(())
use crate::prelude::*;
pub fn solution() -> Solution {
Solution::new(Day::new(8, 2021), part_1, part_2).with_expected(521, 1016804)
}
fn part_1(input: &str) -> Result<usize> {
fn part_1(input: &str) -> Result<String> {
Ok(
input
.lines()
@ -19,17 +16,18 @@ fn part_1(input: &str) -> Result<usize> {
line
.split(" | ")
.nth(1)
.ok_or(eyre!("Invalid input: {}", line))
.ok_or_else(|| eyre!("Invalid input: {}", line))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.flat_map(|line| line.split(" ").map(Display::parse))
.flat_map(|line| line.split(' ').map(Display::parse))
.filter(|display| [2, 4, 3, 7].contains(&display.0))
.count(),
.count()
.to_string(),
)
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
let mut sum = 0;
for line in input.lines() {
@ -39,8 +37,8 @@ fn part_2(input: &str) -> Result<isize> {
let displays = Display::figure_out_from_others(
split
.next()
.ok_or(eyre!("Invalid input: {}", line))?
.split(" ")
.ok_or_else(|| eyre!("Invalid input: {}", line))?
.split(' ')
.map(Display::parse)
.collect::<Vec<_>>(),
)?;
@ -48,25 +46,24 @@ fn part_2(input: &str) -> Result<isize> {
// Get all the CharSets from the encoded side.
let encoded = split
.next()
.ok_or(eyre!("Invalid input: {}", line))?
.split(" ")
.ok_or_else(|| eyre!("Invalid input: {}", line))?
.split(' ')
.map(str::chars)
.map(CharSet::from_iter)
.collect::<Vec<_>>();
.map(CharSet::from_iter);
// Loop through the encoded numbers backwards so we can use the loop index
// to multiply it by 10 to the power of the index.
// So 123 would be (3 * 1) + (2 * 10) + (1 * 100).
for (index, set) in encoded.into_iter().rev().enumerate() {
for (index, set) in encoded.rev().enumerate() {
let decoded_number = displays
.iter()
.find(|display| display.1 == &set)
.map(|display| display.0)
.ok_or(eyre!("Impossible to decode {:?}", set))?;
.ok_or_else(|| eyre!("Impossible to decode {:?}", set))?;
sum += 10_isize.pow(index as u32) * decoded_number;
}
}
Ok(sum)
Ok(sum.to_string())
}

View File

@ -1,13 +1,7 @@
use std::collections::HashMap;
use crate::prelude::*;
use color_eyre::{eyre::eyre, Result};
use itertools::Itertools;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_09.txt").trim();
println!("Day 09 Part 1: {}", part_1(input_data)?);
println!("Day 09 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(9, 2021), part_1, part_2).with_expected(600, 987840)
}
type Coordinate = (isize, isize);
@ -15,10 +9,10 @@ type Coordinate = (isize, isize);
type HeightMap = HashMap<Coordinate, isize>;
const ADJACENT_OFFSETS: &[Coordinate] = &[
(-1, 00), // Left
(01, 00), // Right
(00, -1), // Up
(00, 01), // Down
(-1, 0), // Left
(1, 0), // Right
(0, -1), // Up
(0, 1), // Down
];
fn parse_heightmap(input: &str) -> Result<HeightMap> {
@ -28,7 +22,7 @@ fn parse_heightmap(input: &str) -> Result<HeightMap> {
for (x, height) in line.char_indices() {
let height = height
.to_digit(10)
.ok_or(eyre!("Invalid input: {}", line))?
.ok_or_else(|| eyre!("Invalid input: {}", line))?
.try_into()?;
height_map.insert((x.try_into()?, y.try_into()?), height);
@ -44,7 +38,7 @@ fn parse_map_bounds(input: &str) -> Result<Coordinate> {
.lines()
.next()
.map(|line| line.chars().count())
.ok_or(eyre!("Invalid input: {}", input))?
.ok_or_else(|| eyre!("Invalid input: {}", input))?
.try_into()?,
input.lines().count().try_into()?,
))
@ -80,7 +74,7 @@ fn discover_basins(
coordinates
}
fn part_1(input: &str) -> Result<isize> {
fn part_1(input: &str) -> Result<String> {
let height_map = parse_heightmap(input)?;
let map_bounds = parse_map_bounds(input)?;
let mut risk_level = 0;
@ -90,7 +84,7 @@ fn part_1(input: &str) -> Result<isize> {
let coordinate = (x, y);
let height = height_map
.get(&coordinate)
.ok_or(eyre!("Coordinate {:?} not found", coordinate))?;
.ok_or_else(|| eyre!("Coordinate {:?} not found", coordinate))?;
for (x_offset, y_offset) in ADJACENT_OFFSETS {
let neighbor = (x_offset + x, y_offset + y);
@ -106,10 +100,10 @@ fn part_1(input: &str) -> Result<isize> {
}
}
Ok(risk_level)
Ok(risk_level.to_string())
}
fn part_2(input: &str) -> Result<usize> {
fn part_2(input: &str) -> Result<String> {
let height_map = parse_heightmap(input)?;
let map_bounds = parse_map_bounds(input)?;
let mut basins = vec![];
@ -123,7 +117,7 @@ fn part_2(input: &str) -> Result<usize> {
let height = height_map
.get(&coordinate)
.ok_or(eyre!("Coordinate {:?} not found", coordinate))?;
.ok_or_else(|| eyre!("Coordinate {:?} not found", coordinate))?;
if height == &9 {
continue 'x_loop;
}
@ -145,6 +139,7 @@ fn part_2(input: &str) -> Result<usize> {
.into_iter()
.sorted_by(|a, b| b.cmp(a))
.take(3)
.product(),
.product::<usize>()
.to_string(),
)
}

View File

@ -1,15 +1,11 @@
use std::collections::VecDeque;
use crate::prelude::*;
use color_eyre::Result;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_10.txt").trim();
println!("Day 10 Part 1: {}", part_1(input_data)?);
println!("Day 10 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(10, 2021), part_1, part_2)
.with_expected(387363, 4330777059_i64)
}
fn part_1(input: &str) -> Result<usize> {
fn part_1(input: &str) -> Result<String> {
let mut syntax_error_score = 0;
for line in input.lines() {
@ -41,14 +37,14 @@ fn part_1(input: &str) -> Result<usize> {
}
}
Ok(syntax_error_score)
Ok(syntax_error_score.to_string())
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
let mut scores = vec![];
'line_loop: for line in input.lines() {
let mut line_score = 0;
let mut line_score: isize = 0;
let mut tokens = VecDeque::new();
for token in line.chars() {
@ -86,6 +82,6 @@ fn part_2(input: &str) -> Result<isize> {
scores.push(line_score);
}
scores.sort_by(|a, b| a.cmp(b));
Ok(scores[scores.len() / 2])
scores.sort();
Ok(scores[scores.len() / 2].to_string())
}

View File

@ -1,17 +1,20 @@
use std::collections::HashSet;
use color_eyre::{eyre::eyre, Result};
use itertools::Itertools;
mod canvas;
use canvas::{Canvas, Fold, Point};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_13.txt").trim();
println!("Day 13 Part 1: {}", part_1(input_data)?);
println!("Day 13 Part 2:\n{}", part_2(input_data)?);
Ok(())
use crate::prelude::*;
pub fn solution() -> Solution {
Solution::new(Day::new(13, 2021), part_1, part_2).with_expected(
"712",
" ███ █ █ █ ████ ██ ███ ██ ████
",
)
}
fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
@ -25,16 +28,16 @@ fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
let mut folds = vec![];
for line in input.lines() {
if line == "" {
if line.is_empty() {
in_folds_section = true;
continue;
}
if in_folds_section {
let mut split = line.split("=");
let mut split = line.split('=');
let is_x_axis = split
.next()
.map(|s| s.ends_with("x"))
.map(|s| s.ends_with('x'))
.ok_or_else(|| eyre!("Invalid line: {}", line))?;
let amount = split
@ -49,7 +52,7 @@ fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
}
} else {
let (x, y) = line
.split(",")
.split(',')
.tuples()
.next()
.ok_or_else(|| eyre!("Invalid line: {}", line))?;
@ -71,10 +74,10 @@ fn parse(input: &str) -> Result<(Canvas, Vec<Fold>)> {
Ok((canvas, folds))
}
fn part_1(input: &str) -> Result<usize> {
fn part_1(input: &str) -> Result<String> {
let (mut canvas, folds) = parse(input)?;
canvas = canvas.fold(&folds[0]);
Ok(canvas.points.into_iter().count())
Ok(canvas.points.len().to_string())
}
fn part_2(input: &str) -> Result<String> {

View File

@ -1,16 +1,11 @@
use std::collections::HashMap;
use color_eyre::{eyre::eyre, Result};
use itertools::Itertools;
use crate::prelude::*;
type PairMap = HashMap<(char, char), char>;
type PairCounts = HashMap<(char, char), isize>;
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_14.txt").trim();
println!("Day 14 Part 1: {}", part_1(input_data)?);
println!("Day 14 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(14, 2021), part_1, part_2)
.with_expected(3411, 7477815755570_i64)
}
fn parse(input: &str) -> Result<(String, PairMap)> {
@ -60,7 +55,7 @@ fn count_totals(counts: &PairCounts) -> HashMap<char, isize> {
totals
}
fn run(input: &str, steps: isize) -> Result<isize> {
fn run(input: &str, steps: isize) -> Result<String> {
let (template, pairs) = parse(input)?;
let mut counts = PairCounts::new();
@ -80,18 +75,15 @@ fn run(input: &str, steps: isize) -> Result<isize> {
.clone()
.next()
.ok_or_else(|| eyre!("No minimum found"))?;
let (_, max) = totals
.clone()
.last()
.ok_or_else(|| eyre!("No maximum found"))?;
let (_, max) = totals.last().ok_or_else(|| eyre!("No maximum found"))?;
Ok(max - min)
Ok((max - min).to_string())
}
fn part_1(input: &str) -> Result<isize> {
fn part_1(input: &str) -> Result<String> {
run(input, 10)
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
run(input, 40)
}

View File

@ -1,13 +1,7 @@
use std::collections::HashMap;
use crate::prelude::*;
use color_eyre::{eyre::eyre, Result};
use pathfinding::prelude::{absdiff, astar};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_15.txt").trim();
println!("Day 15 Part 1: {}", part_1(input_data)?);
println!("Day 15 Part 2: {}", part_2(input_data)?);
Ok(())
pub fn solution() -> Solution {
Solution::new(Day::new(15, 2021), part_1, part_2).with_expected(386, 2806)
}
type Grid = HashMap<Coordinate, isize>;
@ -16,8 +10,8 @@ type Grid = HashMap<Coordinate, isize>;
struct Coordinate(isize, isize);
impl Coordinate {
fn distance(&self, target: &Coordinate) -> isize {
absdiff(self.0, target.0) + absdiff(self.1, target.1)
fn distance(&self, target: &Coordinate) -> usize {
self.0.abs_diff(target.0) + self.1.abs_diff(target.1)
}
fn successors(&self, grid: &Grid) -> Vec<(Coordinate, isize)> {
@ -80,25 +74,26 @@ fn enlarge_grid(grid: Grid, end: Coordinate) -> (Grid, Coordinate) {
(larger_grid, Coordinate(width * 5, height * 5))
}
fn run(grid: Grid, end: Coordinate) -> Result<isize> {
fn run(grid: Grid, end: Coordinate) -> Result<String> {
Ok(
astar(
&Coordinate(1, 1),
|p| p.successors(&grid),
|p| p.distance(&end),
|p| p.distance(&end).try_into().unwrap(),
|p| p == &end,
)
.ok_or_else(|| eyre!("No path found"))?
.1,
.1
.to_string(),
)
}
fn part_1(input: &str) -> Result<isize> {
fn part_1(input: &str) -> Result<String> {
let (grid, end) = parse(input)?;
run(grid, end)
}
fn part_2(input: &str) -> Result<isize> {
fn part_2(input: &str) -> Result<String> {
let (grid, end) = parse(input)?;
let (grid, end) = enlarge_grid(grid, end);
run(grid, end)

33
source/year_2021/mod.rs Normal file
View File

@ -0,0 +1,33 @@
use crate::solution::Solution;
mod day_01;
mod day_02;
mod day_03;
mod day_04;
mod day_05;
mod day_06;
mod day_07;
mod day_08;
mod day_09;
mod day_10;
mod day_13;
mod day_14;
mod day_15;
pub fn get_solutions() -> Vec<Solution> {
vec![
day_01::solution(),
day_02::solution(),
day_03::solution(),
day_04::solution(),
day_05::solution(),
day_06::solution(),
day_07::solution(),
day_08::solution(),
day_09::solution(),
day_10::solution(),
day_13::solution(),
day_14::solution(),
day_15::solution(),
]
}