From 54f03d6f71099d7c42625abfdaa2bb3f96a594b2 Mon Sep 17 00:00:00 2001 From: Patrick Stevens Date: Fri, 14 May 2021 23:14:43 +0000 Subject: [PATCH] Move up to day 11 to the lib/main format, and benchmark day 5 (#3) --- Cargo.lock | 620 ++++++++++++++++++++++++++++++++++ day_1/src/lib.rs | 77 +++++ day_1/src/main.rs | 81 +---- day_10/src/lib.rs | 134 ++++++++ day_10/src/main.rs | 140 +------- day_11/src/lib.rs | 138 ++++++++ day_11/src/main.rs | 142 +------- day_2/src/lib.rs | 139 ++++++++ day_2/src/main.rs | 143 +------- day_3/src/lib.rs | 287 ++++++++++++++++ day_3/src/main.rs | 291 +--------------- day_4/src/lib.rs | 85 +++++ day_4/src/main.rs | 89 +---- day_5/Cargo.toml | 6 + day_5/benches/day_5_part_2.rs | 15 + day_5/src/lib.rs | 73 ++++ day_5/src/main.rs | 78 +---- day_6/src/lib.rs | 103 ++++++ day_6/src/main.rs | 108 +----- day_7/src/lib.rs | 370 ++++++++++++++++++++ day_7/src/main.rs | 371 +------------------- day_8/src/lib.rs | 148 ++++++++ day_8/src/main.rs | 152 +-------- 23 files changed, 2236 insertions(+), 1554 deletions(-) create mode 100644 day_1/src/lib.rs create mode 100644 day_10/src/lib.rs create mode 100644 day_11/src/lib.rs create mode 100644 day_2/src/lib.rs create mode 100644 day_3/src/lib.rs create mode 100644 day_4/src/lib.rs create mode 100644 day_5/benches/day_5_part_2.rs create mode 100644 day_5/src/lib.rs create mode 100644 day_6/src/lib.rs create mode 100644 day_7/src/lib.rs create mode 100644 day_8/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4fa1668..1f297fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,181 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[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]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bstr" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.10.0", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" +dependencies = [ + "cast", + "itertools 0.9.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "day_1" version = "0.1.0" @@ -59,6 +235,9 @@ version = "0.1.0" [[package]] name = "day_5" version = "0.1.0" +dependencies = [ + "criterion", +] [[package]] name = "day_6" @@ -71,3 +250,444 @@ version = "0.1.0" [[package]] name = "day_8" version = "0.1.0" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[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.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "memoffset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" + +[[package]] +name = "plotters-svg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" + +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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" diff --git a/day_1/src/lib.rs b/day_1/src/lib.rs new file mode 100644 index 0000000..4b81c40 --- /dev/null +++ b/day_1/src/lib.rs @@ -0,0 +1,77 @@ +pub mod day_1 { + + pub fn input() -> Vec { + let input = include_str!("../input.txt"); + input + .trim() + .chars() + .map(|l| { + l.to_digit(10) + .unwrap_or_else(|| panic!("{} wasn't a valid u32", l)) + }) + .collect::>() + } + + pub fn part_1(numbers: &[u32]) -> u32 { + let mut sum = 0; + let mut previous = numbers[0]; + let len = numbers.len(); + for &item in numbers.iter().skip(1) { + if item == previous { + sum += previous; + } + previous = item; + } + if len <= 1 { + // Start = end, so no need to compare last with first + return sum; + } + + if previous == numbers[0] { + sum += previous; + } + + sum + } + + pub fn part_2(numbers: &[u32]) -> u32 { + let mut sum = 0; + let len = numbers.len(); + for i in 0..len / 2 { + if numbers[i] == numbers[len / 2 + i] { + sum += 2 * numbers[i]; + } + } + + sum + } +} + +#[cfg(test)] +mod tests { + use super::day_1::*; + + #[test] + fn part1_known() { + assert_eq!(part_1(&[1, 1, 2, 2]), 3); + assert_eq!(part_1(&[1, 1, 1, 1]), 4); + assert_eq!(part_1(&[1, 2, 3, 4]), 0); + assert_eq!(part_1(&[9, 1, 2, 1, 2, 1, 2, 9]), 9); + } + + #[test] + fn part2_known() { + assert_eq!(part_2(&[1, 2, 1, 2]), 6); + assert_eq!(part_2(&[1, 2, 2, 1]), 0); + assert_eq!(part_2(&[1, 2, 3, 4, 2, 5]), 4); + assert_eq!(part_2(&[1, 2, 3, 1, 2, 3]), 12); + assert_eq!(part_2(&[1, 2, 1, 3, 1, 4, 1, 5]), 4); + } + + #[test] + fn test_day_1() { + let input = input(); + assert_eq!(part_1(&input), 1223); + assert_eq!(part_2(&input), 1284); + } +} diff --git a/day_1/src/main.rs b/day_1/src/main.rs index 496f469..5c92cfb 100644 --- a/day_1/src/main.rs +++ b/day_1/src/main.rs @@ -1,80 +1,7 @@ -fn input() -> Vec { - let input = include_str!("../input.txt"); - input - .trim() - .chars() - .map(|l| { - l.to_digit(10) - .unwrap_or_else(|| panic!("{} wasn't a valid u32", l)) - }) - .collect::>() -} - -pub fn part_1(numbers: &[u32]) -> u32 { - let mut sum = 0; - let mut previous = numbers[0]; - let len = numbers.len(); - for &item in numbers.iter().skip(1) { - if item == previous { - sum += previous; - } - previous = item; - } - if len <= 1 { - // Start = end, so no need to compare last with first - return sum; - } - - if previous == numbers[0] { - sum += previous; - } - - sum -} - -pub fn part_2(numbers: &[u32]) -> u32 { - let mut sum = 0; - let len = numbers.len(); - for i in 0..len / 2 { - if numbers[i] == numbers[len / 2 + i] { - sum += 2 * numbers[i]; - } - } - - sum -} +use day_1::day_1; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&input)); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!(part_1(&[1, 1, 2, 2]), 3); - assert_eq!(part_1(&[1, 1, 1, 1]), 4); - assert_eq!(part_1(&[1, 2, 3, 4]), 0); - assert_eq!(part_1(&[9, 1, 2, 1, 2, 1, 2, 9]), 9); - } - - #[test] - fn part2_known() { - assert_eq!(part_2(&[1, 2, 1, 2]), 6); - assert_eq!(part_2(&[1, 2, 2, 1]), 0); - assert_eq!(part_2(&[1, 2, 3, 4, 2, 5]), 4); - assert_eq!(part_2(&[1, 2, 3, 1, 2, 3]), 12); - assert_eq!(part_2(&[1, 2, 1, 3, 1, 4, 1, 5]), 4); - } - - #[test] - fn test_day_1() { - let input = input(); - assert_eq!(part_1(&input), 1223); - assert_eq!(part_2(&input), 1284); - } + let input = day_1::input(); + println!("part 1 => {}", day_1::part_1(&input)); + println!("part 2 => {}", day_1::part_2(&input)); } diff --git a/day_10/src/lib.rs b/day_10/src/lib.rs new file mode 100644 index 0000000..5dc7093 --- /dev/null +++ b/day_10/src/lib.rs @@ -0,0 +1,134 @@ +pub mod day_10 { + + pub fn input_1() -> Vec { + let input = include_str!("../input.txt"); + input + .trim() + .split(',') + .map(|l| l.parse().unwrap()) + .collect::>() + } + + pub fn input_2() -> Vec { + let input = include_str!("../input.txt"); + input.trim().chars().map(|c| c as u8).collect::>() + } + + fn rev(start: usize, length: usize, b: &mut [T]) + where + T: Copy, + { + for j in 0..(length / 2) { + let tmp = b[(start + j) % b.len()]; + b[(start + j) % b.len()] = b[(start + length - j - 1) % b.len()]; + b[(start + length - j - 1) % b.len()] = tmp; + } + } + + pub(crate) struct HashState { + pub(crate) v: Vec, + pub(crate) curr_pos: usize, + pub(crate) skip_size: usize, + } + + pub(crate) fn new_state(size: usize) -> HashState { + HashState { + v: (0..size).map(|i| i as u8).collect(), + curr_pos: 0, + skip_size: 0, + } + } + + pub(crate) fn execute_round(state: &mut HashState, input: &[u8]) { + for &i in input { + let i = i as usize; + rev(state.curr_pos, i, &mut state.v); + state.curr_pos = (state.curr_pos + i + state.skip_size) % state.v.len(); + state.skip_size += 1; + } + } + + pub fn part_1(size: usize, input: &[u8]) -> u32 { + let mut state = new_state(size); + execute_round(&mut state, input); + state.v[0] as u32 * state.v[1] as u32 + } + + fn densify(v: &[u8]) -> Vec { + v.chunks_exact(16) + .map(|i| i.iter().fold(0, |x, y| x ^ y)) + .collect() + } + + // Convert a number from 0 to 15 into an ASCII hex char + fn to_hex(i: u8) -> u8 { + if i < 10 { + i + b'0' + } else { + i - 10 + b'a' + } + } + + pub fn knot_hash_unsalted(bytes: &[u8]) -> String { + let mut state = new_state(256); + for _ in 0..64 { + execute_round(&mut state, &bytes); + } + let dense = densify(&state.v); + let mut answer = vec![0u8; 2 * dense.len()]; + for (i, b) in dense.iter().enumerate() { + answer[2 * i] = to_hex(b / 16); + answer[2 * i + 1] = to_hex(b % 16); + } + String::from_utf8(answer).unwrap() + } + + pub fn knot_hash(bytes: &[u8]) -> String { + let mut copy: Vec = bytes.to_vec(); + copy.extend(vec![17, 31, 73, 47, 23]); + knot_hash_unsalted(©) + } + + pub fn part_2(input: &[u8]) -> String { + knot_hash(input) + } +} + +#[cfg(test)] +mod tests { + use super::day_10::*; + + #[test] + fn part1_known() { + let mut state = new_state(5); + execute_round(&mut state, &[3, 4, 1, 5]); + assert_eq!(state.v, vec![3, 4, 2, 1, 0]); + assert_eq!(state.skip_size, 4); + assert_eq!(state.curr_pos, 4); + } + + #[test] + fn part2_known() { + assert_eq!(knot_hash("".as_bytes()), "a2582a3a0e66e6e86e3812dcb672a272"); + assert_eq!( + knot_hash("AoC 2017".as_bytes()), + "33efeb34ea91902bb2f59c9920caa6cd" + ); + assert_eq!( + knot_hash("1,2,3".as_bytes()), + "3efbe78a8d82f29979031a4aa0b16a9d" + ); + assert_eq!( + knot_hash("1,2,4".as_bytes()), + "63960835bcdc130f0b66d7ff4f6a5a8e" + ); + } + + #[test] + fn test_day_10() { + let input = input_1(); + assert_eq!(part_1(256, &input), 4114); + let input = input_2(); + assert_eq!(part_2(&input), "2f8c3d2100fdd57cec130d928b0fd2dd"); + } +} diff --git a/day_10/src/main.rs b/day_10/src/main.rs index 5d8c3b5..8a5da0c 100644 --- a/day_10/src/main.rs +++ b/day_10/src/main.rs @@ -1,140 +1,10 @@ -fn input_1() -> Vec { - let input = include_str!("../input.txt"); - input - .trim() - .split(',') - .map(|l| l.parse().unwrap()) - .collect::>() -} - -fn input_2() -> Vec { - let input = include_str!("../input.txt"); - input.trim().chars().map(|c| c as u8).collect::>() -} - -fn rev(start: usize, length: usize, b: &mut [T]) -where - T: Copy, -{ - for j in 0..(length / 2) { - let tmp = b[(start + j) % b.len()]; - b[(start + j) % b.len()] = b[(start + length - j - 1) % b.len()]; - b[(start + length - j - 1) % b.len()] = tmp; - } -} - -struct HashState { - v: Vec, - curr_pos: usize, - skip_size: usize, -} - -fn new_state(size: usize) -> HashState { - HashState { - v: (0..size).map(|i| i as u8).collect(), - curr_pos: 0, - skip_size: 0, - } -} - -fn execute_round(state: &mut HashState, input: &[u8]) { - for &i in input { - let i = i as usize; - rev(state.curr_pos, i, &mut state.v); - state.curr_pos = (state.curr_pos + i + state.skip_size) % state.v.len(); - state.skip_size += 1; - } -} - -fn part_1(size: usize, input: &[u8]) -> u32 { - let mut state = new_state(size); - execute_round(&mut state, input); - state.v[0] as u32 * state.v[1] as u32 -} - -fn densify(v: &[u8]) -> Vec { - v.chunks_exact(16) - .map(|i| i.iter().fold(0, |x, y| x ^ y)) - .collect() -} - -// Convert a number from 0 to 15 into an ASCII hex char -fn to_hex(i: u8) -> u8 { - if i < 10 { - i + b'0' - } else { - i - 10 + b'a' - } -} - -pub fn knot_hash_unsalted(bytes: &[u8]) -> String { - let mut state = new_state(256); - for _ in 0..64 { - execute_round(&mut state, &bytes); - } - let dense = densify(&state.v); - let mut answer = vec![0u8; 2 * dense.len()]; - for (i, b) in dense.iter().enumerate() { - answer[2 * i] = to_hex(b / 16); - answer[2 * i + 1] = to_hex(b % 16); - } - String::from_utf8(answer).unwrap() -} - -pub fn knot_hash(bytes: &[u8]) -> String { - let mut copy: Vec = bytes.to_vec(); - copy.extend(vec![17, 31, 73, 47, 23]); - knot_hash_unsalted(©) -} - -fn part_2(input: &[u8]) -> String { - knot_hash(input) -} +use day_10::day_10; fn main() { - let input = input_1(); - println!("part 1 => {}", part_1(256, &input)); + let input = day_10::input_1(); + println!("part 1 => {}", day_10::part_1(256, &input)); - let mut input = input_2(); + let mut input = day_10::input_2(); input.extend(vec![17, 31, 73, 47, 23]); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - let mut state = new_state(5); - execute_round(&mut state, &[3, 4, 1, 5]); - assert_eq!(state.v, vec![3, 4, 2, 1, 0]); - assert_eq!(state.skip_size, 4); - assert_eq!(state.curr_pos, 4); - } - - #[test] - fn part2_known() { - assert_eq!(knot_hash("".as_bytes()), "a2582a3a0e66e6e86e3812dcb672a272"); - assert_eq!( - knot_hash("AoC 2017".as_bytes()), - "33efeb34ea91902bb2f59c9920caa6cd" - ); - assert_eq!( - knot_hash("1,2,3".as_bytes()), - "3efbe78a8d82f29979031a4aa0b16a9d" - ); - assert_eq!( - knot_hash("1,2,4".as_bytes()), - "63960835bcdc130f0b66d7ff4f6a5a8e" - ); - } - - #[test] - fn test_day_10() { - let input = input_1(); - assert_eq!(part_1(256, &input), 4114); - let input = input_2(); - assert_eq!(part_2(&input), "2f8c3d2100fdd57cec130d928b0fd2dd"); - } + println!("part 2 => {}", day_10::part_2(&input)); } diff --git a/day_11/src/lib.rs b/day_11/src/lib.rs new file mode 100644 index 0000000..4433878 --- /dev/null +++ b/day_11/src/lib.rs @@ -0,0 +1,138 @@ +pub mod day_11 { + pub enum Direction { + South, + North, + NorthEast, + NorthWest, + SouthEast, + SouthWest, + } + + fn parse(s: &str) -> Direction { + let mut chars = s.chars(); + let c1 = chars.next().unwrap(); + match chars.next() { + None => match c1 { + 's' => Direction::South, + 'n' => Direction::North, + c1 => panic!("Expected south or north, got: {}", c1), + }, + Some(c2) => match c1 { + 's' => match c2 { + 'e' => Direction::SouthEast, + 'w' => Direction::SouthWest, + c2 => panic!("Expected SE or SW, got s{}", c2), + }, + 'n' => match c2 { + 'e' => Direction::NorthEast, + 'w' => Direction::NorthWest, + c2 => panic!("Expected NE or NW, got n{}", c2), + }, + c1 => panic!("Expected north or south for first direction, got: {}", c1), + }, + } + } + + pub fn input() -> Vec { + let input = include_str!("../input.txt"); + input + .trim() + .split(',') + .map(parse) + .collect::>() + } + + fn abs(i: i32) -> u32 { + if i >= 0 { + i as u32 + } else { + -i as u32 + } + } + + fn steps_to(x: i32, y: i32) -> u32 { + let x = abs(x); + let y = abs(y); + y + if 2 * y < x { (x - y) / 2 } else { 0 } + } + + pub fn part_1(steps: &[Direction]) -> u32 { + let (final_x, final_y) = steps.iter().fold((0, 0), |(x, y), dir| match *dir { + Direction::SouthEast => (x - 1, y + 1), + Direction::NorthEast => (x + 1, y + 1), + Direction::SouthWest => (x - 1, y - 1), + Direction::NorthWest => (x + 1, y - 1), + Direction::South => (x - 2, y), + Direction::North => (x + 2, y), + }); + steps_to(final_x, final_y) + } + + pub fn part_2(steps: &[Direction]) -> u32 { + let (best, _, _) = steps.iter().fold((0, 0, 0), |(best, x, y), dir| { + let (new_x, new_y) = match *dir { + Direction::SouthEast => (x - 1, y + 1), + Direction::NorthEast => (x + 1, y + 1), + Direction::SouthWest => (x - 1, y - 1), + Direction::NorthWest => (x + 1, y - 1), + Direction::South => (x - 2, y), + Direction::North => (x + 2, y), + }; + let new_steps = steps_to(new_x, new_y); + (std::cmp::max(new_steps, best), new_x, new_y) + }); + best + } +} + +#[cfg(test)] +mod tests { + use super::day_11::*; + + #[test] + fn part1_known() { + assert_eq!( + part_1(&[ + Direction::NorthEast, + Direction::NorthEast, + Direction::NorthEast + ]), + 3 + ); + assert_eq!( + part_1(&[ + Direction::NorthEast, + Direction::NorthEast, + Direction::SouthWest, + Direction::SouthWest + ]), + 0 + ); + assert_eq!( + part_1(&[ + Direction::NorthEast, + Direction::NorthEast, + Direction::South, + Direction::South + ]), + 2 + ); + assert_eq!( + part_1(&[ + Direction::SouthEast, + Direction::SouthWest, + Direction::SouthEast, + Direction::SouthWest, + Direction::SouthWest + ]), + 3 + ); + } + + #[test] + fn test_day_11() { + let input = input(); + assert_eq!(part_1(&input), 743); + assert_eq!(part_2(&input), 1493); + } +} diff --git a/day_11/src/main.rs b/day_11/src/main.rs index f0b9d38..603c994 100644 --- a/day_11/src/main.rs +++ b/day_11/src/main.rs @@ -1,141 +1,7 @@ -pub enum Direction { - South, - North, - NorthEast, - NorthWest, - SouthEast, - SouthWest, -} - -fn parse(s: &str) -> Direction { - let mut chars = s.chars(); - let c1 = chars.next().unwrap(); - match chars.next() { - None => match c1 { - 's' => Direction::South, - 'n' => Direction::North, - c1 => panic!("Expected south or north, got: {}", c1), - }, - Some(c2) => match c1 { - 's' => match c2 { - 'e' => Direction::SouthEast, - 'w' => Direction::SouthWest, - c2 => panic!("Expected SE or SW, got s{}", c2), - }, - 'n' => match c2 { - 'e' => Direction::NorthEast, - 'w' => Direction::NorthWest, - c2 => panic!("Expected NE or NW, got n{}", c2), - }, - c1 => panic!("Expected north or south for first direction, got: {}", c1), - }, - } -} - -fn input() -> Vec { - let input = include_str!("../input.txt"); - input - .trim() - .split(',') - .map(parse) - .collect::>() -} - -fn abs(i: i32) -> u32 { - if i >= 0 { - i as u32 - } else { - -i as u32 - } -} - -fn steps_to(x: i32, y: i32) -> u32 { - let x = abs(x); - let y = abs(y); - y + if 2 * y < x { (x - y) / 2 } else { 0 } -} - -pub fn part_1(steps: &[Direction]) -> u32 { - let (final_x, final_y) = steps.iter().fold((0, 0), |(x, y), dir| match *dir { - Direction::SouthEast => (x - 1, y + 1), - Direction::NorthEast => (x + 1, y + 1), - Direction::SouthWest => (x - 1, y - 1), - Direction::NorthWest => (x + 1, y - 1), - Direction::South => (x - 2, y), - Direction::North => (x + 2, y), - }); - steps_to(final_x, final_y) -} - -pub fn part_2(steps: &[Direction]) -> u32 { - let (best, _, _) = steps.iter().fold((0, 0, 0), |(best, x, y), dir| { - let (new_x, new_y) = match *dir { - Direction::SouthEast => (x - 1, y + 1), - Direction::NorthEast => (x + 1, y + 1), - Direction::SouthWest => (x - 1, y - 1), - Direction::NorthWest => (x + 1, y - 1), - Direction::South => (x - 2, y), - Direction::North => (x + 2, y), - }; - let new_steps = steps_to(new_x, new_y); - (std::cmp::max(new_steps, best), new_x, new_y) - }); - best -} +use day_11::day_11; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&input)); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!( - part_1(&[ - Direction::NorthEast, - Direction::NorthEast, - Direction::NorthEast - ]), - 3 - ); - assert_eq!( - part_1(&[ - Direction::NorthEast, - Direction::NorthEast, - Direction::SouthWest, - Direction::SouthWest - ]), - 0 - ); - assert_eq!( - part_1(&[ - Direction::NorthEast, - Direction::NorthEast, - Direction::South, - Direction::South - ]), - 2 - ); - assert_eq!( - part_1(&[ - Direction::SouthEast, - Direction::SouthWest, - Direction::SouthEast, - Direction::SouthWest, - Direction::SouthWest - ]), - 3 - ); - } - - #[test] - fn test_day_1() { - let input = input(); - assert_eq!(part_1(&input), 743); - } + let input = day_11::input(); + println!("part 1 => {}", day_11::part_1(&input)); + println!("part 2 => {}", day_11::part_2(&input)); } diff --git a/day_2/src/lib.rs b/day_2/src/lib.rs new file mode 100644 index 0000000..24a33e4 --- /dev/null +++ b/day_2/src/lib.rs @@ -0,0 +1,139 @@ +pub mod day_2 { + + use std::cmp::Ordering; + use std::collections::HashSet; + + pub fn input() -> Vec> { + let input = include_str!("../input.txt"); + input + .lines() + .map(|l| { + l.trim() + .split_whitespace() + .map(|i| { + i.parse() + .unwrap_or_else(|_| panic!("{} wasn't a valid u32", i)) + }) + .collect::>() + }) + .collect::>>() + } + + fn min_max(i: &mut I) -> Option<(T, T)> + where + I: Iterator, + T: Copy + Ord, + { + if let Some(fst) = i.next() { + let mut top = fst; + let mut bot = fst; + for next in i { + if next > top { + top = next; + } + if next < bot { + bot = next; + } + } + Some((bot, top)) + } else { + None + } + } + + fn even_divisor(iter: &mut I) -> Option<(u32, u32)> + where + I: Iterator, + { + let mut seen: HashSet = HashSet::new(); + for i in iter { + for s in &seen { + let s = *s; + match u32::cmp(&s, &i) { + Ordering::Less => { + if i % s == 0 { + return Some((i, s)); + }; + } + Ordering::Greater => { + if s % i == 0 { + return Some((s, i)); + }; + } + Ordering::Equal => { + return Some((i, s)); + } + } + } + seen.insert(i); + } + None + } + + pub fn part_1(numbers: &mut I) -> u32 + where + I: Iterator, + J: Iterator, + { + numbers + .map(|mut row| { + if let Some((min, max)) = min_max(&mut row) { + max - min + } else { + 0 + } + }) + .sum() + } + + pub fn part_2(numbers: &mut I) -> u32 + where + I: Iterator, + J: Iterator, + { + numbers + .map(|mut row| { + let (bigger, smaller) = even_divisor(&mut row).unwrap(); + bigger / smaller + }) + .sum() + } +} + +#[cfg(test)] +mod tests { + use super::day_2::*; + + #[test] + fn part1_known() { + assert_eq!( + part_1( + &mut vec![vec![5, 1, 9, 5], vec![7, 5, 3], vec![2, 4, 6, 8]] + .iter() + .map(|r| r.iter().cloned()) + ), + 18 + ); + } + + #[test] + fn part2_known() { + assert_eq!( + part_2( + &mut vec![vec![5, 9, 2, 8], vec![9, 4, 7, 3], vec![3, 8, 6, 5]] + .iter() + .map(|r| r.iter().cloned()) + ), + 9 + ); + } + + #[test] + fn test_day_2() { + let input = input(); + let answer = part_1(&mut input.iter().map(|r| r.iter().cloned())); + assert_eq!(answer, 44887); + let answer = part_2(&mut input.iter().map(|r| r.iter().cloned())); + assert_eq!(answer, 242); + } +} diff --git a/day_2/src/main.rs b/day_2/src/main.rs index 42c047c..e670d6c 100644 --- a/day_2/src/main.rs +++ b/day_2/src/main.rs @@ -1,148 +1,13 @@ -use std::cmp::Ordering; -use std::collections::HashSet; - -fn input() -> Vec> { - let input = include_str!("../input.txt"); - input - .lines() - .map(|l| { - l.trim() - .split_whitespace() - .map(|i| { - i.parse() - .unwrap_or_else(|_| panic!("{} wasn't a valid u32", i)) - }) - .collect::>() - }) - .collect::>>() -} - -fn min_max(i: &mut I) -> Option<(T, T)> -where - I: Iterator, - T: Copy + Ord, -{ - if let Some(fst) = i.next() { - let mut top = fst; - let mut bot = fst; - for next in i { - if next > top { - top = next; - } - if next < bot { - bot = next; - } - } - Some((bot, top)) - } else { - None - } -} - -fn even_divisor(iter: &mut I) -> Option<(u32, u32)> -where - I: Iterator, -{ - let mut seen: HashSet = HashSet::new(); - for i in iter { - for s in &seen { - let s = *s; - match u32::cmp(&s, &i) { - Ordering::Less => { - if i % s == 0 { - return Some((i, s)); - }; - } - Ordering::Greater => { - if s % i == 0 { - return Some((s, i)); - }; - } - Ordering::Equal => { - return Some((i, s)); - } - } - } - seen.insert(i); - } - None -} - -pub fn part_1(numbers: &mut I) -> u32 -where - I: Iterator, - J: Iterator, -{ - numbers - .map(|mut row| { - if let Some((min, max)) = min_max(&mut row) { - max - min - } else { - 0 - } - }) - .sum() -} - -pub fn part_2(numbers: &mut I) -> u32 -where - I: Iterator, - J: Iterator, -{ - numbers - .map(|mut row| { - let (bigger, smaller) = even_divisor(&mut row).unwrap(); - bigger / smaller - }) - .sum() -} +use day_2::day_2; fn main() { - let input = input(); + let input = day_2::input(); println!( "part 1 => {}", - part_1(&mut input.iter().map(|r| r.iter().cloned())) + day_2::part_1(&mut input.iter().map(|r| r.iter().cloned())) ); println!( "part 2 => {}", - part_2(&mut input.iter().map(|r| r.iter().cloned())) + day_2::part_2(&mut input.iter().map(|r| r.iter().cloned())) ); } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!( - part_1( - &mut vec![vec![5, 1, 9, 5], vec![7, 5, 3], vec![2, 4, 6, 8]] - .iter() - .map(|r| r.iter().cloned()) - ), - 18 - ); - } - - #[test] - fn part2_known() { - assert_eq!( - part_2( - &mut vec![vec![5, 9, 2, 8], vec![9, 4, 7, 3], vec![3, 8, 6, 5]] - .iter() - .map(|r| r.iter().cloned()) - ), - 9 - ); - } - - #[test] - fn test_day_2() { - let input = input(); - let answer = part_1(&mut input.iter().map(|r| r.iter().cloned())); - assert_eq!(answer, 44887); - let answer = part_2(&mut input.iter().map(|r| r.iter().cloned())); - assert_eq!(answer, 242); - } -} diff --git a/day_3/src/lib.rs b/day_3/src/lib.rs new file mode 100644 index 0000000..ffdf2a0 --- /dev/null +++ b/day_3/src/lib.rs @@ -0,0 +1,287 @@ +pub mod day_3 { + + pub fn input() -> u32 { + let input = include_str!("../input.txt"); + input + .trim() + .parse() + .unwrap_or_else(|_| panic!("{} wasn't a valid u32", input)) + } + + fn layer(input: u32) -> u32 { + f32::ceil((f32::sqrt(input as f32) + 1.0) / 2.0) as u32 + } + + pub fn part_1(input: u32) -> u32 { + if input == 1 { + return 0; + } + // Which layer are we in? + let layer = layer(input); + // Where did this layer start? + let start = (2 * layer - 3) * (2 * layer - 3) + 1; + let right_middle = start + layer - 2; + if input <= right_middle { + return right_middle - input + layer - 1; + } + let right_top = right_middle + layer - 1; + if input <= right_top { + return input - right_middle + layer - 1; + } + let middle_top = right_top + layer - 1; + if input <= middle_top { + return middle_top - input + layer - 1; + } + let left_top = middle_top + layer - 1; + if input <= left_top { + return input - middle_top + layer - 1; + } + let left_middle = left_top + layer - 1; + if input <= left_middle { + return left_middle - input + layer - 1; + } + let left_bottom = left_middle + layer - 1; + if input <= left_bottom { + return input - left_middle + layer - 1; + } + let middle_bottom = left_bottom + layer - 1; + if input <= middle_bottom { + return middle_bottom - input + layer - 1; + } + input - middle_bottom + layer - 1 + } + + fn layer_size(layer: usize) -> usize { + if layer == 1 { + 1 + } else { + let s = 2 * layer - 1; + let t = 2 * (layer - 1) - 1; + (s * s) - (t * t) + } + } + + #[cfg(test)] + mod layer_test { + use super::*; + + #[test] + fn layer_test_1() { + assert_eq!(layer_size(1), 1); + assert_eq!(layer_size(2), 8); + assert_eq!(layer_size(3), 16); + } + } + + #[macro_export] + macro_rules! set_or_return { + ( $x:expr, $input:ident, $val: expr) => {{ + $x = $val; + if $x > $input { + return $x; + } + }}; + } + + pub fn part_2(input: u32) -> u32 { + let mut prev_layer: Vec = vec![1, 2, 4, 5, 10, 11, 23, 25]; + if input < 25 { + return prev_layer.iter().cloned().find(|&i| i > input).unwrap(); + } + + for layer in 3.. { + let mut curr_layer = vec![0; layer_size(layer)]; + + // Starting one place above the bottom-right, fill in anticlockwise. + // The right-hand edge, where for the purposes of this loop only, + // our `i` is viewing itself as ranging over the actual indices. + // So i = 1 means we're one step above the bottom of the layer (i.e. it's + // the smallest element in the layer). + // As this loop views things, i = 0 would be the largest element in the layer. + // Note, though, that `curr_layer` is still indexed from 0 being the smallest + // element. + set_or_return!( + curr_layer[0], + input, + prev_layer[0] + prev_layer[prev_layer.len() - 1] + ); + set_or_return!( + curr_layer[1], + input, + prev_layer[0] + prev_layer[1] + curr_layer[0] + prev_layer[prev_layer.len() - 1] + ); + // 3 -> 2 + // 4 -> 4 + for i in 2..(2 * layer - 4) { + set_or_return!( + curr_layer[i], + input, + curr_layer[i - 1] + prev_layer[i - 2] + prev_layer[i - 1] + prev_layer[i] + ); + } + // Top-right corner and its neighbours + set_or_return!( + curr_layer[2 * layer - 4], + input, + curr_layer[2 * layer - 5] + + prev_layer[2 * (layer - 1) - 3] + + prev_layer[2 * (layer - 1) - 4] + ); + set_or_return!( + curr_layer[2 * layer - 3], + input, + curr_layer[2 * layer - 4] + prev_layer[2 * (layer - 1) - 3] + ); + + // Walking along the top edge now + set_or_return!( + curr_layer[2 * layer - 2], + input, + curr_layer[2 * layer - 3] + + curr_layer[2 * layer - 4] + + prev_layer[2 * (layer - 1) - 3] + + prev_layer[2 * (layer - 1) - 2] + ); + + for i in 2..(2 * layer - 3) { + set_or_return!( + curr_layer[2 * layer - 3 + i], + input, + curr_layer[2 * layer - 4 + i] + + prev_layer[2 * (layer - 2) + i - 3] + + prev_layer[2 * (layer - 2) + i - 2] + + prev_layer[2 * (layer - 2) + i - 1] + ); + } + // The top-left corner, and its two surrounding squares + set_or_return!( + curr_layer[4 * (layer - 1) - 2], + input, + curr_layer[4 * (layer - 1) - 3] + + prev_layer[4 * (layer - 2) - 2] + + prev_layer[4 * (layer - 2) - 1] + ); + set_or_return!( + curr_layer[4 * (layer - 1) - 1], + input, + curr_layer[4 * (layer - 1) - 2] + prev_layer[4 * (layer - 2) - 1] + ); + set_or_return!( + curr_layer[4 * (layer - 1)], + input, + curr_layer[4 * (layer - 1) - 1] + + curr_layer[4 * (layer - 1) - 2] + + prev_layer[4 * (layer - 2) - 1] + + prev_layer[4 * (layer - 2)] + ); + + // Walk along the left edge + for i in 2..(2 * layer - 3) { + set_or_return!( + curr_layer[4 * (layer - 1) + i - 1], + input, + curr_layer[4 * (layer - 1) + i - 2] + + prev_layer[4 * (layer - 2) + i - 3] + + prev_layer[4 * (layer - 2) + i - 2] + + prev_layer[4 * (layer - 2) + i - 1] + ); + } + + // The bottom-left corner, and its two surrounding squares + set_or_return!( + curr_layer[6 * layer - 8], + input, + curr_layer[6 * layer - 9] + prev_layer[6 * layer - 14] + prev_layer[6 * layer - 13] + ); + set_or_return!( + curr_layer[6 * layer - 7], + input, + curr_layer[6 * layer - 8] + prev_layer[6 * layer - 13] + ); + set_or_return!( + curr_layer[6 * layer - 6], + input, + curr_layer[6 * layer - 7] + + curr_layer[6 * layer - 8] + + prev_layer[6 * layer - 13] + + prev_layer[6 * layer - 12] + ); + + // Walk along the bottom edge + for i in 2..(2 * layer - 3) { + set_or_return!( + curr_layer[6 * (layer - 1) + i - 1], + input, + curr_layer[6 * (layer - 1) + i - 2] + + prev_layer[6 * (layer - 2) + i - 3] + + prev_layer[6 * (layer - 2) + i - 2] + + prev_layer[6 * (layer - 2) + i - 1] + ); + } + + // The bottom-left corner and the square one to its left + set_or_return!( + curr_layer[8 * layer - 10], + input, + curr_layer[8 * layer - 11] + + prev_layer[prev_layer.len() - 1] + + prev_layer[prev_layer.len() - 2] + + curr_layer[0] + ); + set_or_return!( + curr_layer[8 * layer - 9], + input, + curr_layer[8 * layer - 10] + prev_layer[prev_layer.len() - 1] + curr_layer[0] + ); + + prev_layer = curr_layer; + } + + panic!("How could we have broken out of this infinite loop?!") + } +} + +#[cfg(test)] +mod tests { + use super::day_3::*; + + #[test] + fn part1_known() { + assert_eq!(part_1(1), 0); + assert_eq!(part_1(2), 1); + assert_eq!(part_1(3), 2); + assert_eq!(part_1(4), 1); + assert_eq!(part_1(5), 2); + assert_eq!(part_1(6), 1); + assert_eq!(part_1(7), 2); + assert_eq!(part_1(8), 1); + assert_eq!(part_1(9), 2); + assert_eq!(part_1(10), 3); + assert_eq!(part_1(11), 2); + assert_eq!(part_1(12), 3); + assert_eq!(part_1(13), 4); + assert_eq!(part_1(14), 3); + assert_eq!(part_1(15), 2); + assert_eq!(part_1(16), 3); + assert_eq!(part_1(17), 4); + assert_eq!(part_1(18), 3); + assert_eq!(part_1(19), 2); + assert_eq!(part_1(20), 3); + assert_eq!(part_1(21), 4); + assert_eq!(part_1(22), 3); + assert_eq!(part_1(23), 2); + assert_eq!(part_1(24), 3); + assert_eq!(part_1(25), 4); + assert_eq!(part_1(26), 5); + assert_eq!(part_1(1024), 31); + } + + #[test] + fn test_day_3() { + let input = input(); + let answer = part_1(input); + assert_eq!(answer, 438); + let answer = part_2(input); + assert_eq!(answer, 266330); + } +} diff --git a/day_3/src/main.rs b/day_3/src/main.rs index e67174b..41297c8 100644 --- a/day_3/src/main.rs +++ b/day_3/src/main.rs @@ -1,290 +1,7 @@ -fn input() -> u32 { - let input = include_str!("../input.txt"); - input - .trim() - .parse() - .unwrap_or_else(|_| panic!("{} wasn't a valid u32", input)) -} - -fn layer(input: u32) -> u32 { - f32::ceil((f32::sqrt(input as f32) + 1.0) / 2.0) as u32 -} - -pub fn part_1(input: u32) -> u32 { - if input == 1 { - return 0; - } - // Which layer are we in? - let layer = layer(input); - // Where did this layer start? - let start = (2 * layer - 3) * (2 * layer - 3) + 1; - let right_middle = start + layer - 2; - if input <= right_middle { - return right_middle - input + layer - 1; - } - let right_top = right_middle + layer - 1; - if input <= right_top { - return input - right_middle + layer - 1; - } - let middle_top = right_top + layer - 1; - if input <= middle_top { - return middle_top - input + layer - 1; - } - let left_top = middle_top + layer - 1; - if input <= left_top { - return input - middle_top + layer - 1; - } - let left_middle = left_top + layer - 1; - if input <= left_middle { - return left_middle - input + layer - 1; - } - let left_bottom = left_middle + layer - 1; - if input <= left_bottom { - return input - left_middle + layer - 1; - } - let middle_bottom = left_bottom + layer - 1; - if input <= middle_bottom { - return middle_bottom - input + layer - 1; - } - input - middle_bottom + layer - 1 -} - -fn layer_size(layer: usize) -> usize { - if layer == 1 { - 1 - } else { - let s = 2 * layer - 1; - let t = 2 * (layer - 1) - 1; - (s * s) - (t * t) - } -} - -#[cfg(test)] -mod layer_test { - use super::*; - - #[test] - fn layer_test_1() { - assert_eq!(layer_size(1), 1); - assert_eq!(layer_size(2), 8); - assert_eq!(layer_size(3), 16); - } -} - -#[macro_export] -macro_rules! set_or_return { - ( $x:expr, $input:ident, $val: expr) => {{ - $x = $val; - if $x > $input { - return $x; - } - }}; -} - -pub fn part_2(input: u32) -> u32 { - let mut prev_layer: Vec = vec![1, 2, 4, 5, 10, 11, 23, 25]; - if input < 25 { - return prev_layer.iter().cloned().find(|&i| i > input).unwrap(); - } - - for layer in 3.. { - let mut curr_layer = vec![0; layer_size(layer)]; - - // Starting one place above the bottom-right, fill in anticlockwise. - // The right-hand edge, where for the purposes of this loop only, - // our `i` is viewing itself as ranging over the actual indices. - // So i = 1 means we're one step above the bottom of the layer (i.e. it's - // the smallest element in the layer). - // As this loop views things, i = 0 would be the largest element in the layer. - // Note, though, that `curr_layer` is still indexed from 0 being the smallest - // element. - set_or_return!( - curr_layer[0], - input, - prev_layer[0] + prev_layer[prev_layer.len() - 1] - ); - set_or_return!( - curr_layer[1], - input, - prev_layer[0] + prev_layer[1] + curr_layer[0] + prev_layer[prev_layer.len() - 1] - ); - // 3 -> 2 - // 4 -> 4 - for i in 2..(2 * layer - 4) { - set_or_return!( - curr_layer[i], - input, - curr_layer[i - 1] + prev_layer[i - 2] + prev_layer[i - 1] + prev_layer[i] - ); - } - // Top-right corner and its neighbours - set_or_return!( - curr_layer[2 * layer - 4], - input, - curr_layer[2 * layer - 5] - + prev_layer[2 * (layer - 1) - 3] - + prev_layer[2 * (layer - 1) - 4] - ); - set_or_return!( - curr_layer[2 * layer - 3], - input, - curr_layer[2 * layer - 4] + prev_layer[2 * (layer - 1) - 3] - ); - - // Walking along the top edge now - set_or_return!( - curr_layer[2 * layer - 2], - input, - curr_layer[2 * layer - 3] - + curr_layer[2 * layer - 4] - + prev_layer[2 * (layer - 1) - 3] - + prev_layer[2 * (layer - 1) - 2] - ); - - for i in 2..(2 * layer - 3) { - set_or_return!( - curr_layer[2 * layer - 3 + i], - input, - curr_layer[2 * layer - 4 + i] - + prev_layer[2 * (layer - 2) + i - 3] - + prev_layer[2 * (layer - 2) + i - 2] - + prev_layer[2 * (layer - 2) + i - 1] - ); - } - // The top-left corner, and its two surrounding squares - set_or_return!( - curr_layer[4 * (layer - 1) - 2], - input, - curr_layer[4 * (layer - 1) - 3] - + prev_layer[4 * (layer - 2) - 2] - + prev_layer[4 * (layer - 2) - 1] - ); - set_or_return!( - curr_layer[4 * (layer - 1) - 1], - input, - curr_layer[4 * (layer - 1) - 2] + prev_layer[4 * (layer - 2) - 1] - ); - set_or_return!( - curr_layer[4 * (layer - 1)], - input, - curr_layer[4 * (layer - 1) - 1] - + curr_layer[4 * (layer - 1) - 2] - + prev_layer[4 * (layer - 2) - 1] - + prev_layer[4 * (layer - 2)] - ); - - // Walk along the left edge - for i in 2..(2 * layer - 3) { - set_or_return!( - curr_layer[4 * (layer - 1) + i - 1], - input, - curr_layer[4 * (layer - 1) + i - 2] - + prev_layer[4 * (layer - 2) + i - 3] - + prev_layer[4 * (layer - 2) + i - 2] - + prev_layer[4 * (layer - 2) + i - 1] - ); - } - - // The bottom-left corner, and its two surrounding squares - set_or_return!( - curr_layer[6 * layer - 8], - input, - curr_layer[6 * layer - 9] + prev_layer[6 * layer - 14] + prev_layer[6 * layer - 13] - ); - set_or_return!( - curr_layer[6 * layer - 7], - input, - curr_layer[6 * layer - 8] + prev_layer[6 * layer - 13] - ); - set_or_return!( - curr_layer[6 * layer - 6], - input, - curr_layer[6 * layer - 7] - + curr_layer[6 * layer - 8] - + prev_layer[6 * layer - 13] - + prev_layer[6 * layer - 12] - ); - - // Walk along the bottom edge - for i in 2..(2 * layer - 3) { - set_or_return!( - curr_layer[6 * (layer - 1) + i - 1], - input, - curr_layer[6 * (layer - 1) + i - 2] - + prev_layer[6 * (layer - 2) + i - 3] - + prev_layer[6 * (layer - 2) + i - 2] - + prev_layer[6 * (layer - 2) + i - 1] - ); - } - - // The bottom-left corner and the square one to its left - set_or_return!( - curr_layer[8 * layer - 10], - input, - curr_layer[8 * layer - 11] - + prev_layer[prev_layer.len() - 1] - + prev_layer[prev_layer.len() - 2] - + curr_layer[0] - ); - set_or_return!( - curr_layer[8 * layer - 9], - input, - curr_layer[8 * layer - 10] + prev_layer[prev_layer.len() - 1] + curr_layer[0] - ); - - prev_layer = curr_layer; - } - - panic!("How could we have broken out of this infinite loop?!") -} +use day_3::day_3; fn main() { - let input = input(); - println!("part 1 => {}", part_1(input)); - println!("part 2 => {}", part_2(input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!(part_1(1), 0); - assert_eq!(part_1(2), 1); - assert_eq!(part_1(3), 2); - assert_eq!(part_1(4), 1); - assert_eq!(part_1(5), 2); - assert_eq!(part_1(6), 1); - assert_eq!(part_1(7), 2); - assert_eq!(part_1(8), 1); - assert_eq!(part_1(9), 2); - assert_eq!(part_1(10), 3); - assert_eq!(part_1(11), 2); - assert_eq!(part_1(12), 3); - assert_eq!(part_1(13), 4); - assert_eq!(part_1(14), 3); - assert_eq!(part_1(15), 2); - assert_eq!(part_1(16), 3); - assert_eq!(part_1(17), 4); - assert_eq!(part_1(18), 3); - assert_eq!(part_1(19), 2); - assert_eq!(part_1(20), 3); - assert_eq!(part_1(21), 4); - assert_eq!(part_1(22), 3); - assert_eq!(part_1(23), 2); - assert_eq!(part_1(24), 3); - assert_eq!(part_1(25), 4); - assert_eq!(part_1(26), 5); - assert_eq!(part_1(1024), 31); - } - - #[test] - fn test_day_3() { - let input = input(); - let answer = part_1(input); - assert_eq!(answer, 438); - let answer = part_2(input); - assert_eq!(answer, 266330); - } + let input = day_3::input(); + println!("part 1 => {}", day_3::part_1(input)); + println!("part 2 => {}", day_3::part_2(input)); } diff --git a/day_4/src/lib.rs b/day_4/src/lib.rs new file mode 100644 index 0000000..5c13eba --- /dev/null +++ b/day_4/src/lib.rs @@ -0,0 +1,85 @@ +pub mod day_4 { + + use std::collections::HashSet; + use std::hash::Hash; + + pub fn input() -> Vec> { + let input = include_str!("../input.txt"); + input + .lines() + .map(|l| l.split_whitespace().collect()) + .collect() + } + + fn contains_duplicate(i: &mut I) -> bool + where + I: Iterator, + X: Eq + Hash, + { + let mut so_far = HashSet::new(); + for elt in i { + if !so_far.insert(elt) { + return true; + } + } + false + } + + pub fn part_1(input: &[Vec<&str>]) -> usize { + input + .iter() + .filter(|words| !contains_duplicate(&mut words.iter())) + .count() + } + + pub fn part_2(input: &[Vec<&str>]) -> usize { + input + .iter() + .filter(|words| { + !contains_duplicate(&mut words.iter().map(|&w| { + let mut w = w.chars().collect::>(); + w.sort_unstable(); + w + })) + }) + .count() + } +} + +#[cfg(test)] +mod tests { + use super::day_4::*; + + #[test] + fn part1_known() { + assert_eq!( + part_1(&vec![ + vec!["aa", "bb", "cc", "dd", "ee"], + vec!["aa", "bb", "cc", "dd", "aa"], + vec!["aa", "bb", "cc", "dd", "aaa"] + ]), + 2 + ); + } + + #[test] + fn part2_known() { + assert_eq!( + part_2(&vec![ + vec!["abcde", "fghij"], + vec!["abcde", "xyz", "ecdab"], + vec!["a", "ab", "abc", "abd", "abf", "abj"], + vec!["iiii", "oiii", "ooii", "oooi", "oooo"], + vec!["oiii", "ioii", "iioi", "iiio"] + ]), + 3 + ); + } + + #[test] + fn test_day_4() { + let input = input(); + assert_eq!(part_1(&input), 325); + assert_eq!(part_2(&input), 119); + } +} diff --git a/day_4/src/main.rs b/day_4/src/main.rs index 1c3cfac..e67c027 100644 --- a/day_4/src/main.rs +++ b/day_4/src/main.rs @@ -1,88 +1,7 @@ -use std::collections::HashSet; -use std::hash::Hash; - -fn input() -> Vec> { - let input = include_str!("../input.txt"); - input - .lines() - .map(|l| l.split_whitespace().collect()) - .collect() -} - -fn contains_duplicate(i: &mut I) -> bool -where - I: Iterator, - X: Eq + Hash, -{ - let mut so_far = HashSet::new(); - for elt in i { - if !so_far.insert(elt) { - return true; - } - } - false -} - -pub fn part_1(input: &[Vec<&str>]) -> usize { - input - .iter() - .filter(|words| !contains_duplicate(&mut words.iter())) - .count() -} - -pub fn part_2(input: &[Vec<&str>]) -> usize { - input - .iter() - .filter(|words| { - !contains_duplicate(&mut words.iter().map(|&w| { - let mut w = w.chars().collect::>(); - w.sort_unstable(); - w - })) - }) - .count() -} +use day_4::day_4; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&input)); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!( - part_1(&vec![ - vec!["aa", "bb", "cc", "dd", "ee"], - vec!["aa", "bb", "cc", "dd", "aa"], - vec!["aa", "bb", "cc", "dd", "aaa"] - ]), - 2 - ); - } - - #[test] - fn part2_known() { - assert_eq!( - part_2(&vec![ - vec!["abcde", "fghij"], - vec!["abcde", "xyz", "ecdab"], - vec!["a", "ab", "abc", "abd", "abf", "abj"], - vec!["iiii", "oiii", "ooii", "oooi", "oooo"], - vec!["oiii", "ioii", "iioi", "iiio"] - ]), - 3 - ); - } - - #[test] - fn test_day_4() { - let input = input(); - assert_eq!(part_1(&input), 325); - assert_eq!(part_2(&input), 119); - } + let input = day_4::input(); + println!("part 1 => {}", day_4::part_1(&input)); + println!("part 2 => {}", day_4::part_2(&input)); } diff --git a/day_5/Cargo.toml b/day_5/Cargo.toml index d4f9292..d8a4b0a 100644 --- a/day_5/Cargo.toml +++ b/day_5/Cargo.toml @@ -7,3 +7,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +[dev_dependencies] +criterion = "0.3" + +[[bench]] +name = "day_5_part_2" +harness = false diff --git a/day_5/benches/day_5_part_2.rs b/day_5/benches/day_5_part_2.rs new file mode 100644 index 0000000..e8c06f4 --- /dev/null +++ b/day_5/benches/day_5_part_2.rs @@ -0,0 +1,15 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use day_5::day_5::{input, part_2}; + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("part 2", |b| { + let input = input(); + b.iter(|| { + let mut input = input.to_vec(); + part_2(&mut input); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/day_5/src/lib.rs b/day_5/src/lib.rs new file mode 100644 index 0000000..aa1cf16 --- /dev/null +++ b/day_5/src/lib.rs @@ -0,0 +1,73 @@ +pub mod day_5 { + use std::convert::TryFrom; + + pub fn input() -> Vec { + let input = include_str!("../input.txt"); + input + .lines() + .map(|l| { + l.parse() + .unwrap_or_else(|_| panic!("{} wasn't a valid u32", l)) + }) + .collect::>() + } + + pub fn part_1(v: &mut Vec) -> u32 { + let mut count = 0; + let mut index: usize = 0; + while index < v.len() { + let bounce = v[index]; + v[index] = bounce + 1; + let test = i32::try_from(index).unwrap() + bounce; + if test < 0 { + return count; + } + index = usize::try_from(test).unwrap(); + count += 1; + } + count + } + + pub fn part_2(v: &mut Vec) -> u32 { + let mut count = 0; + let mut index: usize = 0; + while index < v.len() { + let bounce = v[index]; + if bounce >= 3 { + v[index] = bounce - 1; + } else { + v[index] = bounce + 1; + } + let test = i32::try_from(index).unwrap() + bounce; + if test < 0 { + return count; + } + index = usize::try_from(test).unwrap(); + count += 1; + } + count + } +} + +#[cfg(test)] +mod tests { + use super::day_5::*; + + #[test] + fn part1_known() { + assert_eq!(part_1(&mut vec![0, 3, 0, 1, -3]), 5); + } + + #[test] + fn part2_known() { + assert_eq!(part_2(&mut vec![0, 3, 0, 1, -3]), 10); + } + + #[test] + fn test_day_5() { + let input = input(); + assert_eq!(part_1(&mut input.clone()), 391540); + let mut input = input; + assert_eq!(part_2(&mut input), 30513679); + } +} diff --git a/day_5/src/main.rs b/day_5/src/main.rs index c97acf2..c3064ff 100644 --- a/day_5/src/main.rs +++ b/day_5/src/main.rs @@ -1,78 +1,8 @@ -use std::convert::TryFrom; - -fn input() -> Vec { - let input = include_str!("../input.txt"); - input - .lines() - .map(|l| { - l.parse() - .unwrap_or_else(|_| panic!("{} wasn't a valid u32", l)) - }) - .collect::>() -} - -pub fn part_1(v: &mut Vec) -> u32 { - let mut count = 0; - let mut index: usize = 0; - while index < v.len() { - let bounce = v[index]; - v[index] = bounce + 1; - let test = i32::try_from(index).unwrap() + bounce; - if test < 0 { - return count; - } - index = usize::try_from(test).unwrap(); - count += 1; - } - count -} - -pub fn part_2(v: &mut Vec) -> u32 { - let mut count = 0; - let mut index: usize = 0; - while index < v.len() { - let bounce = v[index]; - if bounce >= 3 { - v[index] = bounce - 1; - } else { - v[index] = bounce + 1; - } - let test = i32::try_from(index).unwrap() + bounce; - if test < 0 { - return count; - } - index = usize::try_from(test).unwrap(); - count += 1; - } - count -} +use day_5::day_5; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&mut input.clone())); + let input = day_5::input(); + println!("part 1 => {}", day_5::part_1(&mut input.clone())); let mut input = input; - println!("part 2 => {}", part_2(&mut input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!(part_1(&mut vec![0, 3, 0, 1, -3]), 5); - } - - #[test] - fn part2_known() { - assert_eq!(part_2(&mut vec![0, 3, 0, 1, -3]), 10); - } - - #[test] - fn test_day_5() { - let input = input(); - assert_eq!(part_1(&mut input.clone()), 391540); - let mut input = input; - assert_eq!(part_2(&mut input), 30513679); - } + println!("part 2 => {}", day_5::part_2(&mut input)); } diff --git a/day_6/src/lib.rs b/day_6/src/lib.rs new file mode 100644 index 0000000..7770d99 --- /dev/null +++ b/day_6/src/lib.rs @@ -0,0 +1,103 @@ +pub mod day_6 { + use std::collections::HashMap; + use std::collections::HashSet; + + pub fn input() -> Vec { + let input = include_str!("../input.txt"); + input + .trim() + .split_whitespace() + .map(|i| { + i.parse() + .unwrap_or_else(|_| panic!("{} wasn't a valid u32", i)) + }) + .collect::>() + } + + pub fn part_1(v: &mut [u32]) -> u32 { + let len = v.len() as u32; + let mut seen: HashSet> = HashSet::new(); + let mut count = 0; + let (mut max_pos, mut max) = v + .iter() + .cloned() + .enumerate() + .max_by_key(|(_, x)| *x) + .unwrap(); + while seen.insert(v.to_vec()) { + let extras = max % len; + let all = max / len; + v[max_pos] = 0; + for i in 0..(extras as usize) { + v[(max_pos + i + 1) % v.len()] += 1; + } + max = 0; + for (i, item) in v.iter_mut().enumerate() { + *item += all; + if *item > max { + max = *item; + max_pos = i; + } + } + count += 1; + } + count + } + + pub fn part_2(v: &mut [u32]) -> u32 { + let len = v.len() as u32; + let mut seen: HashMap, u32> = HashMap::new(); + let mut count = 0; + let (mut max_pos, mut max) = v + .iter() + .cloned() + .enumerate() + .max_by_key(|(_, x)| *x) + .unwrap(); + loop { + if let Some(existing) = seen.insert(v.to_vec(), count) { + return count - existing; + } + let extras = max % len; + let all = max / len; + v[max_pos] = 0; + for i in 0..(extras as usize) { + v[(max_pos + i + 1) % v.len()] += 1; + } + max = 0; + for (i, item) in v.iter_mut().enumerate() { + *item += all; + if *item > max { + max = *item; + max_pos = i; + } + } + count += 1; + } + } +} + +#[cfg(test)] +mod tests { + use super::day_6::*; + + #[test] + fn part1_known() { + assert_eq!(part_1(&mut vec![0, 2, 7, 0]), 5); + } + + #[test] + fn part2_known() { + assert_eq!(part_2(&mut vec![0, 2, 7, 0]), 4); + } + + #[test] + fn test_day_6() { + let input = input(); + let answer = part_1(&mut input.clone()); + assert_eq!(answer, 4074); + let mut input = input; + let answer = part_2(&mut input); + assert_eq!(answer, 2793); + } +} diff --git a/day_6/src/main.rs b/day_6/src/main.rs index c2cb238..a793a55 100644 --- a/day_6/src/main.rs +++ b/day_6/src/main.rs @@ -1,108 +1,8 @@ -use std::collections::HashMap; -use std::collections::HashSet; - -fn input() -> Vec { - let input = include_str!("../input.txt"); - input - .trim() - .split_whitespace() - .map(|i| { - i.parse() - .unwrap_or_else(|_| panic!("{} wasn't a valid u32", i)) - }) - .collect::>() -} - -pub fn part_1(v: &mut [u32]) -> u32 { - let len = v.len() as u32; - let mut seen: HashSet> = HashSet::new(); - let mut count = 0; - let (mut max_pos, mut max) = v - .iter() - .cloned() - .enumerate() - .max_by_key(|(_, x)| *x) - .unwrap(); - while seen.insert(v.to_vec()) { - let extras = max % len; - let all = max / len; - v[max_pos] = 0; - for i in 0..(extras as usize) { - v[(max_pos + i + 1) % v.len()] += 1; - } - max = 0; - for (i, item) in v.iter_mut().enumerate() { - *item += all; - if *item > max { - max = *item; - max_pos = i; - } - } - count += 1; - } - count -} - -pub fn part_2(v: &mut [u32]) -> u32 { - let len = v.len() as u32; - let mut seen: HashMap, u32> = HashMap::new(); - let mut count = 0; - let (mut max_pos, mut max) = v - .iter() - .cloned() - .enumerate() - .max_by_key(|(_, x)| *x) - .unwrap(); - loop { - if let Some(existing) = seen.insert(v.to_vec(), count) { - return count - existing; - } - let extras = max % len; - let all = max / len; - v[max_pos] = 0; - for i in 0..(extras as usize) { - v[(max_pos + i + 1) % v.len()] += 1; - } - max = 0; - for (i, item) in v.iter_mut().enumerate() { - *item += all; - if *item > max { - max = *item; - max_pos = i; - } - } - count += 1; - } -} +use day_6::day_6; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&mut input.clone())); + let input = day_6::input(); + println!("part 1 => {}", day_6::part_1(&mut input.clone())); let mut input = input; - println!("part 2 => {}", part_2(&mut input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1_known() { - assert_eq!(part_1(&mut vec![0, 2, 7, 0]), 5); - } - - #[test] - fn part2_known() { - assert_eq!(part_2(&mut vec![0, 2, 7, 0]), 4); - } - - #[test] - fn test_day_6() { - let input = input(); - let answer = part_1(&mut input.clone()); - assert_eq!(answer, 4074); - let mut input = input; - let answer = part_2(&mut input); - assert_eq!(answer, 2793); - } + println!("part 2 => {}", day_6::part_2(&mut input)); } diff --git a/day_7/src/lib.rs b/day_7/src/lib.rs new file mode 100644 index 0000000..3324afe --- /dev/null +++ b/day_7/src/lib.rs @@ -0,0 +1,370 @@ +pub mod day_7 { + use std::collections::HashMap; + + #[derive(Debug, PartialEq, Eq)] + pub struct Node<'a> { + pub(crate) name: &'a str, + pub(crate) weight: u32, + pub(crate) children: Vec<&'a str>, + } + + struct TreeNode<'a> { + children: Vec, + name: &'a str, + weight: u32, + } + + struct Tree<'a> { + nodes: Vec>, + root: usize, + } + + enum ParserState { + Name, + ExpectBracket, + Weight, + AnyChildren(usize), + ParseChild(usize), + ExpectSpace, + } + + pub fn parse_line(s: &str) -> Node { + let mut state = ParserState::Name; + let mut count: usize = 0; + let mut name_end = 0; + let mut weight: u32 = 0; + let mut children: Vec<&str> = vec![]; + + for chr in s.chars() { + match state { + ParserState::Name => { + if chr == ' ' { + state = ParserState::ExpectBracket; + name_end = count; + } + } + ParserState::ExpectBracket => { + if chr != '(' { + panic!("Malformed string! {}", s) + } + state = ParserState::Weight; + } + ParserState::Weight => { + if chr == ')' { + state = ParserState::AnyChildren(0); + } else { + weight = weight * 10 + + chr + .to_digit(10) + .unwrap_or_else(|| panic!("Expected a digit, got {}", chr)); + } + } + ParserState::AnyChildren(i) => { + if i < 3 { + state = ParserState::AnyChildren(i + 1); + } else { + state = ParserState::ParseChild(count + 1); + } + } + ParserState::ParseChild(start) => { + if chr == ',' { + children.push(&s[start..count]); + state = ParserState::ExpectSpace; + } + } + ParserState::ExpectSpace => { + if chr != ' ' { + panic!("Parse failure, expected space, got {}", chr); + } + state = ParserState::ParseChild(count + 1); + } + } + count += 1; + } + + let name = &s[0..name_end]; + match state { + ParserState::AnyChildren(0) => { + // No children + Node { + name, + weight, + children, + } + } + ParserState::ParseChild(start) => { + children.push(&s[start..count]); + Node { + name, + weight, + children, + } + } + _ => { + panic!("Parse failure"); + } + } + } + + pub fn input() -> Vec> { + let input = include_str!("../input.txt"); + input.lines().map(|l| parse_line(l)).collect::>() + } + + fn tree_it<'a>(nodes: &[Node<'a>]) -> Tree<'a> { + let nodes_by_name: HashMap<&'a str, (&Node<'a>, usize)> = nodes + .iter() + .enumerate() + .map(|(count, i)| (i.name, (i, count))) + .collect(); + let mut built: Vec> = nodes + .iter() + .map(|n| TreeNode { + name: n.name, + weight: n.weight, + children: vec![], + }) + .collect(); + let mut is_set: Vec = vec![false; nodes.len()]; + + let mut stack: Vec = vec![]; + for parent_node_index in 0..nodes.len() { + if !is_set[parent_node_index] { + stack.push(parent_node_index); + while let Some(node_index) = stack.pop() { + if is_set[node_index] { + continue; + } + let node = &nodes[node_index]; + let children = (*node).children.iter().map(|&i| { + let (_, child_index) = nodes_by_name.get(i).unwrap(); + *child_index + }); + built[node_index].children.extend(children); + stack.extend(built[node_index].children.iter()); + is_set[node_index] = true; + } + } + } + + // We'll reuse the is_set array for an "is possibly the root" array. + // They're all true now. + for node in built.iter() { + for child in node.children.iter() { + is_set[*child] = false; + } + } + let (top_node, _) = is_set.iter().enumerate().find(|(_, v)| **v).unwrap(); + Tree { + nodes: built, + root: top_node, + } + } + + pub fn part_1<'a>(nodes: &[Node<'a>]) -> &'a str { + let tree = tree_it(nodes); + tree.nodes[tree.root].name + } + + #[derive(Debug)] + struct BalancedWeight { + child_weights: u32, + self_weight: u32, + child_count: usize, + } + + fn total_weight(b: &BalancedWeight) -> u32 { + b.child_weights * (b.child_count as u32) + b.self_weight + } + + fn weight(tree: &Tree, node: usize) -> Result { + let node = &tree.nodes[node]; + let children: Vec<_> = node + .children + .iter() + .map(|node| weight(&tree, *node)) + .collect(); + + let count = children.len(); + let mut c = children.iter(); + let mut different_child = None; + if let Some(child) = c.next() { + match child { + Err(e) => Err(*e), + Ok(child) => { + for other in c { + match other { + Err(e) => return Err(*e), + Ok(other) => { + if total_weight(other) != total_weight(child) { + match different_child { + None => { + different_child = Some(other); + } + Some(different_child) => { + if total_weight(other) == total_weight(different_child) + { + // The bad one is `child`. + return Err(total_weight(other) as i32 + - (child.child_weights + * child.child_count as u32) + as i32); + } else { + // The bad one is `other`. + return Err(total_weight(child) as i32 + - (other.child_weights + * other.child_count as u32) + as i32); + } + } + } + } + } + } + } + match different_child { + None => Ok(BalancedWeight { + self_weight: node.weight, + child_weights: total_weight(child), + child_count: count, + }), + Some(different_child) => { + // The bad one is `different_child`. + Err(total_weight(child) as i32 + - (different_child.child_weights + * different_child.child_count as u32) + as i32) + } + } + } + } + } else { + Ok(BalancedWeight { + self_weight: node.weight, + child_weights: 0, + child_count: 0, + }) + } + } + + pub fn part_2(nodes: &[Node]) -> i32 { + let tree = tree_it(nodes); + let result = weight(&tree, tree.root); + match result { + Err(e) => e, + Ok(w) => panic!("Expected unbalanced tree, got {:?}", w), + } + } +} + +#[cfg(test)] +mod tests { + use super::day_7::*; + + fn test_inputs() -> Vec> { + vec![ + Node { + name: "pbga", + weight: 66, + children: vec![], + }, + Node { + name: "xhth", + weight: 57, + children: vec![], + }, + Node { + name: "ebii", + weight: 61, + children: vec![], + }, + Node { + name: "havc", + weight: 66, + children: vec![], + }, + Node { + name: "ktlj", + weight: 57, + children: vec![], + }, + Node { + name: "fwft", + weight: 72, + children: vec!["ktlj", "cntj", "xhth"], + }, + Node { + name: "qoyq", + weight: 66, + children: vec![], + }, + Node { + name: "padx", + weight: 45, + children: vec!["pbga", "havc", "qoyq"], + }, + Node { + name: "tknk", + weight: 41, + children: vec!["ugml", "padx", "fwft"], + }, + Node { + name: "jptl", + weight: 61, + children: vec![], + }, + Node { + name: "ugml", + weight: 68, + children: vec!["gyxo", "ebii", "jptl"], + }, + Node { + name: "gyxo", + weight: 61, + children: vec![], + }, + Node { + name: "cntj", + weight: 57, + children: vec![], + }, + ] + } + + #[test] + fn test_parse() { + assert_eq!( + parse_line("vvwrg (51)"), + Node { + name: "vvwrg", + weight: 51, + children: vec![] + } + ); + assert_eq!( + parse_line("uglvj (99) -> ymfjt, gkpgf"), + Node { + name: "uglvj", + weight: 99, + children: vec!["ymfjt", "gkpgf"] + } + ); + } + + #[test] + fn part1_known() { + assert_eq!(part_1(&test_inputs()), "tknk"); + } + + #[test] + fn part2_known() { + assert_eq!(part_2(&test_inputs()), 60); + } + + #[test] + fn test_day_7() { + let input = input(); + assert_eq!(part_1(&input), "dgoocsw"); + assert_eq!(part_2(&input), 1275); + } +} diff --git a/day_7/src/main.rs b/day_7/src/main.rs index 0b53cf4..683eef4 100644 --- a/day_7/src/main.rs +++ b/day_7/src/main.rs @@ -1,370 +1,7 @@ -use std::collections::HashMap; - -#[derive(Debug, PartialEq, Eq)] -struct Node<'a> { - name: &'a str, - weight: u32, - children: Vec<&'a str>, -} - -struct TreeNode<'a> { - children: Vec, - name: &'a str, - weight: u32, -} - -struct Tree<'a> { - nodes: Vec>, - root: usize, -} - -enum ParserState { - Name, - ExpectBracket, - Weight, - AnyChildren(usize), - ParseChild(usize), - ExpectSpace, -} - -fn parse_line(s: &str) -> Node { - let mut state = ParserState::Name; - let mut count: usize = 0; - let mut name_end = 0; - let mut weight: u32 = 0; - let mut children: Vec<&str> = vec![]; - - for chr in s.chars() { - match state { - ParserState::Name => { - if chr == ' ' { - state = ParserState::ExpectBracket; - name_end = count; - } - } - ParserState::ExpectBracket => { - if chr != '(' { - panic!("Malformed string! {}", s) - } - state = ParserState::Weight; - } - ParserState::Weight => { - if chr == ')' { - state = ParserState::AnyChildren(0); - } else { - weight = weight * 10 - + chr - .to_digit(10) - .unwrap_or_else(|| panic!("Expected a digit, got {}", chr)); - } - } - ParserState::AnyChildren(i) => { - if i < 3 { - state = ParserState::AnyChildren(i + 1); - } else { - state = ParserState::ParseChild(count + 1); - } - } - ParserState::ParseChild(start) => { - if chr == ',' { - children.push(&s[start..count]); - state = ParserState::ExpectSpace; - } - } - ParserState::ExpectSpace => { - if chr != ' ' { - panic!("Parse failure, expected space, got {}", chr); - } - state = ParserState::ParseChild(count + 1); - } - } - count += 1; - } - - let name = &s[0..name_end]; - match state { - ParserState::AnyChildren(0) => { - // No children - Node { - name, - weight, - children, - } - } - ParserState::ParseChild(start) => { - children.push(&s[start..count]); - Node { - name, - weight, - children, - } - } - _ => { - panic!("Parse failure"); - } - } -} - -fn input() -> Vec> { - let input = include_str!("../input.txt"); - input.lines().map(|l| parse_line(l)).collect::>() -} - -fn tree_it<'a>(nodes: &[Node<'a>]) -> Tree<'a> { - let nodes_by_name: HashMap<&'a str, (&Node<'a>, usize)> = nodes - .iter() - .enumerate() - .map(|(count, i)| (i.name, (i, count))) - .collect(); - let mut built: Vec> = nodes - .iter() - .map(|n| TreeNode { - name: n.name, - weight: n.weight, - children: vec![], - }) - .collect(); - let mut is_set: Vec = vec![false; nodes.len()]; - - let mut stack: Vec = vec![]; - for parent_node_index in 0..nodes.len() { - if !is_set[parent_node_index] { - stack.push(parent_node_index); - while let Some(node_index) = stack.pop() { - if is_set[node_index] { - continue; - } - let node = &nodes[node_index]; - let children = (*node).children.iter().map(|&i| { - let (_, child_index) = nodes_by_name.get(i).unwrap(); - *child_index - }); - built[node_index].children.extend(children); - stack.extend(built[node_index].children.iter()); - is_set[node_index] = true; - } - } - } - - // We'll reuse the is_set array for an "is possibly the root" array. - // They're all true now. - for node in built.iter() { - for child in node.children.iter() { - is_set[*child] = false; - } - } - let (top_node, _) = is_set.iter().enumerate().find(|(_, v)| **v).unwrap(); - Tree { - nodes: built, - root: top_node, - } -} - -fn part_1<'a>(nodes: &[Node<'a>]) -> &'a str { - let tree = tree_it(nodes); - tree.nodes[tree.root].name -} - -#[derive(Debug)] -struct BalancedWeight { - child_weights: u32, - self_weight: u32, - child_count: usize, -} - -fn total_weight(b: &BalancedWeight) -> u32 { - b.child_weights * (b.child_count as u32) + b.self_weight -} - -fn weight(tree: &Tree, node: usize) -> Result { - let node = &tree.nodes[node]; - let children: Vec<_> = node - .children - .iter() - .map(|node| weight(&tree, *node)) - .collect(); - - let count = children.len(); - let mut c = children.iter(); - let mut different_child = None; - if let Some(child) = c.next() { - match child { - Err(e) => Err(*e), - Ok(child) => { - for other in c { - match other { - Err(e) => return Err(*e), - Ok(other) => { - if total_weight(other) != total_weight(child) { - match different_child { - None => { - different_child = Some(other); - } - Some(different_child) => { - if total_weight(other) == total_weight(different_child) { - // The bad one is `child`. - return Err(total_weight(other) as i32 - - (child.child_weights * child.child_count as u32) - as i32); - } else { - // The bad one is `other`. - return Err(total_weight(child) as i32 - - (other.child_weights * other.child_count as u32) - as i32); - } - } - } - } - } - } - } - match different_child { - None => Ok(BalancedWeight { - self_weight: node.weight, - child_weights: total_weight(child), - child_count: count, - }), - Some(different_child) => { - // The bad one is `different_child`. - Err(total_weight(child) as i32 - - (different_child.child_weights * different_child.child_count as u32) - as i32) - } - } - } - } - } else { - Ok(BalancedWeight { - self_weight: node.weight, - child_weights: 0, - child_count: 0, - }) - } -} - -fn part_2(nodes: &[Node]) -> i32 { - let tree = tree_it(nodes); - let result = weight(&tree, tree.root); - match result { - Err(e) => e, - Ok(w) => panic!("Expected unbalanced tree, got {:?}", w), - } -} +use day_7::day_7; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&input)); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - fn test_inputs() -> Vec> { - vec![ - Node { - name: "pbga", - weight: 66, - children: vec![], - }, - Node { - name: "xhth", - weight: 57, - children: vec![], - }, - Node { - name: "ebii", - weight: 61, - children: vec![], - }, - Node { - name: "havc", - weight: 66, - children: vec![], - }, - Node { - name: "ktlj", - weight: 57, - children: vec![], - }, - Node { - name: "fwft", - weight: 72, - children: vec!["ktlj", "cntj", "xhth"], - }, - Node { - name: "qoyq", - weight: 66, - children: vec![], - }, - Node { - name: "padx", - weight: 45, - children: vec!["pbga", "havc", "qoyq"], - }, - Node { - name: "tknk", - weight: 41, - children: vec!["ugml", "padx", "fwft"], - }, - Node { - name: "jptl", - weight: 61, - children: vec![], - }, - Node { - name: "ugml", - weight: 68, - children: vec!["gyxo", "ebii", "jptl"], - }, - Node { - name: "gyxo", - weight: 61, - children: vec![], - }, - Node { - name: "cntj", - weight: 57, - children: vec![], - }, - ] - } - - #[test] - fn test_parse() { - assert_eq!( - parse_line("vvwrg (51)"), - Node { - name: "vvwrg", - weight: 51, - children: vec![] - } - ); - assert_eq!( - parse_line("uglvj (99) -> ymfjt, gkpgf"), - Node { - name: "uglvj", - weight: 99, - children: vec!["ymfjt", "gkpgf"] - } - ); - } - - #[test] - fn part1_known() { - assert_eq!(part_1(&test_inputs()), "tknk"); - } - - #[test] - fn part2_known() { - assert_eq!(part_2(&test_inputs()), 60); - } - - #[test] - fn test_day_7() { - let input = input(); - assert_eq!(part_1(&input), "dgoocsw"); - assert_eq!(part_2(&input), 1275); - } + let input = day_7::input(); + println!("part 1 => {}", day_7::part_1(&input)); + println!("part 2 => {}", day_7::part_2(&input)); } diff --git a/day_8/src/lib.rs b/day_8/src/lib.rs new file mode 100644 index 0000000..835c2af --- /dev/null +++ b/day_8/src/lib.rs @@ -0,0 +1,148 @@ +pub mod day_8 { + + use std::collections::HashMap; + + enum Operation { + Incr, + Decr, + } + + enum Comparison { + Greater, + Equal, + Less, + LessEqual, + GreaterEqual, + NotEqual, + } + + struct Condition<'a> { + register: &'a str, + comparison: Comparison, + number: i32, + } + + pub struct Instruction<'a> { + register: &'a str, + op: Operation, + amount: i32, + condition: Condition<'a>, + } + + pub fn parse(s: &str) -> Instruction { + let mut iter = s.split_whitespace(); + let register = iter.next().unwrap(); + let op = match iter.next().unwrap() { + "inc" => Operation::Incr, + "dec" => Operation::Decr, + s => panic!("Bad! {}", s), + }; + let amount: i32 = iter.next().unwrap().parse().unwrap(); + let if_clause = iter.next().unwrap(); + if if_clause != "if" { + panic!("Expected 'if', got: {}", if_clause); + } + let cmp_variable = iter.next().unwrap(); + let cmp_operator = match iter.next().unwrap() { + "<=" => Comparison::LessEqual, + ">=" => Comparison::GreaterEqual, + "==" => Comparison::Equal, + "<" => Comparison::Less, + ">" => Comparison::Greater, + "!=" => Comparison::NotEqual, + s => panic!("Expected comparison, got: {}", s), + }; + let cmp_num: i32 = iter.next().unwrap().parse().unwrap(); + let condition = Condition { + register: cmp_variable, + comparison: cmp_operator, + number: cmp_num, + }; + match iter.next() { + None => Instruction { + register, + op, + amount, + condition, + }, + Some(s) => panic!("Exp ected end of line, got {}", s), + } + } + + pub fn input() -> Vec> { + let input = include_str!("../input.txt"); + input.lines().map(|l| parse(l)).collect::>() + } + + fn process<'a>(instructions: &[Instruction<'a>]) -> HashMap<&'a str, (i32, i32)> { + let mut registers = HashMap::new(); + for instruction in instructions { + let original = registers + .get(instruction.condition.register) + .map_or(0, |&(_, v)| v); + let proceed = match instruction.condition.comparison { + Comparison::Greater => original > instruction.condition.number, + Comparison::Less => original < instruction.condition.number, + Comparison::GreaterEqual => original >= instruction.condition.number, + Comparison::LessEqual => original <= instruction.condition.number, + Comparison::Equal => original == instruction.condition.number, + Comparison::NotEqual => original != instruction.condition.number, + }; + if proceed { + let (original_max, original_val) = + registers.get(instruction.register).map_or((0, 0), |i| *i); + let new_val = match instruction.op { + Operation::Incr => original_val + instruction.amount, + Operation::Decr => original_val - instruction.amount, + }; + let new_max = std::cmp::max(original_max, new_val); + registers.insert(instruction.register, (new_max, new_val)); + } + } + registers + } + + pub fn part_1(instructions: &[Instruction]) -> i32 { + let map = process(instructions); + map.values().map(|&(_, i)| i).max().unwrap() + } + + pub fn part_2(instructions: &[Instruction]) -> i32 { + let map = process(instructions); + map.values().map(|&(i, _)| i).max().unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::day_8::*; + + fn data() -> Vec> { + vec![ + "b inc 5 if a > 1", + "a inc 1 if b < 5", + "c dec -10 if a >= 1", + "c inc -20 if c == 10", + ] + .iter() + .map(|s| parse(s)) + .collect() + } + + #[test] + fn part1_known() { + assert_eq!(part_1(&data()), 1); + } + + #[test] + fn part2_known() { + assert_eq!(part_2(&data()), 10); + } + + #[test] + fn test_day_8() { + let input = input(); + assert_eq!(part_1(&input), 5752); + assert_eq!(part_2(&input), 6366); + } +} diff --git a/day_8/src/main.rs b/day_8/src/main.rs index 2460786..d560bc0 100644 --- a/day_8/src/main.rs +++ b/day_8/src/main.rs @@ -1,151 +1,7 @@ -use std::collections::HashMap; - -enum Operation { - Incr, - Decr, -} - -enum Comparison { - Greater, - Equal, - Less, - LessEqual, - GreaterEqual, - NotEqual, -} - -struct Condition<'a> { - register: &'a str, - comparison: Comparison, - number: i32, -} - -struct Instruction<'a> { - register: &'a str, - op: Operation, - amount: i32, - condition: Condition<'a>, -} - -fn parse(s: &str) -> Instruction { - let mut iter = s.split_whitespace(); - let register = iter.next().unwrap(); - let op = match iter.next().unwrap() { - "inc" => Operation::Incr, - "dec" => Operation::Decr, - s => panic!("Bad! {}", s), - }; - let amount: i32 = iter.next().unwrap().parse().unwrap(); - let if_clause = iter.next().unwrap(); - if if_clause != "if" { - panic!("Expected 'if', got: {}", if_clause); - } - let cmp_variable = iter.next().unwrap(); - let cmp_operator = match iter.next().unwrap() { - "<=" => Comparison::LessEqual, - ">=" => Comparison::GreaterEqual, - "==" => Comparison::Equal, - "<" => Comparison::Less, - ">" => Comparison::Greater, - "!=" => Comparison::NotEqual, - s => panic!("Expected comparison, got: {}", s), - }; - let cmp_num: i32 = iter.next().unwrap().parse().unwrap(); - let condition = Condition { - register: cmp_variable, - comparison: cmp_operator, - number: cmp_num, - }; - match iter.next() { - None => Instruction { - register, - op, - amount, - condition, - }, - Some(s) => panic!("Exp ected end of line, got {}", s), - } -} - -fn input() -> Vec> { - let input = include_str!("../input.txt"); - input.lines().map(|l| parse(l)).collect::>() -} - -fn process<'a>(instructions: &[Instruction<'a>]) -> HashMap<&'a str, (i32, i32)> { - let mut registers = HashMap::new(); - for instruction in instructions { - let original = registers - .get(instruction.condition.register) - .map_or(0, |&(_, v)| v); - let proceed = match instruction.condition.comparison { - Comparison::Greater => original > instruction.condition.number, - Comparison::Less => original < instruction.condition.number, - Comparison::GreaterEqual => original >= instruction.condition.number, - Comparison::LessEqual => original <= instruction.condition.number, - Comparison::Equal => original == instruction.condition.number, - Comparison::NotEqual => original != instruction.condition.number, - }; - if proceed { - let (original_max, original_val) = - registers.get(instruction.register).map_or((0, 0), |i| *i); - let new_val = match instruction.op { - Operation::Incr => original_val + instruction.amount, - Operation::Decr => original_val - instruction.amount, - }; - let new_max = std::cmp::max(original_max, new_val); - registers.insert(instruction.register, (new_max, new_val)); - } - } - registers -} - -fn part_1(instructions: &[Instruction]) -> i32 { - let map = process(instructions); - map.values().map(|&(_, i)| i).max().unwrap() -} - -fn part_2(instructions: &[Instruction]) -> i32 { - let map = process(instructions); - map.values().map(|&(i, _)| i).max().unwrap() -} +use day_8::day_8; fn main() { - let input = input(); - println!("part 1 => {}", part_1(&input)); - println!("part 2 => {}", part_2(&input)); -} - -#[cfg(test)] -mod tests { - use super::*; - - fn data() -> Vec> { - vec![ - "b inc 5 if a > 1", - "a inc 1 if b < 5", - "c dec -10 if a >= 1", - "c inc -20 if c == 10", - ] - .iter() - .map(|s| parse(s)) - .collect() - } - - #[test] - fn part1_known() { - assert_eq!(part_1(&data()), 1); - } - - #[test] - fn part2_known() { - assert_eq!(part_2(&data()), 10); - } - - #[test] - fn test_day_1() { - let input = input(); - assert_eq!(part_1(&input), 5752); - assert_eq!(part_2(&input), 6366); - } + let input = day_8::input(); + println!("part 1 => {}", day_8::part_1(&input)); + println!("part 2 => {}", day_8::part_2(&input)); }