Compare commits
26 Commits
51c64d827b
...
main
Author | SHA1 | Date | |
---|---|---|---|
50472363cf | |||
2c4288de42 | |||
14c6da72d9 | |||
bdc150d0ed | |||
b802cf1d16 | |||
9db0e3f054 | |||
a19a3258c2 | |||
d3c11dcbed | |||
e3f0f932e4 | |||
8b3bbab954 | |||
05a71ce349 | |||
6e0f7b3c2f | |||
e0655bd700 | |||
7e082e6558 | |||
4e494e5596 | |||
0645f39e51 | |||
831d97d5b4 | |||
1c4bf97e07 | |||
593851daee | |||
c3849c1ad4 | |||
4980b285e8 | |||
bece6b708b | |||
97d8acbad8 | |||
ffb43d4860 | |||
be7ca6666d | |||
39633cc659 |
237
Cargo.lock
generated
237
Cargo.lock
generated
@@ -8,8 +8,24 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aoc-runner",
|
"aoc-runner",
|
||||||
"aoc-runner-derive",
|
"aoc-runner-derive",
|
||||||
|
"array2d",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"num",
|
||||||
|
"prev-iter",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"regex",
|
||||||
"rust-crypto",
|
"rust-crypto",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -41,6 +57,24 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "array2d"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b39cb2c1bf5a7c0dd097aa95ab859cf87dab5a4328900f5388942dc1889f74"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@@ -59,6 +93,23 @@ version = "0.3.55"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -80,6 +131,100 @@ version = "0.2.151"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prev-iter"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ceacb352f798299b2044f4b9a894e8ef5d21115ccd293a323d44fc665132ec83"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.70"
|
version = "1.0.70"
|
||||||
@@ -121,6 +266,27 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -136,6 +302,15 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -145,6 +320,35 @@ dependencies = [
|
|||||||
"rand_core 0.3.1",
|
"rand_core 0.3.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-crypto"
|
name = "rust-crypto"
|
||||||
version = "0.2.36"
|
version = "0.2.36"
|
||||||
@@ -164,6 +368,12 @@ version = "0.3.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401"
|
checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
@@ -201,6 +411,25 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.25.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.41",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@@ -230,7 +459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -246,6 +475,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@@ -11,3 +11,10 @@ rust-crypto = "0.2.36"
|
|||||||
aoc-runner = "0.3.0"
|
aoc-runner = "0.3.0"
|
||||||
aoc-runner-derive = "0.3.0"
|
aoc-runner-derive = "0.3.0"
|
||||||
itertools = "0.12.0"
|
itertools = "0.12.0"
|
||||||
|
array2d = "0.3.0"
|
||||||
|
strum = "0.25.0"
|
||||||
|
strum_macros = "0.25"
|
||||||
|
prev-iter = "0.1.2"
|
||||||
|
regex = "1.10.2"
|
||||||
|
num = "0.4.1"
|
||||||
|
rand = "0.8.5"
|
||||||
|
7
old/day01/Cargo.lock
generated
7
old/day01/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day1"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day1"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,48 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input = input.split('\n');
|
|
||||||
let mut sum = 0;
|
|
||||||
for line in input {
|
|
||||||
let str_nums: Vec<(&str, &str)> = vec![("one", "1"), ("two", "2"), ("three", "3"), ("four", "4"), ("five", "5"), ("six", "6"), ("seven", "7"), ("eight", "8"), ("nine", "9")];
|
|
||||||
let mut matches: Vec<(usize, &str)> = vec![];
|
|
||||||
for str_num in str_nums {
|
|
||||||
// Get every alphabetic number in the string with it's index
|
|
||||||
let str_match: Vec<_> = line.match_indices(str_num.0).collect();
|
|
||||||
// convert the string to a numeral
|
|
||||||
let mut str_match: Vec<_> = str_match.iter().map(|x| return (x.0, str_num.1)).collect();
|
|
||||||
matches.append(&mut str_match);
|
|
||||||
}
|
|
||||||
// get the numerials from the line with their index
|
|
||||||
let mut num_matches: Vec<(usize, &str)> = line.match_indices(|x: char| x.is_numeric()).collect();
|
|
||||||
matches.append(&mut num_matches);
|
|
||||||
// sort by index
|
|
||||||
matches.sort_by(|lhs, rhs| lhs.cmp(rhs));
|
|
||||||
let num = (matches.first().unwrap().1).to_owned() + (matches.last().unwrap().1);
|
|
||||||
sum += num.parse::<i32>().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}", sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input = input.split('\n');
|
|
||||||
// let mut sum = 0;
|
|
||||||
// for line in input {
|
|
||||||
// let chars: Vec<char>= line.chars().filter(|x| x.is_numeric()).collect();
|
|
||||||
// let mut num = chars.first().unwrap().to_string();
|
|
||||||
// num += &chars.last().unwrap().to_string();
|
|
||||||
// let num: u32 = num.parse().unwrap();
|
|
||||||
// sum += num;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("{}", sum);
|
|
||||||
// }
|
|
7
old/day02/Cargo.lock
generated
7
old/day02/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day2"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day2"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,83 +0,0 @@
|
|||||||
use std::{fs, cmp::max};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<_> = input.split('\n').collect();
|
|
||||||
let mut sum = 0;
|
|
||||||
for line in input {
|
|
||||||
let split:Vec<_> = line.split(':').collect();
|
|
||||||
let rounds: Vec<_> = split.last().unwrap().split(';').collect();
|
|
||||||
let rounds: Vec<_> = rounds.iter()
|
|
||||||
.map(|x| {
|
|
||||||
x.split(',').collect::<Vec<_>>()
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let rounds: Vec<_> = rounds.iter()
|
|
||||||
.map(|x| {
|
|
||||||
x.iter().map(|x| {
|
|
||||||
x.trim().split(' ').collect::<Vec<_>>()
|
|
||||||
}).collect::<Vec<_>>()
|
|
||||||
}).collect();
|
|
||||||
let (mut r_max, mut g_max, mut b_max) = (0, 0, 0);
|
|
||||||
for round in rounds {
|
|
||||||
for set in round {
|
|
||||||
let color = set.last().unwrap();
|
|
||||||
let num: i32 = set.first().unwrap().parse().unwrap();
|
|
||||||
match *color {
|
|
||||||
"red" => r_max = max(num, r_max),
|
|
||||||
"blue" => b_max = max(num, b_max),
|
|
||||||
"green" => g_max = max(num, g_max),
|
|
||||||
&_ => todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
//println!("{}", r_max);
|
|
||||||
sum += r_max * g_max * b_max;
|
|
||||||
}
|
|
||||||
println!("{}", sum);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input: Vec<_> = input.split('\n').collect();
|
|
||||||
// let mut game_id = 0;
|
|
||||||
// let (r_max, g_max, b_max) = (12, 13, 14);
|
|
||||||
// let mut sum = 0;
|
|
||||||
// for line in input {
|
|
||||||
// game_id += 1;
|
|
||||||
// let split:Vec<_> = line.split(':').collect();
|
|
||||||
// let rounds: Vec<_> = split.last().unwrap().split(';').collect();
|
|
||||||
// let rounds: Vec<_> = rounds.iter().map(|x| x.split(',').collect::<Vec<_>>()).collect();
|
|
||||||
// let rounds: Vec<_> = rounds.iter().map(|x| x.iter().map(|x| x.trim().split(' ').collect::<Vec<_>>()).collect::<Vec<_>>()).collect();
|
|
||||||
// let mut sad = false;
|
|
||||||
|
|
||||||
// for round in rounds {
|
|
||||||
// let (mut r_cur, mut g_cur, mut b_cur) = (0, 0, 0);
|
|
||||||
// for set in round {
|
|
||||||
// let color = set.last().unwrap();
|
|
||||||
// let num: i32 = set.first().unwrap().parse().unwrap();
|
|
||||||
// match *color {
|
|
||||||
// "red" => r_cur += num,
|
|
||||||
// "blue" => b_cur += num,
|
|
||||||
// "green" => g_cur += num,
|
|
||||||
// &_ => todo!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if r_cur > r_max || b_cur > b_max || g_cur > g_max {
|
|
||||||
// sad = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if !sad {
|
|
||||||
// sum += game_id;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// println!("{}", sum);
|
|
||||||
|
|
||||||
// }
|
|
16
old/day03/Cargo.lock
generated
16
old/day03/Cargo.lock
generated
@@ -1,16 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "array2d"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "79093d31d0a9c7832c71ad74dd945b7861f721e6f242aa67be253a5eecbac937"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day3"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"array2d",
|
|
||||||
]
|
|
@@ -1,9 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day3"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
array2d = "0.3.0"
|
|
7
old/day04/Cargo.lock
generated
7
old/day04/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day4"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day4"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,99 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
|
|
||||||
// an array in this format [[card 1: [winning nums][ our nums]]]
|
|
||||||
let input: Vec<_> = input.split('\n') // split days
|
|
||||||
.map(|card| &card[(card.find(':').unwrap() + 1)..]) // remove day numbers
|
|
||||||
.map(|card| card.trim()) // trim extra whitespace
|
|
||||||
.map(|card| {
|
|
||||||
card.split('|') // split winning/own numbers
|
|
||||||
.map(|numbers| {
|
|
||||||
numbers.trim() // trim whitespace
|
|
||||||
.split(' ') // split into individual nums
|
|
||||||
.filter(|x| !x.is_empty()) // remove empty strings
|
|
||||||
.map(|x| {return x.parse::<i32>().unwrap()}) // convert to i32
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut queue = VecDeque::from((0..input.len()).collect::<Vec<usize>>());
|
|
||||||
|
|
||||||
let mut total_cards = 0;
|
|
||||||
while !queue.is_empty() {
|
|
||||||
let card_num = queue.pop_front().unwrap();
|
|
||||||
let card = input.get(card_num).unwrap();
|
|
||||||
total_cards += 1;
|
|
||||||
let mut dup_cards = 0;
|
|
||||||
let winning_nums = card.first().unwrap();
|
|
||||||
let our_nums = card.last().unwrap();
|
|
||||||
|
|
||||||
//dp would kill here, but im lazy
|
|
||||||
for num in our_nums {
|
|
||||||
if winning_nums.contains(num) {
|
|
||||||
dup_cards += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for card in (card_num + 1)..=(card_num + dup_cards) {
|
|
||||||
if card < input.len() {
|
|
||||||
queue.push_back(card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
println!("{:?}", total_cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
|
|
||||||
// // an array in this format [[card 1: [winning nums][ our nums]]]
|
|
||||||
// let input: Vec<_> = input.split('\n') // split days
|
|
||||||
// .map(|card| &card[(card.find(':').unwrap() + 1)..]) // remove day numbers
|
|
||||||
// .map(|card| card.trim()) // trim extra whitespace
|
|
||||||
// .map(|card| {
|
|
||||||
// card.split('|') // split winning/own numbers
|
|
||||||
// .map(|numbers| {
|
|
||||||
// numbers.trim() // trim whitespace
|
|
||||||
// .split(' ') // split into individual nums
|
|
||||||
// .filter(|x| !x.is_empty()) // remove empty strings
|
|
||||||
// .map(|x| {return x.parse::<i32>().unwrap()}) // convert to i32
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// })
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// })
|
|
||||||
// .collect();
|
|
||||||
|
|
||||||
// let mut total_pts = 0;
|
|
||||||
// for card in input {
|
|
||||||
// let mut card_pts = 0;
|
|
||||||
// let winning_nums = card.first().unwrap();
|
|
||||||
// let our_nums = card.last().unwrap();
|
|
||||||
|
|
||||||
// for num in our_nums {
|
|
||||||
// if winning_nums.contains(num) {
|
|
||||||
// if card_pts == 0 {
|
|
||||||
// card_pts = 1;
|
|
||||||
// } else {
|
|
||||||
// card_pts *= 2;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //println!("{}", card_pts);
|
|
||||||
// total_pts += card_pts;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// println!("{:?}", total_pts);
|
|
||||||
// }
|
|
7
old/day05/Cargo.lock
generated
7
old/day05/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day5"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day5"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,93 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let mut mappers: Vec<_> = input.split("\n\n")
|
|
||||||
.map(|maps| &maps[(maps.find(':').unwrap() + 1)..])
|
|
||||||
.map(|maps| maps.trim())
|
|
||||||
.map(|maps| {
|
|
||||||
maps.split('\n')
|
|
||||||
.map(|range| {
|
|
||||||
range.split(' ')
|
|
||||||
.map(|num| num.parse::<i64>().unwrap())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let seeds = mappers.first().unwrap().first().unwrap().clone();
|
|
||||||
let mappers: &mut [Vec<Vec<i64>>] = mappers.get_mut(1..).unwrap();
|
|
||||||
|
|
||||||
for mapper in mappers.into_iter() {
|
|
||||||
for range in mapper {
|
|
||||||
range[2] = range[1] + range[2] - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cur_vals: Vec<_> = Vec::new();
|
|
||||||
for i in 0..seeds.len() / 2 {
|
|
||||||
let end = seeds[i * 2] + seeds[(i * 2) + 1];
|
|
||||||
let mut range: Vec<_> = (seeds[i * 2]..end).collect();
|
|
||||||
cur_vals.append(&mut range);
|
|
||||||
}
|
|
||||||
println!("{}", cur_vals.len());
|
|
||||||
for mapper in mappers {
|
|
||||||
for val in cur_vals.iter_mut() {
|
|
||||||
println!("{}", val);
|
|
||||||
for range in mapper.into_iter() {
|
|
||||||
if range[1] <= *val && *val <= range[2] {
|
|
||||||
let diff = *val - range[1];
|
|
||||||
*val = range[0] + diff;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{:?}", cur_vals.into_iter().min().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let mut mappers: Vec<_> = input.split("\n\n")
|
|
||||||
// .map(|maps| &maps[(maps.find(':').unwrap() + 1)..])
|
|
||||||
// .map(|maps| maps.trim())
|
|
||||||
// .map(|maps| {
|
|
||||||
// maps.split('\n')
|
|
||||||
// .map(|range| {
|
|
||||||
// range.split(' ')
|
|
||||||
// .map(|num| num.parse::<i64>().unwrap())
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// })
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// })
|
|
||||||
// .collect();
|
|
||||||
|
|
||||||
// let seeds = mappers.first().unwrap().first().unwrap().clone();
|
|
||||||
// let mappers: &mut [Vec<Vec<i64>>] = mappers.get_mut(1..).unwrap();
|
|
||||||
|
|
||||||
// for mapper in mappers.into_iter() {
|
|
||||||
// for range in mapper {
|
|
||||||
// range[2] = range[1] + range[2] - 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut cur_vals: Vec<_> = seeds;
|
|
||||||
// for mapper in mappers {
|
|
||||||
// for val in cur_vals.iter_mut() {
|
|
||||||
// for range in mapper.into_iter() {
|
|
||||||
// if range[1] <= *val && *val <= range[2] {
|
|
||||||
// let diff = *val - range[1];
|
|
||||||
// *val = range[0] + diff;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("{:?}", cur_vals.into_iter().min().unwrap())
|
|
||||||
// }
|
|
7
old/day06/Cargo.lock
generated
7
old/day06/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day6"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day6"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,56 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<_> = input.split('\n') // Separate the Time and Distance lines
|
|
||||||
.map(|line| {
|
|
||||||
line[line.find(':').unwrap() + 1..] // Drop "Time:" and "Distance:"
|
|
||||||
.split_whitespace() // Split the numbers into their own elements
|
|
||||||
.flat_map(|s| s.chars()).collect::<String>() // Combine the strings into a single one
|
|
||||||
.parse::<i64>().expect("Couldn't parse number") // Parse numbers into i32
|
|
||||||
}).collect(); // Collect into Vec
|
|
||||||
|
|
||||||
let time = input[0];
|
|
||||||
let dist = input[1];
|
|
||||||
let mut valid = 0;
|
|
||||||
|
|
||||||
for remaining_time in 0..time {
|
|
||||||
if (time - remaining_time) * remaining_time > dist {
|
|
||||||
valid += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}", valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input: Vec<_> = input.split('\n') // Separate the Time and Distance lines
|
|
||||||
// .map(|line| {
|
|
||||||
// line[line.find(':').unwrap() + 1..] // Drop "Time:" and "Distance:"
|
|
||||||
// .split_whitespace() // Split the numbers into their own elements.
|
|
||||||
// .map(|num| num.parse::<i32>().expect("Couldn't parse number")) // Parse numbers into i32
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// }).collect(); // collect into Vec
|
|
||||||
|
|
||||||
// let mut valid_total = 1;
|
|
||||||
|
|
||||||
// for round in 0..input.first().unwrap().len() {
|
|
||||||
// let time = input[0][round];
|
|
||||||
// let dist = input[1][round];
|
|
||||||
// let mut valid = 0;
|
|
||||||
|
|
||||||
// for remaining_time in 0..time {
|
|
||||||
// if (time - remaining_time) * remaining_time > dist {
|
|
||||||
// valid += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// valid_total *= valid;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("{}", valid_total);
|
|
||||||
// }
|
|
7
old/day07/Cargo.lock
generated
7
old/day07/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day07"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day07"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,183 +0,0 @@
|
|||||||
use std::{fs, collections::HashMap};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let mut input: Vec<_> = input.split('\n')
|
|
||||||
.map(|line| line.split(' ').collect::<Vec<_>>())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for line in 0..input.len() {
|
|
||||||
let hand = input[line][0];
|
|
||||||
if hand == "JJJJJ" {
|
|
||||||
input[line].push("7");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut card_freq: HashMap<char, i16> = HashMap::new();
|
|
||||||
let joker_count: i16 = hand.chars().filter(|c| c == &'J').count().try_into().unwrap();
|
|
||||||
for card in hand.chars().filter(|c| c != &'J') {
|
|
||||||
card_freq.entry(card)
|
|
||||||
.and_modify(|count| *count += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The most helpful place for the jokers will always be with the max card count
|
|
||||||
let max = card_freq.clone().into_iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();
|
|
||||||
card_freq.entry(max.0)
|
|
||||||
.and_modify(|count| *count += joker_count);
|
|
||||||
|
|
||||||
let mut set_count: HashMap<i16, i16> = HashMap::new();
|
|
||||||
for i in 1..=5 {
|
|
||||||
let card_count = card_freq.values().filter(|x| **x == i).count().try_into().unwrap();
|
|
||||||
if card_count != 0 {
|
|
||||||
set_count.insert(i, card_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let power = match set_count {
|
|
||||||
x if x.contains_key(&5) => "7",
|
|
||||||
x if x.contains_key(&4) => "6",
|
|
||||||
x if x.contains_key(&3) && x.contains_key(&2) => "5",
|
|
||||||
x if x.contains_key(&3) => "4",
|
|
||||||
x if x.get(&2).unwrap_or(&0) >= &2 => "3",
|
|
||||||
x if x.get(&2).unwrap_or(&0) == &1 => "2",
|
|
||||||
HashMap { .. } => "1"
|
|
||||||
};
|
|
||||||
|
|
||||||
input[line].push(power);
|
|
||||||
}
|
|
||||||
|
|
||||||
input.sort_by(|lhs, rhs| {
|
|
||||||
let lhs_power: i32 = lhs[2].parse().unwrap();
|
|
||||||
let rhs_power: i32 = rhs[2].parse().unwrap();
|
|
||||||
if lhs_power != rhs_power {
|
|
||||||
return lhs_power.cmp(&rhs_power);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lhs_hand: Vec<i32> = lhs[0].chars().map(card_value).collect();
|
|
||||||
let rhs_hand: Vec<i32> = rhs[0].chars().map(card_value).collect();
|
|
||||||
for i in 0..5 {
|
|
||||||
if lhs_hand[i] == rhs_hand[i] { continue; }
|
|
||||||
return lhs_hand[i].cmp(&rhs_hand[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("Should not be reachable");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let mut total_winnings = 0;
|
|
||||||
for i in 0..input.len() {
|
|
||||||
let bid: usize = input[i][1].parse().unwrap();
|
|
||||||
total_winnings += (i + 1) * bid;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}", total_winnings);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn card_value(card: char) -> i32 {
|
|
||||||
match card {
|
|
||||||
'A' => 13,
|
|
||||||
'K' => 12,
|
|
||||||
'Q' => 11,
|
|
||||||
'T' => 10,
|
|
||||||
'9' => 9,
|
|
||||||
'8' => 8,
|
|
||||||
'7' => 7,
|
|
||||||
'6' => 6,
|
|
||||||
'5' => 5,
|
|
||||||
'4' => 4,
|
|
||||||
'3' => 3,
|
|
||||||
'2' => 2,
|
|
||||||
'J' => 1,
|
|
||||||
_ => panic!("invalid card")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use std::{fs, collections::HashMap};
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let mut input: Vec<_> = input.split('\n')
|
|
||||||
// .map(|line| line.split(' ').collect::<Vec<_>>())
|
|
||||||
// .collect();
|
|
||||||
|
|
||||||
// for line in 0..input.len() {
|
|
||||||
// let hand = input[line][0];
|
|
||||||
// let mut card_freq: HashMap<char, i16> = HashMap::new();
|
|
||||||
// for card in hand.chars() {
|
|
||||||
// card_freq.entry(card)
|
|
||||||
// .and_modify(|count| *count += 1)
|
|
||||||
// .or_insert(1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut set_count: HashMap<i16, i16> = HashMap::new();
|
|
||||||
// for i in 1..=5 {
|
|
||||||
// let card_count = card_freq.values().filter(|x| **x == i).count().try_into().unwrap();
|
|
||||||
// if card_count != 0 {
|
|
||||||
// set_count.insert(i, card_count);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let power = match set_count {
|
|
||||||
// x if x.contains_key(&5) => "7",
|
|
||||||
// x if x.contains_key(&4) => "6",
|
|
||||||
// x if x.contains_key(&3) && x.contains_key(&2) => "5",
|
|
||||||
// x if x.contains_key(&3) => "4",
|
|
||||||
// x if x.get(&2).unwrap_or(&0) >= &2 => "3",
|
|
||||||
// x if x.get(&2).unwrap_or(&0) == &1 => "2",
|
|
||||||
// HashMap { .. } => "1"
|
|
||||||
// };
|
|
||||||
|
|
||||||
// input[line].push(power);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// input.sort_by(|lhs, rhs| {
|
|
||||||
// let lhs_power: i32 = lhs[2].parse().unwrap();
|
|
||||||
// let rhs_power: i32 = rhs[2].parse().unwrap();
|
|
||||||
// if lhs_power != rhs_power {
|
|
||||||
// return lhs_power.cmp(&rhs_power);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let lhs_hand: Vec<i32> = lhs[0].chars().map(card_value).collect();
|
|
||||||
// let rhs_hand: Vec<i32> = rhs[0].chars().map(card_value).collect();
|
|
||||||
// for i in 0..5 {
|
|
||||||
// if lhs_hand[i] == rhs_hand[i] { continue; }
|
|
||||||
// return lhs_hand[i].cmp(&rhs_hand[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// panic!("Should not be reachable");
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
// let mut total_winnings = 0;
|
|
||||||
// for i in 0..input.len() {
|
|
||||||
// let bid: usize = input[i][1].parse().unwrap();
|
|
||||||
// total_winnings += (i + 1) * bid;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("{}", total_winnings);
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// fn card_value(card: char) -> i32 {
|
|
||||||
// match card {
|
|
||||||
// 'A' => 13,
|
|
||||||
// 'K' => 12,
|
|
||||||
// 'Q' => 11,
|
|
||||||
// 'J' => 10,
|
|
||||||
// 'T' => 9,
|
|
||||||
// '9' => 8,
|
|
||||||
// '8' => 7,
|
|
||||||
// '7' => 6,
|
|
||||||
// '6' => 5,
|
|
||||||
// '5' => 4,
|
|
||||||
// '4' => 3,
|
|
||||||
// '3' => 2,
|
|
||||||
// '2' => 1,
|
|
||||||
// _ => panic!("invalid card")
|
|
||||||
// }
|
|
||||||
// }
|
|
7
old/day08/Cargo.lock
generated
7
old/day08/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day08"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day08"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,122 +0,0 @@
|
|||||||
use std::{fs, collections::{HashMap, VecDeque}};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<_> = input.split("\n\n").collect();
|
|
||||||
let (directions, nodes) = (input[0], input[1]);
|
|
||||||
let directions: Vec<_> = directions.chars().map(|char| {
|
|
||||||
match char {
|
|
||||||
'L' => Direction::Left,
|
|
||||||
'R' => Direction::Right,
|
|
||||||
_ => panic!("Invalid direction!")
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let nodes: HashMap<&str, (&str, &str)> = nodes.split('\n')
|
|
||||||
.map(|line| {
|
|
||||||
let line = line.split('=').map(|x| x.trim()).collect::<Vec<_>>();
|
|
||||||
let children: Vec<_> = line[1].trim_matches(|c| c == '(' || c == ')').split(", ").collect();
|
|
||||||
(line[0], (children[0], children[1]))
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let starts: Vec<_> = nodes.keys().filter(|x| x.ends_with('A')).collect();
|
|
||||||
let dists: Vec<_> = starts.iter().map(|start| dist(&start, &directions, &nodes)).collect();
|
|
||||||
|
|
||||||
let gcf = gcf(&dists);
|
|
||||||
|
|
||||||
let step_count = gcf * dists.iter().map(|value| value / gcf).product::<i64>();
|
|
||||||
|
|
||||||
println!("{:?}", step_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gcf(values: &Vec<i64>) -> i64 {
|
|
||||||
let mut gcf = values[0];
|
|
||||||
|
|
||||||
for val in values {
|
|
||||||
gcf = find_gcf(gcf, *val);
|
|
||||||
|
|
||||||
if gcf == 1 {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gcf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_gcf(a: i64, b: i64) -> i64 {
|
|
||||||
if a == 0 {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_gcf(b % a, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dist(cur_node: &str, directions: &Vec<Direction>, nodes: &HashMap<&str, (&str, &str)>) -> i64 {
|
|
||||||
let mut cur_node = cur_node;
|
|
||||||
let mut step_queue: VecDeque<Direction> = VecDeque::from(directions.clone());
|
|
||||||
let mut step_count = 0;
|
|
||||||
|
|
||||||
while !cur_node.ends_with('Z') {
|
|
||||||
step_count += 1;
|
|
||||||
let cur_step = step_queue.pop_front().unwrap();
|
|
||||||
match cur_step {
|
|
||||||
Direction::Left => cur_node = nodes[cur_node].0,
|
|
||||||
Direction::Right => cur_node = nodes[cur_node].1,
|
|
||||||
}
|
|
||||||
step_queue.push_back(cur_step);
|
|
||||||
}
|
|
||||||
|
|
||||||
return step_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Direction {
|
|
||||||
Left,
|
|
||||||
Right
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use std::{fs, collections::{HashMap, VecDeque}};
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input: Vec<_> = input.split("\n\n").collect();
|
|
||||||
// let (directions, nodes) = (input[0], input[1]);
|
|
||||||
// let directions: Vec<_> = directions.chars().map(|char| {
|
|
||||||
// match char {
|
|
||||||
// 'L' => Direction::Left,
|
|
||||||
// 'R' => Direction::Right,
|
|
||||||
// _ => panic!("Invalid direction!")
|
|
||||||
// }
|
|
||||||
// }).collect();
|
|
||||||
|
|
||||||
// let nodes: HashMap<&str, (&str, &str)> = nodes.split('\n')
|
|
||||||
// .map(|line| {
|
|
||||||
// let line = line.split('=').map(|x| x.trim()).collect::<Vec<_>>();
|
|
||||||
// let children: Vec<_> = line[1].trim_matches(|c| c == '(' || c == ')').split(", ").collect();
|
|
||||||
// (line[0], (children[0], children[1]))
|
|
||||||
// }).collect();
|
|
||||||
|
|
||||||
// let mut cur_node = "AAA";
|
|
||||||
// let mut step_queue = VecDeque::from(directions);
|
|
||||||
// let mut step_count = 0;
|
|
||||||
|
|
||||||
// while cur_node != "ZZZ" {
|
|
||||||
// step_count += 1;
|
|
||||||
// let cur_step = step_queue.pop_front().unwrap();
|
|
||||||
// match cur_step {
|
|
||||||
// Direction::Left => cur_node = nodes[cur_node].0,
|
|
||||||
// Direction::Right => cur_node = nodes[cur_node].1,
|
|
||||||
// }
|
|
||||||
// step_queue.push_back(cur_step);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("{:?}", step_count);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// enum Direction {
|
|
||||||
// Left,
|
|
||||||
// Right
|
|
||||||
// }
|
|
7
old/day09/Cargo.lock
generated
7
old/day09/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day09"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day09"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,70 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<_> = input.split('\n')
|
|
||||||
.map(|line| {
|
|
||||||
line.split_whitespace()
|
|
||||||
.map(|num| num.parse::<i64>().unwrap())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let mut total = 0;
|
|
||||||
|
|
||||||
for line in input {
|
|
||||||
let mut line_starters: Vec<i64> = vec![*line.first().unwrap()];
|
|
||||||
let mut cur_line: Vec<i64> = line;
|
|
||||||
while !cur_line.iter().all(|x| *x == 0) {
|
|
||||||
let mut next_line: Vec<i64> = vec![];
|
|
||||||
for i in 1..cur_line.len() {
|
|
||||||
let diff = cur_line[i] - cur_line[i-1];
|
|
||||||
next_line.push(diff)
|
|
||||||
}
|
|
||||||
line_starters.push(*next_line.first().unwrap());
|
|
||||||
cur_line = next_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut sum = 0;
|
|
||||||
for i in (1..line_starters.len()).rev() {
|
|
||||||
sum = line_starters[i - 1] - sum;
|
|
||||||
}
|
|
||||||
total += sum;
|
|
||||||
}
|
|
||||||
println!("{:?}", total)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input: Vec<_> = input.split('\n')
|
|
||||||
// .map(|line| {
|
|
||||||
// line.split_whitespace()
|
|
||||||
// .map(|num| num.parse::<i64>().unwrap())
|
|
||||||
// .collect::<Vec<_>>()
|
|
||||||
// }).collect();
|
|
||||||
|
|
||||||
// let mut total = 0;
|
|
||||||
|
|
||||||
// for line in input {
|
|
||||||
// let mut line_enders: Vec<i64> = vec![*line.last().unwrap()];
|
|
||||||
// let mut cur_line: Vec<i64> = line;
|
|
||||||
// while !cur_line.iter().all(|x| *x == 0) {
|
|
||||||
// let mut next_line: Vec<i64> = vec![];
|
|
||||||
// for i in 1..cur_line.len() {
|
|
||||||
// let diff = cur_line[i] - cur_line[i-1];
|
|
||||||
// next_line.push(diff)
|
|
||||||
// }
|
|
||||||
// line_enders.push(*next_line.last().unwrap());
|
|
||||||
// cur_line = next_line;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut sum = 0;
|
|
||||||
// for i in (1..line_enders.len()).rev() {
|
|
||||||
// sum = sum + line_enders[i - 1];
|
|
||||||
// }
|
|
||||||
// total += sum;
|
|
||||||
// }
|
|
||||||
// println!("{:?}", total)
|
|
||||||
// }
|
|
77
old/day10/Cargo.lock
generated
77
old/day10/Cargo.lock
generated
@@ -1,77 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day10"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"strum",
|
|
||||||
"strum_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.70"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.33"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum"
|
|
||||||
version = "0.25.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum_macros"
|
|
||||||
version = "0.25.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustversion",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
@@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day10"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
strum = "0.25.0"
|
|
||||||
strum_macros = "0.25"
|
|
@@ -1,234 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use strum_macros::EnumIter;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input = parse_input(&input);
|
|
||||||
let start = find_start(&input);
|
|
||||||
|
|
||||||
let mut start_dirs: Vec<Direction> = vec![];
|
|
||||||
for dir in Direction::iter().filter(|x| x != &Direction::Start) {
|
|
||||||
let (x, y) = ((start.0 as i32 + dir.to_ind().1), (start.1 as i32 + dir.to_ind().0));
|
|
||||||
if x < 0 || y < 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let neibhor = &input[x as usize][y as usize];
|
|
||||||
if neibhor.contains(&dir.reverse()) {
|
|
||||||
start_dirs.push(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut finished = false;
|
|
||||||
|
|
||||||
let mut pre = start_dirs[0].reverse();
|
|
||||||
let mut pos = ((start.0 as i32 + start_dirs[0].to_ind().1) as usize, (start.1 as i32 + start_dirs[0].to_ind().0) as usize);
|
|
||||||
|
|
||||||
let mut the_loop = vec![start, pos];
|
|
||||||
let mut area = 0;
|
|
||||||
|
|
||||||
while !finished {
|
|
||||||
let first_next = &input[pos.0][pos.1].iter().filter(|x| x != &&pre).next().unwrap();
|
|
||||||
pos = ((pos.0 as i32 + first_next.to_ind().1) as usize, (pos.1 as i32 + first_next.to_ind().0) as usize);
|
|
||||||
pre = first_next.reverse();
|
|
||||||
|
|
||||||
finished = pos == start;
|
|
||||||
the_loop.push(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for win in the_loop.windows(2) {
|
|
||||||
area += (win[0].1 * win[1].0) as i64;
|
|
||||||
area -= (win[0].0 * win[1].1) as i64;
|
|
||||||
}
|
|
||||||
let area = i64::abs(area) / 2;
|
|
||||||
|
|
||||||
let spaces = area - (the_loop.len() as i64 / 2) + 1;
|
|
||||||
|
|
||||||
println!("{}", spaces)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_input(input: &String) -> Vec<Vec<Vec<Direction>>> {
|
|
||||||
let input: Vec<_> = input.split('\n')
|
|
||||||
.map(|line| {
|
|
||||||
line.chars()
|
|
||||||
.map(|char| {
|
|
||||||
match char {
|
|
||||||
'|' => vec![Direction::North, Direction::South],
|
|
||||||
'-' => vec![Direction::East, Direction::West],
|
|
||||||
'L' => vec![Direction::North, Direction::East],
|
|
||||||
'J' => vec![Direction::North, Direction::West],
|
|
||||||
'7' => vec![Direction::South, Direction::West],
|
|
||||||
'F' => vec![Direction::South, Direction::East],
|
|
||||||
'.' => vec![],
|
|
||||||
'S' => vec![Direction::Start],
|
|
||||||
_ => panic!("Invalid pipe char")
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>()
|
|
||||||
}).collect();
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_start(input: &Vec<Vec<Vec<Direction>>>) -> (usize, usize) {
|
|
||||||
let mut start_point: Option<(usize, usize)> = None;
|
|
||||||
for i in 0..input.len() {
|
|
||||||
for j in 0..input[0].len() {
|
|
||||||
if input[i][j].contains(&Direction::Start) {
|
|
||||||
start_point = Some((i,j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match start_point {
|
|
||||||
Some(x) => x,
|
|
||||||
None => panic!("No start point found! AHHHHH")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, EnumIter)]
|
|
||||||
enum Direction {
|
|
||||||
North,
|
|
||||||
South,
|
|
||||||
East,
|
|
||||||
West,
|
|
||||||
Start
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
pub fn to_ind(&self) -> (i32, i32) {
|
|
||||||
match self {
|
|
||||||
Direction::North => (0,-1),
|
|
||||||
Direction::South => (0,1),
|
|
||||||
Direction::East => (1,0),
|
|
||||||
Direction::West => (-1,0),
|
|
||||||
Direction::Start => panic!("Start should never be converted to an index. AHH"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reverse(&self) -> Direction {
|
|
||||||
match self {
|
|
||||||
Direction::North => Direction::South,
|
|
||||||
Direction::South => Direction::North,
|
|
||||||
Direction::East => Direction::West,
|
|
||||||
Direction::West => Direction::East,
|
|
||||||
Direction::Start => panic!("Start should never be reversed. AHH"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
// use strum::IntoEnumIterator;
|
|
||||||
// use strum_macros::EnumIter;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input = parse_input(&input);
|
|
||||||
// let start = find_start(&input);
|
|
||||||
|
|
||||||
// let mut start_dirs: Vec<Direction> = vec![];
|
|
||||||
// for dir in Direction::iter().filter(|x| x != &Direction::Start) {
|
|
||||||
// let (x, y) = ((start.0 as i32 + dir.to_ind().1), (start.1 as i32 + dir.to_ind().0));
|
|
||||||
// if x < 0 || y < 0 {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// let neibhor = &input[x as usize][y as usize];
|
|
||||||
// if neibhor.contains(&dir.reverse()) {
|
|
||||||
// start_dirs.push(dir);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut finished = false;
|
|
||||||
// let mut count = 1;
|
|
||||||
|
|
||||||
// let mut first_pre = start_dirs[0].reverse();
|
|
||||||
// let mut second_pre = start_dirs[1].reverse();
|
|
||||||
// let mut first_pos = ((start.0 as i32 + start_dirs[0].to_ind().1) as usize, (start.1 as i32 + start_dirs[0].to_ind().0) as usize);
|
|
||||||
// let mut second_pos = ((start.0 as i32 + start_dirs[1].to_ind().1) as usize, (start.1 as i32 + start_dirs[1].to_ind().0) as usize);
|
|
||||||
|
|
||||||
// while !finished {
|
|
||||||
// let first_next = &input[first_pos.0][first_pos.1].iter().filter(|x| x != &&first_pre).next().unwrap();
|
|
||||||
// first_pos = ((first_pos.0 as i32 + first_next.to_ind().1) as usize, (first_pos.1 as i32 + first_next.to_ind().0) as usize);
|
|
||||||
// first_pre = first_next.reverse();
|
|
||||||
|
|
||||||
// let second_next = &input[second_pos.0][second_pos.1].iter().filter(|x| x != &&second_pre).next().unwrap();
|
|
||||||
// second_pos = ((second_pos.0 as i32 + second_next.to_ind().1) as usize, (second_pos.1 as i32 + second_next.to_ind().0) as usize);
|
|
||||||
// second_pre = second_next.reverse();
|
|
||||||
|
|
||||||
// count += 1;
|
|
||||||
// finished = first_pos == second_pos;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// println!("{:?}", count)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn parse_input(input: &String) -> Vec<Vec<Vec<Direction>>> {
|
|
||||||
// let input: Vec<_> = input.split('\n')
|
|
||||||
// .map(|line| {
|
|
||||||
// line.chars()
|
|
||||||
// .map(|char| {
|
|
||||||
// match char {
|
|
||||||
// '|' => vec![Direction::North, Direction::South],
|
|
||||||
// '-' => vec![Direction::East, Direction::West],
|
|
||||||
// 'L' => vec![Direction::North, Direction::East],
|
|
||||||
// 'J' => vec![Direction::North, Direction::West],
|
|
||||||
// '7' => vec![Direction::South, Direction::West],
|
|
||||||
// 'F' => vec![Direction::South, Direction::East],
|
|
||||||
// '.' => vec![],
|
|
||||||
// 'S' => vec![Direction::Start],
|
|
||||||
// _ => panic!("Invalid pipe char")
|
|
||||||
// }
|
|
||||||
// }).collect::<Vec<_>>()
|
|
||||||
// }).collect();
|
|
||||||
// return input;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn find_start(input: &Vec<Vec<Vec<Direction>>>) -> (usize, usize) {
|
|
||||||
// let mut start_point: Option<(usize, usize)> = None;
|
|
||||||
// for i in 0..input.len() {
|
|
||||||
// for j in 0..input[0].len() {
|
|
||||||
// if input[i][j].contains(&Direction::Start) {
|
|
||||||
// start_point = Some((i,j));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match start_point {
|
|
||||||
// Some(x) => x,
|
|
||||||
// None => panic!("No start point found! AHHHHH")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Debug, PartialEq, EnumIter)]
|
|
||||||
// enum Direction {
|
|
||||||
// North,
|
|
||||||
// South,
|
|
||||||
// East,
|
|
||||||
// West,
|
|
||||||
// Start
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl Direction {
|
|
||||||
// pub fn to_ind(&self) -> (i32, i32) {
|
|
||||||
// match self {
|
|
||||||
// Direction::North => (0,-1),
|
|
||||||
// Direction::South => (0,1),
|
|
||||||
// Direction::East => (1,0),
|
|
||||||
// Direction::West => (-1,0),
|
|
||||||
// Direction::Start => panic!("Start should never be converted to an index. AHH"),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn reverse(&self) -> Direction {
|
|
||||||
// match self {
|
|
||||||
// Direction::North => Direction::South,
|
|
||||||
// Direction::South => Direction::North,
|
|
||||||
// Direction::East => Direction::West,
|
|
||||||
// Direction::West => Direction::East,
|
|
||||||
// Direction::Start => panic!("Start should never be reversed. AHH"),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
7
old/day11/Cargo.lock
generated
7
old/day11/Cargo.lock
generated
@@ -1,7 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day11"
|
|
||||||
version = "0.1.0"
|
|
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day11"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@@ -1,121 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<_> = input.split('\n')
|
|
||||||
.map(|x| x.chars().collect::<Vec<char>>())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut y_expand: Vec<i32> = vec![];
|
|
||||||
for line in &input {
|
|
||||||
if line.iter().all(|char| char == &'.') {
|
|
||||||
y_expand.push(1);
|
|
||||||
} else {
|
|
||||||
y_expand.push(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut x_expand: Vec<i32> = vec![];
|
|
||||||
for j in 0..input[0].len() {
|
|
||||||
let mut is_empty = true;
|
|
||||||
for i in 0..input.len() {
|
|
||||||
if input[i][j] != '.' {
|
|
||||||
is_empty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if is_empty {
|
|
||||||
x_expand.push(1);
|
|
||||||
} else {
|
|
||||||
x_expand.push(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// println!("{:?}", x_expand);
|
|
||||||
|
|
||||||
let mut galaxies: Vec<(i64, i64)> = vec![];
|
|
||||||
|
|
||||||
let mut y_offset: i64 = 0;
|
|
||||||
|
|
||||||
for (i, line) in input.iter().enumerate() {
|
|
||||||
if y_expand[i] == 1 {
|
|
||||||
y_offset += 1000000 - 1;
|
|
||||||
}
|
|
||||||
let mut x_offset: i64 = 0;
|
|
||||||
|
|
||||||
for (j, char) in line.iter().enumerate() {
|
|
||||||
if x_expand[j] == 1 {
|
|
||||||
x_offset += 1000000 - 1;
|
|
||||||
}
|
|
||||||
if char == &'#' {
|
|
||||||
galaxies.push((i as i64 + y_offset, j as i64 + x_offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let dist_total = galaxies.clone().into_iter().enumerate()
|
|
||||||
.fold(0, |mut acc, (i, gal)| {
|
|
||||||
for next in &galaxies[i + 1..] {
|
|
||||||
acc += gal.0.abs_diff(next.0) + gal.1.abs_diff(next.1);
|
|
||||||
}
|
|
||||||
acc
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("{:?}", dist_total);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// use std::fs;
|
|
||||||
|
|
||||||
// fn main() {
|
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
// let input: Vec<_> = input.split('\n')
|
|
||||||
// .map(|x| x.chars().collect::<Vec<char>>())
|
|
||||||
// .collect();
|
|
||||||
|
|
||||||
// let mut rotate: Vec<Vec<char>> = vec![];
|
|
||||||
// for j in 0..input[0].len() {
|
|
||||||
// let mut tmp: Vec<char> = vec![];
|
|
||||||
// for i in 0..input.len() {
|
|
||||||
// tmp.push(input[i][j]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if tmp.iter().all(|x| x == &'.') {
|
|
||||||
// rotate.push(tmp.clone());
|
|
||||||
// }
|
|
||||||
// rotate.push(tmp);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// let mut expanded: Vec<Vec<char>> = vec![];
|
|
||||||
// for j in 0..rotate[0].len() {
|
|
||||||
// let mut tmp: Vec<char> = vec![];
|
|
||||||
// for i in 0..rotate.len() {
|
|
||||||
// tmp.push(rotate[i][j]);
|
|
||||||
// }
|
|
||||||
// if tmp.iter().all(|x| x == &'.') {
|
|
||||||
// expanded.push(tmp.clone());
|
|
||||||
// }
|
|
||||||
// expanded.push(tmp);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut galaxies: Vec<(i32, i32)> = vec![];
|
|
||||||
|
|
||||||
// for (i, line) in expanded.iter().enumerate() {
|
|
||||||
// for (j, char) in line.iter().enumerate() {
|
|
||||||
// if char == &'#' {
|
|
||||||
// galaxies.push((i as i32,j as i32));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let dist_total = galaxies.clone().into_iter().enumerate()
|
|
||||||
// .fold(0, |mut acc, (i, gal)| {
|
|
||||||
// for next in &galaxies[i + 1..] {
|
|
||||||
// acc += gal.0.abs_diff(next.0) + gal.1.abs_diff(next.1);
|
|
||||||
// }
|
|
||||||
// acc
|
|
||||||
// });
|
|
||||||
|
|
||||||
// println!("{:?}", dist_total);
|
|
||||||
|
|
||||||
// }
|
|
25
old/day12/Cargo.lock
generated
25
old/day12/Cargo.lock
generated
@@ -1,25 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "day12"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
@@ -1,9 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "day12"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itertools = "0.12.0"
|
|
@@ -1,54 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = parse_input();
|
|
||||||
|
|
||||||
for (springs, groups) in input {
|
|
||||||
println!("{:?} {:?}", springs, groups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_input() -> Vec<(Vec<Condition>, Vec<i32>)> {
|
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
|
||||||
let input: Vec<(Vec<Condition>, Vec<i32>)> = input.split('\n')
|
|
||||||
.map(|line| {
|
|
||||||
line.split(' ')
|
|
||||||
.collect_tuple()
|
|
||||||
.unwrap()
|
|
||||||
}).map(|(springs, groups)| {
|
|
||||||
let springs: Vec<Condition> = springs.chars()
|
|
||||||
.map(|char| {
|
|
||||||
char.to_condition()
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let groups: Vec<_> = groups.split(',')
|
|
||||||
.map(|num| num.parse::<i32>().expect("Failed to parse group len"))
|
|
||||||
.collect();
|
|
||||||
(springs, groups)
|
|
||||||
}).collect();
|
|
||||||
input
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Condition {
|
|
||||||
Good,
|
|
||||||
Bad,
|
|
||||||
WhoKnows
|
|
||||||
}
|
|
||||||
|
|
||||||
trait ConditionConvertable {
|
|
||||||
fn to_condition(self) -> Condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConditionConvertable for char {
|
|
||||||
fn to_condition(self) -> Condition {
|
|
||||||
match self {
|
|
||||||
'.' => Condition::Good,
|
|
||||||
'#' => Condition::Bad,
|
|
||||||
'?' => Condition::WhoKnows,
|
|
||||||
_ => panic!("Invalid spring char AHHH")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
190
src/day10.rs
Normal file
190
src/day10.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
#[aoc_generator(day10)]
|
||||||
|
fn parse(input: &str) -> ((usize, usize), Vec<Vec<Vec<Direction>>>) {
|
||||||
|
let input: Vec<_> = input.split('\n')
|
||||||
|
.map(|line| {
|
||||||
|
line.chars()
|
||||||
|
.map(|char| {
|
||||||
|
match char {
|
||||||
|
'|' => vec![Direction::North, Direction::South],
|
||||||
|
'-' => vec![Direction::East, Direction::West],
|
||||||
|
'L' => vec![Direction::North, Direction::East],
|
||||||
|
'J' => vec![Direction::North, Direction::West],
|
||||||
|
'7' => vec![Direction::South, Direction::West],
|
||||||
|
'F' => vec![Direction::South, Direction::East],
|
||||||
|
'.' => vec![],
|
||||||
|
'S' => vec![Direction::Start],
|
||||||
|
_ => panic!("Invalid pipe char")
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let start = find_start(&input);
|
||||||
|
|
||||||
|
(start, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_start(input: &Vec<Vec<Vec<Direction>>>) -> (usize, usize) {
|
||||||
|
let mut start_point: Option<(usize, usize)> = None;
|
||||||
|
for i in 0..input.len() {
|
||||||
|
for j in 0..input[0].len() {
|
||||||
|
if input[i][j].contains(&Direction::Start) {
|
||||||
|
start_point = Some((i,j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match start_point {
|
||||||
|
Some(x) => x,
|
||||||
|
None => panic!("No start point found! AHHHHH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part1)]
|
||||||
|
fn part1((start, input): &((usize, usize), Vec<Vec<Vec<Direction>>>)) -> i32 {
|
||||||
|
let mut start_dirs: Vec<Direction> = vec![];
|
||||||
|
for dir in Direction::iter().filter(|x| x != &Direction::Start) {
|
||||||
|
let (x, y) = ((start.0 as i32 + dir.to_ind().1), (start.1 as i32 + dir.to_ind().0));
|
||||||
|
if x < 0 || y < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let neibhor = &input[x as usize][y as usize];
|
||||||
|
if neibhor.contains(&dir.reverse()) {
|
||||||
|
start_dirs.push(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut finished = false;
|
||||||
|
let mut count = 1;
|
||||||
|
|
||||||
|
let mut first_pre = start_dirs[0].reverse();
|
||||||
|
let mut second_pre = start_dirs[1].reverse();
|
||||||
|
let mut first_pos = ((start.0 as i32 + start_dirs[0].to_ind().1) as usize, (start.1 as i32 + start_dirs[0].to_ind().0) as usize);
|
||||||
|
let mut second_pos = ((start.0 as i32 + start_dirs[1].to_ind().1) as usize, (start.1 as i32 + start_dirs[1].to_ind().0) as usize);
|
||||||
|
|
||||||
|
while !finished {
|
||||||
|
let first_next = &input[first_pos.0][first_pos.1].iter().filter(|x| x != &&first_pre).next().unwrap();
|
||||||
|
first_pos = ((first_pos.0 as i32 + first_next.to_ind().1) as usize, (first_pos.1 as i32 + first_next.to_ind().0) as usize);
|
||||||
|
first_pre = first_next.reverse();
|
||||||
|
|
||||||
|
let second_next = &input[second_pos.0][second_pos.1].iter().filter(|x| x != &&second_pre).next().unwrap();
|
||||||
|
second_pos = ((second_pos.0 as i32 + second_next.to_ind().1) as usize, (second_pos.1 as i32 + second_next.to_ind().0) as usize);
|
||||||
|
second_pre = second_next.reverse();
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
finished = first_pos == second_pos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part2)]
|
||||||
|
fn part2((start, input): &((usize, usize), Vec<Vec<Vec<Direction>>>)) -> i64 {
|
||||||
|
let mut start_dirs: Vec<Direction> = vec![];
|
||||||
|
for dir in Direction::iter().filter(|x| x != &Direction::Start) {
|
||||||
|
let (x, y) = ((start.0 as i32 + dir.to_ind().1), (start.1 as i32 + dir.to_ind().0));
|
||||||
|
if x < 0 || y < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let neibhor = &input[x as usize][y as usize];
|
||||||
|
if neibhor.contains(&dir.reverse()) {
|
||||||
|
start_dirs.push(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut finished = false;
|
||||||
|
|
||||||
|
let mut pre = start_dirs[0].reverse();
|
||||||
|
let mut pos = ((start.0 as i32 + start_dirs[0].to_ind().1) as usize, (start.1 as i32 + start_dirs[0].to_ind().0) as usize);
|
||||||
|
|
||||||
|
let mut the_loop = vec![*start, pos];
|
||||||
|
let mut area = 0;
|
||||||
|
|
||||||
|
while !finished {
|
||||||
|
let first_next = &input[pos.0][pos.1].iter().filter(|x| x != &&pre).next().unwrap();
|
||||||
|
pos = ((pos.0 as i32 + first_next.to_ind().1) as usize, (pos.1 as i32 + first_next.to_ind().0) as usize);
|
||||||
|
pre = first_next.reverse();
|
||||||
|
|
||||||
|
finished = pos == *start;
|
||||||
|
the_loop.push(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for win in the_loop.windows(2) {
|
||||||
|
area += (win[0].1 * win[1].0) as i64;
|
||||||
|
area -= (win[0].0 * win[1].1) as i64;
|
||||||
|
}
|
||||||
|
let area = i64::abs(area) / 2;
|
||||||
|
|
||||||
|
let spaces = area - (the_loop.len() as i64 / 2) + 1;
|
||||||
|
|
||||||
|
spaces
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, EnumIter)]
|
||||||
|
enum Direction {
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
East,
|
||||||
|
West,
|
||||||
|
Start
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
pub fn to_ind(&self) -> (i32, i32) {
|
||||||
|
match self {
|
||||||
|
Direction::North => (0,-1),
|
||||||
|
Direction::South => (0,1),
|
||||||
|
Direction::East => (1,0),
|
||||||
|
Direction::West => (-1,0),
|
||||||
|
Direction::Start => panic!("Start should never be converted to an index. AHH"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reverse(&self) -> Direction {
|
||||||
|
match self {
|
||||||
|
Direction::North => Direction::South,
|
||||||
|
Direction::South => Direction::North,
|
||||||
|
Direction::East => Direction::West,
|
||||||
|
Direction::West => Direction::East,
|
||||||
|
Direction::Start => panic!("Start should never be reversed. AHH"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"7-F7-
|
||||||
|
.FJ|7
|
||||||
|
SJLL7
|
||||||
|
|F--J
|
||||||
|
LJ.LJ";
|
||||||
|
|
||||||
|
const EX_2: &str = r".F----7F7F7F7F-7....
|
||||||
|
.|F--7||||||||FJ....
|
||||||
|
.||.FJ||||||||L7....
|
||||||
|
FJL7L7LJLJ||LJ.L-7..
|
||||||
|
L--J.L7...LJS7F-7L7.
|
||||||
|
....F-J..F7FJ|L7L7L7
|
||||||
|
....L7.F7||L7|.L7L7|
|
||||||
|
.....|FJLJ|FJ|F7|.LJ
|
||||||
|
....FJL-7.||.||||...
|
||||||
|
....L---J.LJ.LJLJ...";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX_2)), 8);
|
||||||
|
}
|
||||||
|
}
|
144
src/day11.rs
Normal file
144
src/day11.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day11)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<char>> {
|
||||||
|
let input: Vec<_> = input.split('\n')
|
||||||
|
.map(|x| x.chars().collect::<Vec<char>>())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day11, part1)]
|
||||||
|
fn part1(input: &Vec<Vec<char>>) -> u32 {
|
||||||
|
let mut rotate: Vec<Vec<char>> = vec![];
|
||||||
|
for j in 0..input[0].len() {
|
||||||
|
let mut tmp: Vec<char> = vec![];
|
||||||
|
for i in 0..input.len() {
|
||||||
|
tmp.push(input[i][j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmp.iter().all(|x| x == &'.') {
|
||||||
|
rotate.push(tmp.clone());
|
||||||
|
}
|
||||||
|
rotate.push(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let mut expanded: Vec<Vec<char>> = vec![];
|
||||||
|
for j in 0..rotate[0].len() {
|
||||||
|
let mut tmp: Vec<char> = vec![];
|
||||||
|
for i in 0..rotate.len() {
|
||||||
|
tmp.push(rotate[i][j]);
|
||||||
|
}
|
||||||
|
if tmp.iter().all(|x| x == &'.') {
|
||||||
|
expanded.push(tmp.clone());
|
||||||
|
}
|
||||||
|
expanded.push(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut galaxies: Vec<(i32, i32)> = vec![];
|
||||||
|
|
||||||
|
for (i, line) in expanded.iter().enumerate() {
|
||||||
|
for (j, char) in line.iter().enumerate() {
|
||||||
|
if char == &'#' {
|
||||||
|
galaxies.push((i as i32,j as i32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dist_total = galaxies.clone().into_iter().enumerate()
|
||||||
|
.fold(0, |mut acc, (i, gal)| {
|
||||||
|
for next in &galaxies[i + 1..] {
|
||||||
|
acc += gal.0.abs_diff(next.0) + gal.1.abs_diff(next.1);
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
|
||||||
|
dist_total
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day11, part2)]
|
||||||
|
fn part2(input: &Vec<Vec<char>>) -> u64 {
|
||||||
|
let mut y_expand: Vec<i32> = vec![];
|
||||||
|
for line in input {
|
||||||
|
if line.iter().all(|char| char == &'.') {
|
||||||
|
y_expand.push(1);
|
||||||
|
} else {
|
||||||
|
y_expand.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut x_expand: Vec<i32> = vec![];
|
||||||
|
for j in 0..input[0].len() {
|
||||||
|
let mut is_empty = true;
|
||||||
|
for i in 0..input.len() {
|
||||||
|
if input[i][j] != '.' {
|
||||||
|
is_empty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_empty {
|
||||||
|
x_expand.push(1);
|
||||||
|
} else {
|
||||||
|
x_expand.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("{:?}", x_expand);
|
||||||
|
|
||||||
|
let mut galaxies: Vec<(i64, i64)> = vec![];
|
||||||
|
|
||||||
|
let mut y_offset: i64 = 0;
|
||||||
|
|
||||||
|
for (i, line) in input.iter().enumerate() {
|
||||||
|
if y_expand[i] == 1 {
|
||||||
|
y_offset += 1000000 - 1;
|
||||||
|
}
|
||||||
|
let mut x_offset: i64 = 0;
|
||||||
|
|
||||||
|
for (j, char) in line.iter().enumerate() {
|
||||||
|
if x_expand[j] == 1 {
|
||||||
|
x_offset += 1000000 - 1;
|
||||||
|
}
|
||||||
|
if char == &'#' {
|
||||||
|
galaxies.push((i as i64 + y_offset, j as i64 + x_offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dist_total = galaxies.clone().into_iter().enumerate()
|
||||||
|
.fold(0, |mut acc, (i, gal)| {
|
||||||
|
for next in &galaxies[i + 1..] {
|
||||||
|
acc += gal.0.abs_diff(next.0) + gal.1.abs_diff(next.1);
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
|
||||||
|
dist_total
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 374);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn part2_example() {
|
||||||
|
// assert_eq!(part2(&parse(EX)), "<RESULT>");
|
||||||
|
// }
|
||||||
|
}
|
155
src/day12.rs
Normal file
155
src/day12.rs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
#[aoc_generator(day12)]
|
||||||
|
fn parse(input: &str) -> Vec<(Vec<Condition>, Vec<usize>)> {
|
||||||
|
let input: Vec<(Vec<Condition>, Vec<usize>)> = input.split('\n')
|
||||||
|
.map(|line| {
|
||||||
|
line.split(' ')
|
||||||
|
.collect_tuple()
|
||||||
|
.unwrap()
|
||||||
|
}).map(|(springs, groups)| {
|
||||||
|
let springs: Vec<Condition> = springs.chars()
|
||||||
|
.map(|char| {
|
||||||
|
char.to_condition()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let groups: Vec<_> = groups.split(',')
|
||||||
|
.map(|num| num.parse::<usize>().expect("Failed to parse group len"))
|
||||||
|
.collect();
|
||||||
|
(springs, groups)
|
||||||
|
}).collect();
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn possible_count_cache(springs: &[Condition], groups: &[usize], cache: &mut HashMap<(usize, usize), usize>) -> usize {
|
||||||
|
if let Some(count) = cache.get(&(springs.len(), groups.len())) {
|
||||||
|
return *count;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
if groups.is_empty() {
|
||||||
|
count = if springs.contains(&Condition::Bad) { 0 } else { 1 };
|
||||||
|
cache.insert((springs.len(), groups.len()), count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..springs.len() {
|
||||||
|
if springs[0..i].contains(&Condition::Bad) || i + groups[0] > springs.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if springs[i..i + groups[0]].contains(&Condition::Good) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if groups.len() == 1 {
|
||||||
|
if i + groups[0] == springs.len() {
|
||||||
|
count += 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
count += possible_count_cache(&springs[i + groups[0]..], &[], cache);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if i + groups[0] + 1 > springs.len() {
|
||||||
|
break;
|
||||||
|
} else if springs[i + groups[0]] == Condition::Bad {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += possible_count_cache(
|
||||||
|
&springs[i + groups[0] + 1..],
|
||||||
|
&groups[1..],
|
||||||
|
cache,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.insert((springs.len(), groups.len()), count);
|
||||||
|
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn possible_count(springs: &[Condition], groups: &[usize]) -> usize {
|
||||||
|
possible_count_cache(springs, groups, &mut HashMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day12, part1)]
|
||||||
|
fn part1(input: &Vec<(Vec<Condition>, Vec<usize>)>) -> usize {
|
||||||
|
input.iter()
|
||||||
|
.map(|(springs, groups)| possible_count(springs, groups))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day12, part2)]
|
||||||
|
fn part2(input: &Vec<(Vec<Condition>, Vec<usize>)>) -> usize {
|
||||||
|
input.iter()
|
||||||
|
.map(|(springs, groups)| possible_count(
|
||||||
|
&[
|
||||||
|
&springs[..],
|
||||||
|
slice::from_ref(&Condition::WhoKnows),
|
||||||
|
&springs[..],
|
||||||
|
slice::from_ref(&Condition::WhoKnows),
|
||||||
|
&springs[..],
|
||||||
|
slice::from_ref(&Condition::WhoKnows),
|
||||||
|
&springs[..],
|
||||||
|
slice::from_ref(&Condition::WhoKnows),
|
||||||
|
&springs[..],
|
||||||
|
].concat(),
|
||||||
|
&[
|
||||||
|
&groups[..],
|
||||||
|
&groups[..],
|
||||||
|
&groups[..],
|
||||||
|
&groups[..],
|
||||||
|
&groups[..],
|
||||||
|
].concat()))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
enum Condition {
|
||||||
|
Good,
|
||||||
|
Bad,
|
||||||
|
WhoKnows
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ConditionConvertable {
|
||||||
|
fn to_condition(self) -> Condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConditionConvertable for char {
|
||||||
|
fn to_condition(self) -> Condition {
|
||||||
|
match self {
|
||||||
|
'.' => Condition::Good,
|
||||||
|
'#' => Condition::Bad,
|
||||||
|
'?' => Condition::WhoKnows,
|
||||||
|
_ => panic!("Invalid spring char AHHH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 525152);
|
||||||
|
}
|
||||||
|
}
|
113
src/day13.rs
Normal file
113
src/day13.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
type Coordinates = (i32, i32);
|
||||||
|
|
||||||
|
#[aoc_generator(day13)]
|
||||||
|
fn parse(input: &str) -> Vec<HashSet<Coordinates>> {
|
||||||
|
input.split("\n\n")
|
||||||
|
.map(|pattern| {
|
||||||
|
let mut map: HashSet<Coordinates> = HashSet::new();
|
||||||
|
for (y, line) in pattern.lines().into_iter().enumerate() {
|
||||||
|
for (x, char) in line.chars().enumerate() {
|
||||||
|
if char == '#' {
|
||||||
|
map.insert((x as i32 + 1, y as i32 + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflection((x, y): &Coordinates, pattern: &HashSet<Coordinates>, max_x: i32, max_y: i32, axis: &i32, is_column: bool) -> bool {
|
||||||
|
if is_column {
|
||||||
|
x <= axis && (2 * axis - x + 1 > max_x || pattern.contains(&(2 * axis - x + 1, *y)))
|
||||||
|
|| x > axis && (2 * axis - x + 1 < 1 || pattern.contains(&(2 * axis - x + 1, *y)))
|
||||||
|
} else {
|
||||||
|
y <= axis && (2 * axis - y + 1 > max_y || pattern.contains(&(*x, 2 * axis - y + 1)))
|
||||||
|
|| y > axis && (2 * axis - y + 1 < 1 || pattern.contains(&(*x, 2 * axis - y + 1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn calc_summary(pattern: &HashSet<Coordinates>) -> usize {
|
||||||
|
let max_x = pattern.iter().max_by(|a, b| a.0.cmp(&b.0)).unwrap().0;
|
||||||
|
let max_y = pattern.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap().1;
|
||||||
|
|
||||||
|
((1..max_x)
|
||||||
|
.find(|column| {
|
||||||
|
pattern.iter()
|
||||||
|
.all(|coords| {
|
||||||
|
reflection(coords, pattern, max_x, max_y, column, true)
|
||||||
|
})
|
||||||
|
}).unwrap_or(0)
|
||||||
|
+ 100 * (1..max_y)
|
||||||
|
.find(|row| {
|
||||||
|
pattern.iter().all(|coordinates| {
|
||||||
|
reflection(coordinates, pattern, max_x, max_y, row, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(0)) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc(day13, part1)]
|
||||||
|
fn part1(input: &Vec<HashSet<Coordinates>>) -> usize {
|
||||||
|
input.iter().map(calc_summary).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_summary_part2(pattern: &HashSet<Coordinates>) -> usize {
|
||||||
|
let max_x = pattern.iter().max_by(|a, b| a.0.cmp(&b.0)).unwrap().0;
|
||||||
|
let max_y = pattern.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap().1;
|
||||||
|
|
||||||
|
((1..max_x)
|
||||||
|
.find(|column| {
|
||||||
|
pattern.iter().filter(|coords| {
|
||||||
|
reflection(coords, pattern, max_x, max_y, column, true)
|
||||||
|
}).count() == pattern.len() - 1
|
||||||
|
}).unwrap_or(0)
|
||||||
|
+ 100 * (1..max_y)
|
||||||
|
.find(|row| {
|
||||||
|
pattern.iter().filter(|coordinates| {
|
||||||
|
reflection(coordinates, pattern, max_x, max_y, row, false)
|
||||||
|
}).count() == pattern.len() - 1
|
||||||
|
})
|
||||||
|
.unwrap_or(0)) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day13, part2)]
|
||||||
|
fn part2(input: &Vec<HashSet<Coordinates>>) -> usize {
|
||||||
|
input.iter().map(calc_summary_part2).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 405);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 400);
|
||||||
|
}
|
||||||
|
}
|
159
src/day14.rs
Normal file
159
src/day14.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum Rock {
|
||||||
|
Round,
|
||||||
|
Square,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
trait RockConvertable {
|
||||||
|
fn to_rock(&self) -> Rock;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RockConvertable for char {
|
||||||
|
fn to_rock(&self) -> Rock {
|
||||||
|
match self {
|
||||||
|
'O' => Rock::Round,
|
||||||
|
'#' => Rock::Square,
|
||||||
|
'.' => Rock::None,
|
||||||
|
_ => panic!("Invalid rock char")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day14)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<Rock>> {
|
||||||
|
let input: Vec<Vec<Rock>> = input.lines()
|
||||||
|
.map(|line| {
|
||||||
|
line.chars().map(|char| {
|
||||||
|
char.to_rock()
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day14, part1)]
|
||||||
|
fn part1(input: &Vec<Vec<Rock>>) -> usize {
|
||||||
|
let mut load: usize = 0;
|
||||||
|
for j in 0..input[0].len() {
|
||||||
|
let mut cur_weight = input[0].len() + 1;
|
||||||
|
|
||||||
|
for i in 0..input.len() {
|
||||||
|
match input[i][j] {
|
||||||
|
Rock::Round => {
|
||||||
|
cur_weight -= 1;
|
||||||
|
load += cur_weight;
|
||||||
|
},
|
||||||
|
Rock::Square => {
|
||||||
|
cur_weight = input[0].len() - i;
|
||||||
|
},
|
||||||
|
Rock::None => continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
load
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day14, part2)]
|
||||||
|
fn part2(input: &Vec<Vec<Rock>>) -> usize {
|
||||||
|
let mut seen_at = HashMap::new();
|
||||||
|
let mut next_y = Vec::<usize>::new();
|
||||||
|
let mut map = input.to_vec();
|
||||||
|
|
||||||
|
// Functions to get a Rock using a rotated coordinate space
|
||||||
|
let cycle_parts: &[(Box<dyn Fn(&mut Vec<Vec<Rock>>, usize, usize) -> &mut Rock>, _, _); 4] = &[
|
||||||
|
(Box::new(|map: &mut Vec<Vec<Rock>>, x: usize, y: usize| &mut map[y][x]),
|
||||||
|
map[0].len(), map.len()),
|
||||||
|
(Box::new(|map: &mut Vec<Vec<Rock>>, x: usize, y: usize| &mut map[x][y]),
|
||||||
|
map.len(), map[0].len()),
|
||||||
|
(Box::new(|map: &mut Vec<Vec<Rock>>, x: usize, y: usize| { let h = map.len(); &mut map[h - 1 - y][x] }),
|
||||||
|
map[0].len(), map.len()),
|
||||||
|
(Box::new(|map: &mut Vec<Vec<Rock>>, x: usize, y: usize| { let w = map[0].len(); &mut map[x][w - 1 - y] }),
|
||||||
|
map.len(), map[0].len()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut cycle = 0;
|
||||||
|
const END: u32 = 1000000000;
|
||||||
|
while cycle < END {
|
||||||
|
// Handle tilts in each direction
|
||||||
|
for (getter, width, height) in cycle_parts {
|
||||||
|
next_y.clear();
|
||||||
|
next_y.resize(*width, 0);
|
||||||
|
for y in 0..*height {
|
||||||
|
for x in 0..*width {
|
||||||
|
let item = getter(&mut map, x, y);
|
||||||
|
match *item {
|
||||||
|
Rock::None => {}
|
||||||
|
Rock::Square => {
|
||||||
|
next_y[x] = y + 1;
|
||||||
|
}
|
||||||
|
Rock::Round => {
|
||||||
|
*item = Rock::None;
|
||||||
|
*getter(&mut map, x, next_y[x]) = Rock::Round;
|
||||||
|
next_y[x] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// More compact representation of the current state, for saving in hashmap
|
||||||
|
let key = map.iter().enumerate().flat_map(|(y, line)| {
|
||||||
|
line.iter().enumerate().filter_map(move |(x, &ref rock)| {
|
||||||
|
if rock == &Rock::Round {
|
||||||
|
Some((x as u8, y as u8))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
cycle += 1;
|
||||||
|
if let Some(seen_at_cycle) = seen_at.insert(key, cycle) {
|
||||||
|
// Current state was identical to one we'd already seen, we can skip forward
|
||||||
|
let diff = cycle - seen_at_cycle;
|
||||||
|
let remaining = END - cycle;
|
||||||
|
let skipped = remaining / diff * diff;
|
||||||
|
cycle += skipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = map.len();
|
||||||
|
|
||||||
|
map.into_iter().enumerate().flat_map(|(y, line)| line.into_iter().filter_map(move |rock| {
|
||||||
|
if rock == Rock::Round {
|
||||||
|
Some(height - y)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 136);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 64);
|
||||||
|
}
|
||||||
|
}
|
93
src/day15.rs
Normal file
93
src/day15.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[aoc_generator(day15)]
|
||||||
|
fn parse(input: &str) -> Vec<String> {
|
||||||
|
input.split(',').map(|x| x.into()).collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day15, part1)]
|
||||||
|
fn part1(input: &Vec<String>) -> u32 {
|
||||||
|
let mut total = 0;
|
||||||
|
for step in input {
|
||||||
|
total += hash(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(string: &str) -> u32 {
|
||||||
|
let mut cur_val = 0;
|
||||||
|
for char in string.chars() {
|
||||||
|
cur_val += char as u32;
|
||||||
|
cur_val *= 17;
|
||||||
|
cur_val = cur_val % 256;
|
||||||
|
}
|
||||||
|
cur_val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day15, part2)]
|
||||||
|
fn part2(input: &Vec<String>) -> usize {
|
||||||
|
let mut boxes: [Vec<(String, usize)>; 256] =
|
||||||
|
std::iter::repeat_with(|| Vec::with_capacity(input.len()))
|
||||||
|
.take(256)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
for step in input {
|
||||||
|
if let Some(dash) = step.find('-') {
|
||||||
|
let label = &step[0..dash];
|
||||||
|
let box_number = hash(label) as usize;
|
||||||
|
if let Some(position) = boxes[box_number].iter().position(|(l, _)| *l == *label) {
|
||||||
|
boxes[box_number].remove(position);
|
||||||
|
}
|
||||||
|
} else if let Some(equal) = step.find('=') {
|
||||||
|
let label = &step[0..equal];
|
||||||
|
let box_number = hash(label) as usize;
|
||||||
|
let focal_length = &step[equal + 1..].parse::<u32>().unwrap();
|
||||||
|
if let Some(position) = boxes[box_number].iter().position(|(l, _)| *l == *label) {
|
||||||
|
let _ = std::mem::replace(
|
||||||
|
&mut boxes[box_number][position],
|
||||||
|
(label.to_string(), *focal_length as usize),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
boxes[box_number].push((label.to_string(), *focal_length as usize));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("invalid step AHHH {}", step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for (index, lens_box) in boxes.iter().enumerate() {
|
||||||
|
for (slot, (_, focal_len)) in lens_box.iter().enumerate() {
|
||||||
|
total += (index + 1) * (slot + 1) * focal_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"HASH";
|
||||||
|
const EX_2: &str = r"rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example2() {
|
||||||
|
assert_eq!(part1(&parse(EX_2)), 1320);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example2() {
|
||||||
|
assert_eq!(part2(&parse(EX_2)), 145);
|
||||||
|
}
|
||||||
|
}
|
211
src/day16.rs
Normal file
211
src/day16.rs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
use prev_iter::PrevPeekable;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use strum_macros::Display;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Tile {
|
||||||
|
None,
|
||||||
|
MirrorLeft,
|
||||||
|
MirrorRight,
|
||||||
|
SplitterVert,
|
||||||
|
SplitterHori
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TileConvertable {
|
||||||
|
fn to_tile(&self) -> Tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileConvertable for char {
|
||||||
|
fn to_tile(&self) -> Tile {
|
||||||
|
match self {
|
||||||
|
'.' => Tile::None,
|
||||||
|
'/' => Tile::MirrorLeft,
|
||||||
|
'\\' => Tile::MirrorRight,
|
||||||
|
'|' => Tile::SplitterVert,
|
||||||
|
'-' => Tile::SplitterHori,
|
||||||
|
_ => panic!("invalid tile char: {} AHHH", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc_generator(day16)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<Tile>> {
|
||||||
|
let input = input.lines()
|
||||||
|
.map(|line| {
|
||||||
|
line.chars()
|
||||||
|
.map(|char| char.to_tile())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day16, part1)]
|
||||||
|
fn part1(map: &Vec<Vec<Tile>>) -> usize {
|
||||||
|
let start = (Position{ x:0 , y:0}, Direction::Right);
|
||||||
|
|
||||||
|
trace_beams(start, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_beams((start_pos, start_dir): (Position, Direction), map: &Vec<Vec<Tile>>) -> usize {
|
||||||
|
let mut in_progress = VecDeque::from([(start_pos, start_dir)]);
|
||||||
|
let mut seen: HashSet<(Position, Direction)> = HashSet::new();
|
||||||
|
|
||||||
|
while !in_progress.is_empty() {
|
||||||
|
let mut cur = in_progress.pop_front();
|
||||||
|
while &cur != &None {
|
||||||
|
seen.insert(cur.clone().unwrap());
|
||||||
|
let (next, new_beam) = do_one_step(cur.clone().unwrap(), map);
|
||||||
|
if let Some(next) = next {
|
||||||
|
if !seen.contains(&next) {
|
||||||
|
cur = Some(next);
|
||||||
|
} else {
|
||||||
|
cur = None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur = None
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_beam) = new_beam {
|
||||||
|
in_progress.push_back(new_beam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.iter().map(|x| x.0).collect::<HashSet<_>>().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_one_step(cur: (Position, Direction), map: &Vec<Vec<Tile>>) -> (Option<(Position, Direction)>, Option<(Position, Direction)>) {
|
||||||
|
let tile = map[cur.0.y as usize][cur.0.x as usize];
|
||||||
|
match tile {
|
||||||
|
Tile::None => (move_and_turn(&cur, None, map), None),
|
||||||
|
Tile::SplitterHori if {cur.1.is_hori()} => (move_and_turn(&cur, None, map), None),
|
||||||
|
Tile::SplitterVert if {cur.1.is_vert()} => (move_and_turn(&cur, None, map), None),
|
||||||
|
Tile::MirrorRight => (move_and_turn(&cur, if cur.1.is_hori() {Some(Direction::Right)} else {Some(Direction::Left)}, map), None),
|
||||||
|
Tile::MirrorLeft => (move_and_turn(&cur, if cur.1.is_hori() {Some(Direction::Left)} else {Some(Direction::Right)}, map), None),
|
||||||
|
Tile::SplitterVert => (move_and_turn(&cur, Some(Direction::Left), map), move_and_turn(&cur, Some(Direction::Right), map)),
|
||||||
|
Tile::SplitterHori => (move_and_turn(&cur, Some(Direction::Right), map), move_and_turn(&cur, Some(Direction::Left), map))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_and_turn(cur: &(Position, Direction), turn: Option<Direction>, map: &Vec<Vec<Tile>>) -> Option<(Position, Direction)> {
|
||||||
|
let next_dir = if let Some(turn) = turn {cur.1.turn(turn)} else {cur.1};
|
||||||
|
let next_pos = cur.0.move_pos(next_dir);
|
||||||
|
|
||||||
|
if let Some(line) = map.get(next_pos.y as usize) {
|
||||||
|
if let Some(_) = line.get(next_pos.x as usize) {
|
||||||
|
return Some((next_pos, next_dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||||
|
struct Position {
|
||||||
|
x: i32,
|
||||||
|
y: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
fn move_pos(&self, dir: Direction) -> Position {
|
||||||
|
let (dx, dy) = match dir {
|
||||||
|
Direction::Up => (0, -1),
|
||||||
|
Direction::Down => (0, 1),
|
||||||
|
Direction::Left => (-1, 0),
|
||||||
|
Direction::Right => (1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Position { x: self.x + dx, y: self.y + dy};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Display, Copy, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn turn(&self, turn: Direction) -> Direction {
|
||||||
|
const TURN_ORDER: [Direction; 4] = [Direction::Up, Direction::Right, Direction::Down, Direction::Left];
|
||||||
|
let mut iter = PrevPeekable::new(TURN_ORDER.iter().cycle());
|
||||||
|
_ = iter.find(|dir| dir == &self).unwrap();
|
||||||
|
|
||||||
|
if turn == Direction::Left && *self == Direction::Up {
|
||||||
|
return Direction::Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = match turn {
|
||||||
|
Direction::Left => iter.prev(),
|
||||||
|
Direction::Right => iter.next(),
|
||||||
|
_ => panic!("Cannot turn {}", turn)
|
||||||
|
};
|
||||||
|
|
||||||
|
return *index.clone().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_hori(&self) -> bool {
|
||||||
|
[Direction::Right, Direction::Left].contains(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_vert(&self) -> bool {
|
||||||
|
[Direction::Up, Direction::Down].contains(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc(day16, part2)]
|
||||||
|
fn part2(map: &Vec<Vec<Tile>>) -> usize {
|
||||||
|
let x_size = map[0].len();
|
||||||
|
let y_size = map.len();
|
||||||
|
let mut edges = vec![];
|
||||||
|
|
||||||
|
let mut top = (0..x_size).map(|x| (Position{ x: x as i32, y: 0}, Direction::Down)).collect::<Vec<_>>();
|
||||||
|
let mut left = (0..y_size).map(|y| (Position{ x: 0, y: y as i32}, Direction::Right)).collect::<Vec<_>>();
|
||||||
|
let mut bottom = (0..x_size).map(|x| (Position{ x: x as i32, y: (y_size - 1) as i32}, Direction::Down)).collect::<Vec<_>>();
|
||||||
|
let mut right = (0..y_size).map(|y| (Position{ x: (x_size - 1) as i32, y: y as i32}, Direction::Left)).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
edges.append(&mut top);
|
||||||
|
edges.append(&mut left);
|
||||||
|
edges.append(&mut bottom);
|
||||||
|
edges.append(&mut right);
|
||||||
|
|
||||||
|
let total = edges.iter().map(|x| trace_beams(*x, map)).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
*total.iter().max().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r".|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 46);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 51);
|
||||||
|
}
|
||||||
|
}
|
115
src/day17.rs
Normal file
115
src/day17.rs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
use std::{collections::BinaryHeap, cmp::Reverse};
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Grid {
|
||||||
|
data: Box<[u8]>,
|
||||||
|
offset: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
fn from_str(s: &str) -> Self {
|
||||||
|
let mut lines = s.lines().peekable();
|
||||||
|
let line_len = lines.peek().map_or(0, |line| line.len());
|
||||||
|
Self {
|
||||||
|
data: lines.flat_map(str::as_bytes).map(|&char| char - b'0').collect::<Box<_>>(),
|
||||||
|
offset: line_len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_pos(&self, p: usize, dir: u8) -> Option<usize> {
|
||||||
|
Some(match dir {
|
||||||
|
0 if p > self.offset => p - self.offset,
|
||||||
|
1 if (p + 1) % self.offset != 0 => p + 1,
|
||||||
|
2 if p < self.data.len() - self.offset => p + self.offset,
|
||||||
|
3 if p % self.offset != 0 => p - 1,
|
||||||
|
_ => { return None }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, dmin: usize, dmax: usize) -> Option<usize> {
|
||||||
|
let lp = self.data.len() - 1;
|
||||||
|
let mut visit = vec![0u8; self.data.len()];
|
||||||
|
let mut ccache = vec![usize::MAX; 2 * self.data.len()];
|
||||||
|
let mut q = BinaryHeap::new();
|
||||||
|
q.push((Reverse(0), 0, 0));
|
||||||
|
q.push((Reverse(0), 0, 1));
|
||||||
|
|
||||||
|
while let Some((Reverse(cost), p, dir)) = q.pop() {
|
||||||
|
if p == lp {
|
||||||
|
return Some(cost)
|
||||||
|
}
|
||||||
|
if visit[p] & (1u8 << dir) != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visit[p] |= 1u8 << dir;
|
||||||
|
let odir = dir ^ 1;
|
||||||
|
for nd in [odir, odir ^ 2] {
|
||||||
|
let mut costsum = 0;
|
||||||
|
let mut np = p;
|
||||||
|
for dist in 1..=dmax {
|
||||||
|
if let Some(op) = self.next_pos(np, nd) {
|
||||||
|
costsum += self.data[op] as usize;
|
||||||
|
if dist >= dmin {
|
||||||
|
let ncost = cost + costsum;
|
||||||
|
let cache_idx = (op << 1) | odir as usize;
|
||||||
|
if ccache[cache_idx] > ncost {
|
||||||
|
ccache[cache_idx] = ncost;
|
||||||
|
q.push((Reverse(ncost), op, odir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
np = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc_generator(day17)]
|
||||||
|
fn parse(input: &str) -> Grid {
|
||||||
|
Grid::from_str(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day17, part1)]
|
||||||
|
fn part1(input: &Grid) -> usize {
|
||||||
|
input.run(1, 3).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day17, part2)]
|
||||||
|
fn part2(input: &Grid) -> usize {
|
||||||
|
input.run(4, 10).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 102);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 94);
|
||||||
|
}
|
||||||
|
}
|
187
src/day18.rs
Normal file
187
src/day18.rs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use strum_macros::Display;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
struct Step {
|
||||||
|
dir: Direction,
|
||||||
|
length: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Display, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn get_coordinate_modifier(&self) -> (i32, i32) {
|
||||||
|
match self {
|
||||||
|
Direction::Up => (0, 1),
|
||||||
|
Direction::Down => (0, -1),
|
||||||
|
Direction::Left => (-1, 0),
|
||||||
|
Direction::Right => (1, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DirectionConvertable {
|
||||||
|
fn to_direction(&self) -> Direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectionConvertable for &str {
|
||||||
|
fn to_direction(&self) -> Direction {
|
||||||
|
match *self {
|
||||||
|
"U" => Direction::Up,
|
||||||
|
"D" => Direction::Down,
|
||||||
|
"L" => Direction::Left,
|
||||||
|
"R" => Direction::Right,
|
||||||
|
_ => panic!("Invalid Direction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectionConvertable for i32 {
|
||||||
|
fn to_direction(&self) -> Direction {
|
||||||
|
match self {
|
||||||
|
3 => Direction::Up,
|
||||||
|
1 => Direction::Down,
|
||||||
|
2 => Direction::Left,
|
||||||
|
0 => Direction::Right,
|
||||||
|
_ => panic!("Invalid Direction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day18, part1)]
|
||||||
|
fn parse_part1(input: &str) -> Vec<Step> {
|
||||||
|
let steps = input.lines()
|
||||||
|
.map(|line| {
|
||||||
|
let mut line = line.split(' ');
|
||||||
|
Step {
|
||||||
|
dir: line.next().unwrap().to_direction(),
|
||||||
|
length: line.next().unwrap().parse::<i32>().unwrap(),
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
steps
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day18, part1)]
|
||||||
|
fn part1(steps: &Vec<Step>) -> i32 {
|
||||||
|
let verticies = get_verticies(steps);
|
||||||
|
|
||||||
|
let mut area = 0;
|
||||||
|
|
||||||
|
for win in verticies.windows(2) {
|
||||||
|
area += win[0].y * win[1].x;
|
||||||
|
area -= win[0].x * win[1].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
(area / 2) + (verticies.len() as i32 / 2) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
struct Coordinate {
|
||||||
|
x: i32,
|
||||||
|
y: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_verticies(steps: &Vec<Step>) -> Vec<Coordinate> {
|
||||||
|
let mut cur_pos = Coordinate{ x: 0, y: 0 };
|
||||||
|
let mut verticies: Vec<Coordinate> = vec![cur_pos.clone()];
|
||||||
|
|
||||||
|
for step in steps {
|
||||||
|
let (x_mod, y_mod) = step.dir.get_coordinate_modifier();
|
||||||
|
for _ in 0..step.length {
|
||||||
|
let new_pos = Coordinate{
|
||||||
|
x: cur_pos.x + x_mod,
|
||||||
|
y: cur_pos.y + y_mod,
|
||||||
|
};
|
||||||
|
cur_pos = new_pos.clone();
|
||||||
|
verticies.push(new_pos.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verticies
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day18, part2)]
|
||||||
|
fn parse_part2(input: &str) -> Vec<Step> {
|
||||||
|
let steps = input.lines()
|
||||||
|
.map(|line| {
|
||||||
|
let line = line.split(' ');
|
||||||
|
parse_hex(line.last().unwrap())
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
steps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_hex(hex: &str) -> Step {
|
||||||
|
let hex = hex.trim_matches(['(', ')', '#'].as_slice());
|
||||||
|
let length = "0".to_string() + &hex[0..5];
|
||||||
|
let length = u32::from_str_radix(&length, 16).expect("Cannot parse Hex value");
|
||||||
|
|
||||||
|
let dir = hex.chars().last().unwrap().to_string().parse::<i32>().unwrap().to_direction();
|
||||||
|
|
||||||
|
Step {
|
||||||
|
dir,
|
||||||
|
length: length as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day18, part2)]
|
||||||
|
fn part2(steps: &Vec<Step>) -> i64 {
|
||||||
|
let verticies = get_verticies(steps);
|
||||||
|
|
||||||
|
let mut area: i64 = 0;
|
||||||
|
|
||||||
|
for win in verticies.windows(2) {
|
||||||
|
area += win[0].y as i64 * win[1].x as i64;
|
||||||
|
area -= win[0].x as i64 * win[1].y as i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
(area / 2) + (verticies.len() as i64 / 2) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"R 6 (#70c710)
|
||||||
|
D 5 (#0dc571)
|
||||||
|
L 2 (#5713f0)
|
||||||
|
D 2 (#d2c081)
|
||||||
|
R 2 (#59c680)
|
||||||
|
D 2 (#411b91)
|
||||||
|
L 5 (#8ceee2)
|
||||||
|
U 2 (#caa173)
|
||||||
|
L 1 (#1b58a2)
|
||||||
|
U 2 (#caa171)
|
||||||
|
R 2 (#7807d2)
|
||||||
|
U 3 (#a77fa3)
|
||||||
|
L 2 (#015232)
|
||||||
|
U 2 (#7a21e3)";
|
||||||
|
|
||||||
|
const EX_2: &str = r"R 6 (#70c710)
|
||||||
|
D 6 (#0dc571)
|
||||||
|
L 6 (#5713f0)
|
||||||
|
U 6 (#d2c081)";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse_part1(EX)), 62);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example2() {
|
||||||
|
assert_eq!(part1(&parse_part1(EX_2)), 49);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse_part2(EX)), 952408144115);
|
||||||
|
}
|
||||||
|
}
|
245
src/day19.rs
Normal file
245
src/day19.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use regex::{Regex, Match};
|
||||||
|
|
||||||
|
enum Rule {
|
||||||
|
ACCEPTED,
|
||||||
|
REJECTED,
|
||||||
|
GOTO(String),
|
||||||
|
GT(String, usize, String),
|
||||||
|
LT(String, usize, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day19, part1)]
|
||||||
|
fn parse(input: &str) -> (HashMap<String, Vec<Rule>>, Vec<HashMap<String, usize>>) {
|
||||||
|
let (workflows_input, ratings_input) = input.split_once("\n\n").unwrap();
|
||||||
|
let workflows = parse_workflows(workflows_input);
|
||||||
|
|
||||||
|
// screw it im using regex
|
||||||
|
let rer = Regex::new(r"^\{x=(\d+),m=(\d+),a=(\d+),s=(\d+)\}$").unwrap();
|
||||||
|
let ratings: Vec<HashMap<String, usize>> = ratings_input.lines().map(|line| {
|
||||||
|
let caps = rer.captures(line).unwrap();
|
||||||
|
let x = parse_usize(caps.get(1));
|
||||||
|
let m = parse_usize(caps.get(2));
|
||||||
|
let a = parse_usize(caps.get(3));
|
||||||
|
let s = parse_usize(caps.get(4));
|
||||||
|
|
||||||
|
let mut vals = HashMap::new();
|
||||||
|
vals.insert("x".to_string(), x);
|
||||||
|
vals.insert("m".to_string(), m);
|
||||||
|
vals.insert("a".to_string(), a);
|
||||||
|
vals.insert("s".to_string(), s);
|
||||||
|
vals
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
(workflows, ratings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_usize(g: Option<Match>) -> usize {
|
||||||
|
g.map_or(0, |m| m.as_str().parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_workflows(workflows_input: &str) -> HashMap<String, Vec<Rule>> {
|
||||||
|
let mut workflows: HashMap<String, Vec<Rule>> = HashMap::new();
|
||||||
|
let rew = Regex::new(r"^(.+)\{(.+)\}$").unwrap();
|
||||||
|
workflows_input.lines().for_each(|line| {
|
||||||
|
let caps = rew.captures(line).unwrap();
|
||||||
|
let key = caps.get(1).unwrap().as_str();
|
||||||
|
let rules_str = caps.get(2).unwrap().as_str();
|
||||||
|
|
||||||
|
let rules = rules_str.split(",").map(|expr| {
|
||||||
|
if expr == "A" {
|
||||||
|
return Rule::ACCEPTED;
|
||||||
|
}
|
||||||
|
if expr == "R" {
|
||||||
|
return Rule::REJECTED;
|
||||||
|
}
|
||||||
|
if !expr.contains(":") {
|
||||||
|
return Rule::GOTO(expr.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (rule, to) = expr.split_once(":").unwrap();
|
||||||
|
|
||||||
|
return if rule.contains(">") {
|
||||||
|
let (prop, val) = rule.split_once(">").unwrap();
|
||||||
|
Rule::GT(prop.to_string(), val.parse().unwrap(), to.to_string())
|
||||||
|
} else if rule.contains("<") {
|
||||||
|
let (prop, val) = rule.split_once("<").unwrap();
|
||||||
|
Rule::LT(prop.to_string(), val.parse().unwrap(), to.to_string())
|
||||||
|
} else {
|
||||||
|
panic!("Unknown rule {}", rule)
|
||||||
|
};
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
workflows.insert(key.to_string(), rules);
|
||||||
|
});
|
||||||
|
workflows
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day19, part1)]
|
||||||
|
fn part1((workflows, ratings): &(HashMap<String, Vec<Rule>>, Vec<HashMap<String, usize>>)) -> usize {
|
||||||
|
let mut accepted: Vec<HashMap<String, usize>> = vec!();
|
||||||
|
'outer: for rating in ratings {
|
||||||
|
let mut wf_key = "in";
|
||||||
|
'middle: loop {
|
||||||
|
if wf_key == "A" {
|
||||||
|
accepted.push(rating.clone());
|
||||||
|
continue 'outer;
|
||||||
|
} else if wf_key == "R" {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rules = workflows.get(wf_key).unwrap();
|
||||||
|
for rule in rules {
|
||||||
|
match rule {
|
||||||
|
Rule::ACCEPTED => {
|
||||||
|
accepted.push(rating.clone());
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
Rule::REJECTED => {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
Rule::GOTO(new_wf_key) => {
|
||||||
|
wf_key = new_wf_key;
|
||||||
|
continue 'middle;
|
||||||
|
}
|
||||||
|
Rule::GT(prop, val, to) => {
|
||||||
|
if rating.get(prop.as_str()).unwrap() > val {
|
||||||
|
wf_key = to;
|
||||||
|
continue 'middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rule::LT(prop, val, to) => {
|
||||||
|
if rating.get(prop.as_str()).unwrap() < val {
|
||||||
|
wf_key = to;
|
||||||
|
continue 'middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted.iter().map(|rating| rating.values().sum::<usize>()).sum::<usize>()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc_generator(day19, part2)]
|
||||||
|
fn parse_part2(input: &str) -> HashMap<String, Vec<Rule>> {
|
||||||
|
let (workflows_input, _) = input.split_once("\n\n").unwrap();
|
||||||
|
parse_workflows(workflows_input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day19, part2)]
|
||||||
|
fn part2(workflows: &HashMap<String, Vec<Rule>>) -> usize {
|
||||||
|
let mut stack: Vec<((usize, usize), (usize, usize), (usize, usize), (usize, usize), &str, usize)> =
|
||||||
|
vec! {((1, 4000), (1, 4000), (1, 4000), (1, 4000), "in", 0)};
|
||||||
|
let mut accepted: Vec<((usize, usize), (usize, usize), (usize, usize), (usize, usize))> = vec!();
|
||||||
|
while let Some(range) = stack.pop() {
|
||||||
|
let (x, m, a, s, wf_key, rule_key) = range;
|
||||||
|
if wf_key == "A" {
|
||||||
|
accepted.push((x, m, a, s));
|
||||||
|
continue;
|
||||||
|
} else if wf_key == "R" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.0 > x.1 || m.0 > m.1 || a.0 > a.1 || s.0 > s.1 { continue }
|
||||||
|
|
||||||
|
let rules = workflows.get(wf_key).unwrap();
|
||||||
|
let rule = &rules[rule_key];
|
||||||
|
match rule {
|
||||||
|
Rule::ACCEPTED => {
|
||||||
|
accepted.push((x, m, a, s));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Rule::REJECTED => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Rule::GOTO(new_wf_key) => {
|
||||||
|
stack.push((x, m, a, s, new_wf_key, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Rule::GT(prop, val, to) => {
|
||||||
|
match prop.as_str() {
|
||||||
|
"x" => {
|
||||||
|
stack.push(((val + 1, x.1), m, a, s, to.as_str(), 0));
|
||||||
|
stack.push(((x.0, *val), m, a, s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"m" => {
|
||||||
|
stack.push((x, (val + 1, m.1), a, s, to.as_str(), 0));
|
||||||
|
stack.push((x, (m.0, *val), a, s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"a" => {
|
||||||
|
stack.push((x, m, (val + 1, a.1), s, to.as_str(), 0));
|
||||||
|
stack.push((x, m, (a.0, *val), s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"s" => {
|
||||||
|
stack.push((x, m, a, (val + 1, s.1), to.as_str(), 0));
|
||||||
|
stack.push((x, m, a, (s.0, *val), wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
_ => { panic!("unknown prop {}", prop) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rule::LT(prop, val, to) => {
|
||||||
|
match prop.as_str() {
|
||||||
|
"x" => {
|
||||||
|
stack.push(((x.0, val - 1), m, a, s, to.as_str(), 0));
|
||||||
|
stack.push(((*val, x.1), m, a, s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"m" => {
|
||||||
|
stack.push((x, (m.0, val - 1), a, s, to.as_str(), 0));
|
||||||
|
stack.push((x, (*val, m.1), a, s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"a" => {
|
||||||
|
stack.push((x, m, (a.0, val - 1), s, to.as_str(), 0));
|
||||||
|
stack.push((x, m, (*val, a.1), s, wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
"s" => {
|
||||||
|
stack.push((x, m, a, (s.0, val - 1), to.as_str(), 0));
|
||||||
|
stack.push((x, m, a, (*val, s.1), wf_key, rule_key + 1));
|
||||||
|
}
|
||||||
|
_ => { panic!("unknown prop {}", prop) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted.iter().map(|(x, m, a, s)| {
|
||||||
|
(x.1 - x.0 + 1) * (m.1 - m.0 + 1) * (a.1 - a.0 + 1) * (s.1 - s.0 + 1)
|
||||||
|
}).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"px{a<2006:qkq,m>2090:A,rfg}
|
||||||
|
pv{a>1716:R,A}
|
||||||
|
lnx{m>1548:A,A}
|
||||||
|
rfg{s<537:gd,x>2440:R,A}
|
||||||
|
qs{s>3448:A,lnx}
|
||||||
|
qkq{x<1416:A,crn}
|
||||||
|
crn{x>2662:A,R}
|
||||||
|
in{s<1351:px,qqz}
|
||||||
|
qqz{s>2770:qs,m<1801:hdj,R}
|
||||||
|
gd{a>3333:R,R}
|
||||||
|
hdj{m>838:A,pv}
|
||||||
|
|
||||||
|
{x=787,m=2655,a=1222,s=2876}
|
||||||
|
{x=1679,m=44,a=2067,s=496}
|
||||||
|
{x=2036,m=264,a=79,s=2244}
|
||||||
|
{x=2461,m=1339,a=466,s=291}
|
||||||
|
{x=2127,m=1623,a=2188,s=1013}";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 19114);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse_part2(EX)), 167409079868000);
|
||||||
|
}
|
||||||
|
}
|
219
src/day20.rs
Normal file
219
src/day20.rs
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use num::Integer;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Module {
|
||||||
|
FlipFlop { state: bool, dests: Vec<String> },
|
||||||
|
Conjunction { state: HashMap<String, bool>, dests: Vec<String> },
|
||||||
|
Broadcast { dests: Vec<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day20)]
|
||||||
|
fn parse(input: &str) -> HashMap<String, Module> {
|
||||||
|
let mut input: HashMap<_, _> = input.lines().map(|line| {
|
||||||
|
let (name, dests) = line.split_once("->").unwrap();
|
||||||
|
let dests = parse_destinations(dests);
|
||||||
|
let module = parse_module(name.trim(), dests);
|
||||||
|
(name.trim_matches(['&', '%', ' '].as_slice()).to_string(), module)
|
||||||
|
}).collect();
|
||||||
|
collect_inputs(&mut input);
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_module(name: &str, dests: Vec<String>) -> Module {
|
||||||
|
match name {
|
||||||
|
"broadcaster" => Module::Broadcast { dests },
|
||||||
|
name if name.starts_with('%') => Module::FlipFlop { state: false, dests },
|
||||||
|
name if name.starts_with('&') => Module::Conjunction { state: HashMap::new(), dests },
|
||||||
|
_ => panic!("Invalid Module {}", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_inputs(modules: &mut HashMap<String, Module>) {
|
||||||
|
for (name, module) in modules.clone() {
|
||||||
|
let dests = match module {
|
||||||
|
Module::FlipFlop { state: _, dests: dest } => dest,
|
||||||
|
Module::Conjunction { state: _, dests: dest } => dest,
|
||||||
|
Module::Broadcast { dests: dest } => dest,
|
||||||
|
};
|
||||||
|
|
||||||
|
for dest_name in dests {
|
||||||
|
if let Some(dest_mod) = modules.get_mut(&dest_name) {
|
||||||
|
match dest_mod {
|
||||||
|
Module::Conjunction { state, dests: _ } => state.insert(name.to_string(), false),
|
||||||
|
_ => continue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_destinations(dests: &str) -> Vec<String> {
|
||||||
|
dests.split(',')
|
||||||
|
.map(|x| x.trim().to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day20, part1)]
|
||||||
|
fn part1(modules: &HashMap<String, Module>) -> usize {
|
||||||
|
let mut modules = modules.clone();
|
||||||
|
let mut high: usize = 0;
|
||||||
|
let mut low: usize = 0;
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let (high_pulses, low_pulses) = push_the_button(&mut modules);
|
||||||
|
high += high_pulses;
|
||||||
|
low += low_pulses;
|
||||||
|
}
|
||||||
|
|
||||||
|
high * low
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_the_button(modules: &mut HashMap<String, Module>) -> (usize, usize) {
|
||||||
|
let mut process_queue: VecDeque<(String, bool, String)> = VecDeque::new();
|
||||||
|
process_queue.push_back(("broadcaster".to_string(), false, "".to_string()));
|
||||||
|
let mut low_pulses: usize = 0;
|
||||||
|
let mut high_pulses: usize = 0;
|
||||||
|
|
||||||
|
while let Some((module_name, input, sender)) = process_queue.pop_front() {
|
||||||
|
match input {
|
||||||
|
true => high_pulses += 1,
|
||||||
|
false => low_pulses += 1,
|
||||||
|
}
|
||||||
|
if let Some(module) = modules.get_mut(&module_name) {
|
||||||
|
match module {
|
||||||
|
Module::FlipFlop { state, dests } => {
|
||||||
|
if !input {
|
||||||
|
let flipped = !(*state);
|
||||||
|
*state = flipped;
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), state.clone(), module_name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Module::Conjunction { state, dests } => {
|
||||||
|
let stored = state.get_mut(&sender.to_string()).unwrap();
|
||||||
|
*stored = input;
|
||||||
|
|
||||||
|
let mut send = true;
|
||||||
|
if state.values().all(|x| *x) {
|
||||||
|
send = false;
|
||||||
|
}
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), send, module_name.clone()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Module::Broadcast { dests } => {
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), input, module_name.clone()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (high_pulses, low_pulses)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day20, part2)]
|
||||||
|
fn part2(og_modules: &HashMap<String, Module>) -> usize {
|
||||||
|
let mut lcm = vec![];
|
||||||
|
let before = ["tr", "xm", "dr", "nh"];
|
||||||
|
|
||||||
|
for module_name in before {
|
||||||
|
let mut modules = og_modules.clone();
|
||||||
|
let mut count: usize = 1;
|
||||||
|
while !push_the_button_part2(&mut modules, module_name.to_string()) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
lcm.push(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", lcm);
|
||||||
|
|
||||||
|
let lcm = lcm.iter()
|
||||||
|
.cloned()
|
||||||
|
.reduce(|a, b| a.lcm(&b))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
lcm
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_the_button_part2(modules: &mut HashMap<String, Module>, to_find: String) -> bool {
|
||||||
|
let mut process_queue: VecDeque<(String, bool, String)> = VecDeque::new();
|
||||||
|
process_queue.push_back(("broadcaster".to_string(), false, "".to_string()));
|
||||||
|
|
||||||
|
while let Some((module_name, input, sender)) = process_queue.pop_front() {
|
||||||
|
if module_name == to_find && input == false {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if let Some(module) = modules.get_mut(&module_name) {
|
||||||
|
match module {
|
||||||
|
Module::FlipFlop { state, dests } => {
|
||||||
|
if !input {
|
||||||
|
let flipped = !(*state);
|
||||||
|
*state = flipped;
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), state.clone(), module_name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Module::Conjunction { state, dests } => {
|
||||||
|
let stored = state.get_mut(&sender.to_string()).unwrap();
|
||||||
|
*stored = input;
|
||||||
|
|
||||||
|
let mut send = true;
|
||||||
|
if state.values().all(|x| *x) {
|
||||||
|
send = false;
|
||||||
|
}
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), send, module_name.clone()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Module::Broadcast { dests } => {
|
||||||
|
for dest in dests {
|
||||||
|
process_queue.push_back((dest.clone(), input, module_name.clone()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX_1: &str = r"broadcaster -> a, b, c
|
||||||
|
%a -> b
|
||||||
|
%b -> c
|
||||||
|
%c -> inv
|
||||||
|
&inv -> a";
|
||||||
|
|
||||||
|
const EX_2: &str = r"broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> rx";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example1() {
|
||||||
|
assert_eq!(part1(&parse(EX_1)), 32000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example2() {
|
||||||
|
assert_eq!(part1(&parse(EX_2)), 11687500);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX_2)), 1);
|
||||||
|
}
|
||||||
|
}
|
186
src/day21.rs
Normal file
186
src/day21.rs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
const STEPS: i32 = 64;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
struct Position(i32, i32);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct Garden {
|
||||||
|
rocks: HashSet<Position>,
|
||||||
|
start: Position,
|
||||||
|
extents: (Position, Position),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day21, part1)]
|
||||||
|
fn parse(input: &str) -> Garden {
|
||||||
|
let mut rocks: HashSet<Position> = HashSet::new();
|
||||||
|
let mut start: Position = Position(0, 0);
|
||||||
|
for (row, line) in input.lines().enumerate() {
|
||||||
|
for (col, ch) in line.chars().enumerate() {
|
||||||
|
match ch {
|
||||||
|
'#' => {
|
||||||
|
rocks.insert(Position(row as i32, col as i32));
|
||||||
|
}
|
||||||
|
'S' => {
|
||||||
|
start = Position(row as i32, col as i32);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut trans_rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for rock in rocks.iter() {
|
||||||
|
let trans_rock = Position(rock.0 - start.0, start.1 - rock.1);
|
||||||
|
trans_rocks.insert(trans_rock);
|
||||||
|
}
|
||||||
|
rocks = trans_rocks;
|
||||||
|
let num_rows = input.lines().count() as i32;
|
||||||
|
let num_cols = input.lines().next().unwrap().chars().count() as i32;
|
||||||
|
let extents = (
|
||||||
|
Position(-num_rows / 2, -num_cols / 2),
|
||||||
|
Position(num_rows / 2, num_cols / 2),
|
||||||
|
);
|
||||||
|
Garden {
|
||||||
|
rocks,
|
||||||
|
start: Position(0, 0),
|
||||||
|
extents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day21, part1)]
|
||||||
|
fn part1(map: &Garden) -> u32 {
|
||||||
|
let mut visited: HashSet<Position> = HashSet::new();
|
||||||
|
visited.insert(map.start);
|
||||||
|
for _ in 0..STEPS {
|
||||||
|
let mut new_visited = HashSet::new();
|
||||||
|
for pos in visited.iter().clone() {
|
||||||
|
let neighbors = [
|
||||||
|
Position(pos.0 - 1, pos.1),
|
||||||
|
Position(pos.0 + 1, pos.1),
|
||||||
|
Position(pos.0, pos.1 - 1),
|
||||||
|
Position(pos.0, pos.1 + 1),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| in_bounds(*p, map.extents) && !map.rocks.contains(p));
|
||||||
|
for neighbor in neighbors {
|
||||||
|
new_visited.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited = new_visited;
|
||||||
|
}
|
||||||
|
visited.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_bounds(pos: Position, extents: (Position, Position)) -> bool {
|
||||||
|
pos.0 >= extents.0 .0 && pos.0 <= extents.1 .0 && pos.1 >= extents.0 .1 && pos.1 < extents.1 .1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_bounds_part2(pos: Position, extents: Position) -> bool {
|
||||||
|
pos.0 >= 0 && pos.0 <= extents.0 && pos.1 >= 0 && pos.1 < extents.1
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GardenPart2 {
|
||||||
|
rocks: HashSet<Position>,
|
||||||
|
start: Position,
|
||||||
|
extents: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day21, part2)]
|
||||||
|
fn parse_part2(input: &str) -> GardenPart2 {
|
||||||
|
let mut rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for (row, line) in input.lines().enumerate() {
|
||||||
|
for (col, ch) in line.chars().enumerate() {
|
||||||
|
match ch {
|
||||||
|
'#' => {
|
||||||
|
rocks.insert(Position(row as i32, col as i32));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num_rows = input.lines().count() as i32;
|
||||||
|
let num_cols = input.lines().next().unwrap().chars().count() as i32;
|
||||||
|
let mut expanded_rocks: HashSet<Position> = HashSet::new();
|
||||||
|
for rock in rocks.iter() {
|
||||||
|
for row_mul in 0..5 {
|
||||||
|
for col_mul in 0..5 {
|
||||||
|
let expanded_rock = Position(
|
||||||
|
rock.0 + (num_rows * row_mul as i32),
|
||||||
|
rock.1 + (num_cols * col_mul as i32),
|
||||||
|
);
|
||||||
|
expanded_rocks.insert(expanded_rock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GardenPart2 {
|
||||||
|
rocks: expanded_rocks,
|
||||||
|
start: Position(num_rows * 5 / 2, num_cols * 5 / 2),
|
||||||
|
extents: Position(num_rows * 5, num_cols * 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day21, part2)]
|
||||||
|
fn part2(map: &GardenPart2) -> i64 {
|
||||||
|
let b0: i64 = walk(&map, 65) as i64;
|
||||||
|
let b1: i64 = walk(&map, 65 + 131) as i64;
|
||||||
|
let b2: i64 = walk(&map, 65 + 2 * 131) as i64;
|
||||||
|
let n: i64 = 202300;
|
||||||
|
// below uses Cramer's Rule to solve for x0, x1, x2
|
||||||
|
let det_a: f64 = -2.0;
|
||||||
|
let det_a0: f64 = -b0 as f64 + 2.0 * b1 as f64 - b2 as f64;
|
||||||
|
let det_a1: f64 = 3.0 * b0 as f64 - 4.0 * b1 as f64 + b2 as f64;
|
||||||
|
let det_a2: f64 = -2.0 * b0 as f64;
|
||||||
|
let x0: i64 = (det_a0 / det_a) as i64;
|
||||||
|
let x1: i64 = (det_a1 / det_a) as i64;
|
||||||
|
let x2: i64 = (det_a2 / det_a) as i64;
|
||||||
|
|
||||||
|
x0 * n * n + x1 * n + x2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk(garden: &GardenPart2, steps: u32) -> u32 {
|
||||||
|
let mut visited: HashSet<Position> = HashSet::new();
|
||||||
|
visited.insert(garden.start);
|
||||||
|
for _ in 0..steps {
|
||||||
|
let mut new_visited = HashSet::new();
|
||||||
|
for pos in visited.iter().clone() {
|
||||||
|
let neighbors = [
|
||||||
|
Position(pos.0 - 1, pos.1),
|
||||||
|
Position(pos.0 + 1, pos.1),
|
||||||
|
Position(pos.0, pos.1 - 1),
|
||||||
|
Position(pos.0, pos.1 + 1),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| in_bounds_part2(*p, garden.extents) && !garden.rocks.contains(p));
|
||||||
|
for neighbor in neighbors {
|
||||||
|
new_visited.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited = new_visited;
|
||||||
|
}
|
||||||
|
visited.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"...........
|
||||||
|
.....###.#.
|
||||||
|
.###.##..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.##..S####.
|
||||||
|
.##..#...#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##..##.##.
|
||||||
|
...........";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 16);
|
||||||
|
}
|
||||||
|
}
|
133
src/day22.rs
Normal file
133
src/day22.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub struct Brick {
|
||||||
|
up: Vec<Vec<usize>>,
|
||||||
|
down: Vec<Vec<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc_generator(day22)]
|
||||||
|
fn parse(input: &str) -> Brick {
|
||||||
|
let re: Regex = Regex::new(r"\d+").unwrap();
|
||||||
|
|
||||||
|
let mut bricks: Vec<[usize; 6]> = input.lines().map(|line| {
|
||||||
|
re.captures_iter(line)
|
||||||
|
.map(|c| c.extract::<0>().0.parse::<usize>().unwrap())
|
||||||
|
.collect::<Vec<_>>().try_into().unwrap()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut heights = [[0; 10]; 10];
|
||||||
|
let mut indices = [[usize::MAX; 10]; 10];
|
||||||
|
let mut up = vec![Vec::new(); bricks.len()];
|
||||||
|
let mut down = vec![Vec::new(); bricks.len()];
|
||||||
|
|
||||||
|
bricks.sort_unstable_by_key(|b| b[2]);
|
||||||
|
|
||||||
|
for (i, &[x1, y1, z1, x2, y2, z2]) in bricks.iter().enumerate() {
|
||||||
|
let height = z2 - z1 + 1;
|
||||||
|
let mut top = 0;
|
||||||
|
let mut previous = usize::MAX;
|
||||||
|
|
||||||
|
for x in x1..=x2 {
|
||||||
|
for y in y1..=y2 {
|
||||||
|
top = top.max(heights[x][y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for x in x1..=x2 {
|
||||||
|
for y in y1..=y2 {
|
||||||
|
if heights[x][y] == top {
|
||||||
|
let index = indices[x][y];
|
||||||
|
if index != previous {
|
||||||
|
up[index].push(i);
|
||||||
|
down[i].push(index);
|
||||||
|
previous = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heights[x][y] = top + height;
|
||||||
|
indices[x][y] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Brick { up , down }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day22, part1)]
|
||||||
|
fn part1(input: &Brick) -> usize {
|
||||||
|
let Brick { down, .. } = input;
|
||||||
|
let mut safe = vec![true; down.len()];
|
||||||
|
|
||||||
|
for underneath in down {
|
||||||
|
if underneath.len() == 1 {
|
||||||
|
safe[underneath[0]] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safe.iter().filter(|&&b| b).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day22, part2)]
|
||||||
|
fn part2(input: &Brick) -> usize {
|
||||||
|
let Brick { up, down } = input;
|
||||||
|
let mut safe = vec![true; down.len()];
|
||||||
|
|
||||||
|
for underneath in down {
|
||||||
|
if underneath.len() == 1 {
|
||||||
|
safe[underneath[0]] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = 0;
|
||||||
|
let mut todo = VecDeque::new();
|
||||||
|
let mut removed = vec![usize::MAX; down.len()];
|
||||||
|
|
||||||
|
for (start, &safe) in safe.iter().enumerate() {
|
||||||
|
if safe {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo.push_back(start);
|
||||||
|
removed[start] = start;
|
||||||
|
|
||||||
|
while let Some(current) = todo.pop_front() {
|
||||||
|
for &next in &up[current] {
|
||||||
|
if removed[next] != start && down[next].iter().all(|&i| removed[i] == start) {
|
||||||
|
result += 1;
|
||||||
|
removed[next] = start;
|
||||||
|
todo.push_back(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"1,0,1~1,2,1
|
||||||
|
0,0,2~2,0,2
|
||||||
|
0,2,3~2,2,3
|
||||||
|
0,0,4~0,2,4
|
||||||
|
2,0,5~2,2,5
|
||||||
|
0,1,6~2,1,6
|
||||||
|
1,1,8~1,1,9";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 7);
|
||||||
|
}
|
||||||
|
}
|
156
src/day23.rs
Normal file
156
src/day23.rs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
const NEIGHBORS: &[(i64,i64)] = &[(-1,0),(0,1),(1,0),(0,-1)];
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
#[aoc_generator(day23)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<char>> {
|
||||||
|
input.lines().map(|x| x.chars().collect::<Vec<_>>()).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dfs(graph: &HashMap<(usize,usize), Vec<(usize,usize,usize)>>, seen: &mut Vec<Vec<bool>>, (r,c): (usize, usize)) -> Option<usize> {
|
||||||
|
if r == seen.len() - 1 {
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
let mut max_dist = None;
|
||||||
|
for &(rr, cc, d) in &graph[&(r,c)] {
|
||||||
|
if !seen[rr][cc] {
|
||||||
|
seen[rr][cc] = true;
|
||||||
|
if let Some(dist) = dfs(graph, seen, (rr,cc)) {
|
||||||
|
max_dist = Some(max_dist.unwrap_or(0).max(d+dist))
|
||||||
|
}
|
||||||
|
seen[rr][cc] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max_dist
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day23, part1)]
|
||||||
|
fn part1(grid: &Vec<Vec<char>>) -> usize {
|
||||||
|
let mut graph = HashMap::<_,Vec<_>>::new();
|
||||||
|
for (r, c) in (0..grid.len()).cartesian_product(0..grid[0].len()) {
|
||||||
|
let neighbors = match grid[r][c] {
|
||||||
|
'#' => continue,
|
||||||
|
'.' => NEIGHBORS,
|
||||||
|
'^' => &NEIGHBORS[0..][..1],
|
||||||
|
'>' => &NEIGHBORS[1..][..1],
|
||||||
|
'v' => &NEIGHBORS[2..][..1],
|
||||||
|
'<' => &NEIGHBORS[3..][..1],
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let e = graph.entry((r,c)).or_default();
|
||||||
|
|
||||||
|
for (dr, dc) in neighbors {
|
||||||
|
let rr = (r as i64 + dr) as usize;
|
||||||
|
let cc = (c as i64 + dc) as usize;
|
||||||
|
if grid.get(rr).and_then(|row| row.get(cc)).is_some_and(|&t| t != '#') {
|
||||||
|
e.push((rr,cc,1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let corridors = graph.iter()
|
||||||
|
.filter(|(_, n)| n.len() == 2)
|
||||||
|
.map(|(&node, _)| node)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (r,c) in corridors {
|
||||||
|
let neighbors = graph.remove(&(r,c)).unwrap();
|
||||||
|
let (r1, c1, d1) = neighbors[0];
|
||||||
|
let (r2, c2, d2) = neighbors[1];
|
||||||
|
let n1 = graph.get_mut(&(r1,c1)).unwrap();
|
||||||
|
if let Some(i) = n1.iter().position(|&(rr,cc,_)| (rr,cc) == (r,c)) {
|
||||||
|
n1[i] = (r2,c2,d1+d2);
|
||||||
|
}
|
||||||
|
let n2 = graph.get_mut(&(r2,c2)).unwrap();
|
||||||
|
if let Some(i) = n2.iter().position(|&(rr,cc,_)| (rr,cc) == (r,c)) {
|
||||||
|
n2[i] = (r1,c1,d1+d2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dfs(&graph, &mut vec![vec![false; grid[0].len()]; grid.len()], (0,1)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day23, part2)]
|
||||||
|
fn part2(grid: &Vec<Vec<char>>) -> usize {
|
||||||
|
let mut graph = HashMap::<_,Vec<_>>::new();
|
||||||
|
for (r, c) in (0..grid.len()).cartesian_product(0..grid[0].len()) {
|
||||||
|
let neighbors = match grid[r][c] {
|
||||||
|
'#' => continue,
|
||||||
|
_ => NEIGHBORS,
|
||||||
|
};
|
||||||
|
let e = graph.entry((r,c)).or_default();
|
||||||
|
|
||||||
|
for (dr, dc) in neighbors {
|
||||||
|
let rr = (r as i64 + dr) as usize;
|
||||||
|
let cc = (c as i64 + dc) as usize;
|
||||||
|
if grid.get(rr).and_then(|row| row.get(cc)).is_some_and(|&t| t != '#') {
|
||||||
|
e.push((rr,cc,1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let corridors = graph.iter()
|
||||||
|
.filter(|(_, n)| n.len() == 2)
|
||||||
|
.map(|(&node, _)| node)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (r,c) in corridors {
|
||||||
|
let neighbors = graph.remove(&(r,c)).unwrap();
|
||||||
|
let (r1, c1, d1) = neighbors[0];
|
||||||
|
let (r2, c2, d2) = neighbors[1];
|
||||||
|
let n1 = graph.get_mut(&(r1,c1)).unwrap();
|
||||||
|
if let Some(i) = n1.iter().position(|&(rr,cc,_)| (rr,cc) == (r,c)) {
|
||||||
|
n1[i] = (r2,c2,d1+d2);
|
||||||
|
}
|
||||||
|
let n2 = graph.get_mut(&(r2,c2)).unwrap();
|
||||||
|
if let Some(i) = n2.iter().position(|&(rr,cc,_)| (rr,cc) == (r,c)) {
|
||||||
|
n2[i] = (r1,c1,d1+d2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dfs(&graph, &mut vec![vec![false; grid[0].len()]; grid.len()], (0,1)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 94);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 154);
|
||||||
|
}
|
||||||
|
}
|
144
src/day24.rs
Normal file
144
src/day24.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
//const RANGE: RangeInclusive<f64> = 7.0..=27.0;
|
||||||
|
const RANGE: RangeInclusive<f64> = 200_000_000_000_000.0..=400_000_000_000_000.0;
|
||||||
|
|
||||||
|
#[aoc_generator(day24)]
|
||||||
|
fn parse(input: &str) -> Vec<[f64; 6]> {
|
||||||
|
let input: Vec<[f64; 6]> = input.lines()
|
||||||
|
.map(|line| {
|
||||||
|
line.split("@")
|
||||||
|
.flat_map(|components| {
|
||||||
|
components.split(",")
|
||||||
|
.map(str::trim)
|
||||||
|
.map(|num| num.parse::<f64>().unwrap())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>().try_into().unwrap()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day24, part1)]
|
||||||
|
fn part1(input: &[[f64; 6]]) -> u32 {
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
// It's just solve the linear equations. I'm using a matrix.
|
||||||
|
for i in 1..input.len() {
|
||||||
|
for j in 0..i {
|
||||||
|
let [a, b, _, d, e, _] = input[i];
|
||||||
|
let [g, h, _, j, k, _] = input[j];
|
||||||
|
|
||||||
|
let determinant = e * j - d * k;
|
||||||
|
if determinant == 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = (j * (h - b) - k * (g - a)) / determinant;
|
||||||
|
let u = (d * (h - b) - e * (g - a)) / determinant;
|
||||||
|
|
||||||
|
let x = a + t * d;
|
||||||
|
let y = b + t * e;
|
||||||
|
|
||||||
|
if t >= 0.0 && u >= 0.0 && RANGE.contains(&x) && RANGE.contains(&y) {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day24, part2)]
|
||||||
|
fn part2(input: &[[f64; 6]]) -> f64 {
|
||||||
|
let [a, b, c, d, e, f] = input[0];
|
||||||
|
let [g, h, i, j, k, l] = input[1];
|
||||||
|
let [m, n, o, p, q, r] = input[2];
|
||||||
|
|
||||||
|
let mut matrix = [
|
||||||
|
[0.0, l - f, e - k, 0.0, c - i, h - b, e * c - b * f + h * l - k * i],
|
||||||
|
[0.0, r - f, e - q, 0.0, c - o, n - b, e * c - b * f + n * r - q * o],
|
||||||
|
[f - l, 0.0, j - d, i - c, 0.0, a - g, a * f - d * c + j * i - g * l],
|
||||||
|
[f - r, 0.0, p - d, o - c, 0.0, a - m, a * f - d * c + p * o - m * r],
|
||||||
|
[k - e, d - j, 0.0, b - h, g - a, 0.0, d * b - a * e + g * k - j * h],
|
||||||
|
[q - e, d - p, 0.0, b - n, m - a, 0.0, d * b - a * e + m * q - p * n],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Use Gaussian elimination to solve for the 6 unknowns.
|
||||||
|
// Forward elimination, processing columns from left to right.
|
||||||
|
// This will leave a matrix in row echelon form.
|
||||||
|
// That's right, I got a D in applied linear algebra.
|
||||||
|
for pivot in 0..6 {
|
||||||
|
// Make leading coefficient of each row positive to make subsequent calculations easier.
|
||||||
|
for row in &mut matrix[pivot..] {
|
||||||
|
if row[pivot] < 0.0 {
|
||||||
|
row.iter_mut().for_each(|num| *num = -*num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let column = matrix.map(|row| row[pivot]);
|
||||||
|
|
||||||
|
// If only one non-zero coefficient remaining in the column then we're done.
|
||||||
|
if column[pivot..].iter().filter(|&&c| c > 0.0).count() == 1 {
|
||||||
|
// Move this row into the pivot location
|
||||||
|
let index = column.iter().rposition(|&c| c > 0.0).unwrap();
|
||||||
|
matrix.swap(pivot, index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the row with the lowest non-zero leading coefficient.
|
||||||
|
let min = *column[pivot..].iter().filter(|&&c| c > 0.0).min_by(|a, b| a.total_cmp(b)).unwrap();
|
||||||
|
let index = column.iter().rposition(|&c| c == min).unwrap();
|
||||||
|
|
||||||
|
// Subtract as many multiples of this minimum row from each other row as possible
|
||||||
|
// to shrink the coefficients of our column towards zero.
|
||||||
|
for row in pivot..6 {
|
||||||
|
if row != index && column[row] != 0.0 {
|
||||||
|
let factor = column[row] / min;
|
||||||
|
|
||||||
|
for col in pivot..7 {
|
||||||
|
matrix[row][col] -= factor * matrix[index][col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back substitution, processing columns from right to left.
|
||||||
|
// This will leave the matrix in reduced row echelon form.
|
||||||
|
// The solved unknowns are then in the 7th column.
|
||||||
|
for pivot in (0..6).rev() {
|
||||||
|
matrix[pivot][6] /= matrix[pivot][pivot];
|
||||||
|
|
||||||
|
for row in 0..pivot {
|
||||||
|
matrix[row][6] -= matrix[pivot][6] * matrix[row][pivot];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(matrix[0][6] + matrix[1][6] + matrix[2][6]).floor()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"19, 13, 30 @ -2, 1, -2
|
||||||
|
18, 19, 22 @ -1, -1, -2
|
||||||
|
20, 25, 34 @ -2, -2, -4
|
||||||
|
12, 31, 28 @ -1, -2, -1
|
||||||
|
20, 19, 15 @ 1, -5, -3";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 47.0);
|
||||||
|
}
|
||||||
|
}
|
109
src/day25.rs
Normal file
109
src/day25.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use std::collections::{HashSet, HashMap};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Graph {
|
||||||
|
edges: Vec<(String, String)>,
|
||||||
|
vertex_count: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graph {
|
||||||
|
fn cut(&mut self) -> (usize, usize, usize) {
|
||||||
|
let mut contracted_edges = self.edges.clone();
|
||||||
|
let mut contracted_vertex_count = self.vertex_count;
|
||||||
|
let mut contracted: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
|
||||||
|
while contracted_vertex_count > 2 {
|
||||||
|
let random = rand::thread_rng().gen_range(0..contracted_edges.len());
|
||||||
|
let edge_to_contract = contracted_edges[random].clone();
|
||||||
|
if contracted.contains_key(&edge_to_contract.0) {
|
||||||
|
contracted.get_mut(&edge_to_contract.0.clone()).unwrap().push(edge_to_contract.1.clone());
|
||||||
|
} else {
|
||||||
|
contracted.insert(edge_to_contract.0.clone(), vec![edge_to_contract.1.clone()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if contracted.contains_key(&edge_to_contract.1) {
|
||||||
|
let mut to_append = contracted.clone().get_mut(&edge_to_contract.1).unwrap().clone();
|
||||||
|
contracted.get_mut(&edge_to_contract.0).unwrap().append(&mut to_append);
|
||||||
|
contracted.remove(&edge_to_contract.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_edges: Vec<(String, String)> = vec![];
|
||||||
|
|
||||||
|
for edge in contracted_edges {
|
||||||
|
if edge.1 == edge_to_contract.1 {
|
||||||
|
new_edges.push((edge.0.clone(), edge_to_contract.0.clone()));
|
||||||
|
} else if edge.0 == edge_to_contract.1 {
|
||||||
|
new_edges.push((edge_to_contract.0.clone(), edge.1.clone()));
|
||||||
|
} else {
|
||||||
|
new_edges.push(edge.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp = new_edges.iter().cloned().filter(|x| x.0 != x.1).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
contracted_edges = tmp;
|
||||||
|
contracted_vertex_count -= 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let counts = contracted.iter().map(|x| x.1.len() + 1).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
return (contracted_edges.len(), counts[0], *counts.last().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day25)]
|
||||||
|
fn parse(input: &str) -> Graph {
|
||||||
|
let input = input.lines().flat_map(|line| {
|
||||||
|
let line = line.split(' ').map(|x| x.trim_end_matches(':').to_string()).collect::<Vec<_>>();
|
||||||
|
line[1..].iter().cloned().map(|x| (line[0].clone(), x)).collect::<Vec<_>>()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let vertex_count = input.clone().into_iter().flat_map(|x| vec![x.0, x.1]).collect::<HashSet<_>>().len();
|
||||||
|
Graph {
|
||||||
|
edges: input,
|
||||||
|
vertex_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day25, part1)]
|
||||||
|
fn part1(graph: &Graph) -> usize {
|
||||||
|
let graph = &mut graph.clone();
|
||||||
|
let mut min_cut = usize::MAX;
|
||||||
|
let mut count1 = 0;
|
||||||
|
let mut count2 = 0;
|
||||||
|
|
||||||
|
while min_cut != 3 {
|
||||||
|
(min_cut, count1, count2) = graph.cut();
|
||||||
|
}
|
||||||
|
|
||||||
|
count1 * count2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"jqt: rhn xhk nvd
|
||||||
|
rsh: frs pzl lsr
|
||||||
|
xhk: hfx
|
||||||
|
cmg: qnr nvd lhk bvb
|
||||||
|
rhn: xhk bvb hfx
|
||||||
|
bvb: xhk hfx
|
||||||
|
pzl: lsr hfx nvd
|
||||||
|
qnr: nvd
|
||||||
|
ntq: jqt hfx bvb xhk
|
||||||
|
nvd: lhk
|
||||||
|
lsr: lhk
|
||||||
|
rzs: qnr cmg lsr rsh
|
||||||
|
frs: qnr lhk lsr";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 54);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,51 +1,22 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use array2d::Array2D;
|
use array2d::Array2D;
|
||||||
|
|
||||||
// pub fn main() {
|
#[aoc_generator(day3)]
|
||||||
// let input = fs::read_to_string("input.txt").unwrap();
|
fn parse(input: &str) -> Array2D<char> {
|
||||||
// let array_2d = get_map(&input);
|
let rows: Vec<&str> = input.split("\n").collect();
|
||||||
// // let mut r = Vec::new();
|
let mut array = Vec::new();
|
||||||
// let mut stars_count: HashMap<(usize, usize), Vec<u32>> = HashMap::new();
|
for row in rows {
|
||||||
// for (y, row_iter) in array_2d.rows_iter().enumerate() {
|
let row_vec: Vec<char> = row.chars().collect();
|
||||||
// let mut checked = false;
|
array.push(row_vec);
|
||||||
// for (x, element) in row_iter.enumerate() {
|
}
|
||||||
// let d: char = element.clone();
|
|
||||||
// if d.is_digit(10) && !checked {
|
|
||||||
// let star_vec = get_neighboring_star(x, y, &array_2d);
|
|
||||||
// if !star_vec.is_empty() {
|
|
||||||
// let (x_star, y_star, _) = star_vec.first().unwrap().clone();
|
|
||||||
// let key = (x_star, y_star);
|
|
||||||
// let gear = get_number(x, y, &array_2d);
|
|
||||||
// if stars_count.contains_key(&key) {
|
|
||||||
// let mut v: Vec<u32> = stars_count.get(&(x_star, y_star)).unwrap().clone();
|
|
||||||
// v.push(gear);
|
|
||||||
// stars_count.insert(key, v);
|
|
||||||
// } else {
|
|
||||||
// stars_count.insert(key, vec![gear]);
|
|
||||||
// }
|
|
||||||
// checked = true
|
|
||||||
// }
|
|
||||||
// } else if !d.is_digit(10) {
|
|
||||||
// checked = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// let r = stars_count.iter().fold(0u32, |acc, (_, gears)| {
|
|
||||||
// if gears.len() == 2 {
|
|
||||||
// acc + gears.first().unwrap() * gears.last().unwrap()
|
|
||||||
// } else {
|
|
||||||
// acc
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// println!("{}", r);
|
Array2D::from_rows(&array).unwrap()
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn main() {
|
#[aoc(day3, part1)]
|
||||||
let input = fs::read_to_string("input.txt").unwrap();
|
fn part1(array_2d: &Array2D<char>) -> u32 {
|
||||||
let array_2d = get_map(&input);
|
|
||||||
let mut r = 0;
|
let mut r = 0;
|
||||||
for (y, row_iter) in array_2d.rows_iter().enumerate() {
|
for (y, row_iter) in array_2d.rows_iter().enumerate() {
|
||||||
let mut checked = false;
|
let mut checked = false;
|
||||||
@@ -63,18 +34,7 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", r);
|
r
|
||||||
}
|
|
||||||
|
|
||||||
fn get_map(input: &str) -> Array2D<char> {
|
|
||||||
let rows: Vec<&str> = input.split("\n").collect();
|
|
||||||
let mut array = Vec::new();
|
|
||||||
for row in rows {
|
|
||||||
let row_vec: Vec<char> = row.chars().collect();
|
|
||||||
array.push(row_vec);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array2D::from_rows(&array).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_number(x: usize, y: usize, array2d: &Array2D<char>) -> u32 {
|
fn get_number(x: usize, y: usize, array2d: &Array2D<char>) -> u32 {
|
||||||
@@ -130,31 +90,10 @@ fn get_neighbors(x: usize, y: usize, array2d: &Array2D<char>) -> Vec<&char> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_neighboring_star(x: usize, y: usize, array2d: &Array2D<char>) -> Vec<(usize, usize, Option<&char>)> {
|
|
||||||
let mut neighbors: Vec<(usize, usize, Option<&char>)> = Vec::new();
|
|
||||||
neighbors.push((x.checked_add(1).unwrap(), y, array2d.get(y, x.checked_add(1).unwrap())));
|
|
||||||
if x > 0 {
|
|
||||||
neighbors.push((x.checked_sub(1).unwrap(), y, array2d.get(y, x.checked_sub(1).unwrap())));
|
|
||||||
neighbors.push((x.checked_sub(1).unwrap(), y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x.checked_sub(1).unwrap())));
|
|
||||||
}
|
|
||||||
if y > 0 {
|
|
||||||
neighbors.push((x, y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x)));
|
|
||||||
neighbors.push((x.checked_add(1).unwrap(), y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x.checked_add(1).unwrap())));
|
|
||||||
}
|
|
||||||
if x > 0 && y > 0 {
|
|
||||||
neighbors.push((x.checked_sub(1).unwrap(), y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x.checked_sub(1).unwrap())));
|
|
||||||
}
|
|
||||||
neighbors.push((x, y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x)));
|
|
||||||
neighbors.push((x.checked_add(1).unwrap(), y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x.checked_add(1).unwrap())));
|
|
||||||
|
|
||||||
neighbors
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(_, _, c)| c.is_some() && c.unwrap() == &'*')
|
|
||||||
.collect::<Vec<(usize, usize, Option<&char>)>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn part_two(input: &str) -> Option<u32> {
|
#[aoc(day3, part2)]
|
||||||
let array_2d = get_map(input);
|
fn part2(array_2d: &Array2D<char>) -> u32 {
|
||||||
// let mut r = Vec::new();
|
// let mut r = Vec::new();
|
||||||
let mut stars_count: HashMap<(usize, usize), Vec<u32>> = HashMap::new();
|
let mut stars_count: HashMap<(usize, usize), Vec<u32>> = HashMap::new();
|
||||||
for (y, row_iter) in array_2d.rows_iter().enumerate() {
|
for (y, row_iter) in array_2d.rows_iter().enumerate() {
|
||||||
@@ -189,5 +128,54 @@ pub fn part_two(input: &str) -> Option<u32> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(r)
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_neighboring_star(x: usize, y: usize, array2d: &Array2D<char>) -> Vec<(usize, usize, Option<&char>)> {
|
||||||
|
let mut neighbors: Vec<(usize, usize, Option<&char>)> = Vec::new();
|
||||||
|
neighbors.push((x.checked_add(1).unwrap(), y, array2d.get(y, x.checked_add(1).unwrap())));
|
||||||
|
if x > 0 {
|
||||||
|
neighbors.push((x.checked_sub(1).unwrap(), y, array2d.get(y, x.checked_sub(1).unwrap())));
|
||||||
|
neighbors.push((x.checked_sub(1).unwrap(), y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x.checked_sub(1).unwrap())));
|
||||||
|
}
|
||||||
|
if y > 0 {
|
||||||
|
neighbors.push((x, y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x)));
|
||||||
|
neighbors.push((x.checked_add(1).unwrap(), y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x.checked_add(1).unwrap())));
|
||||||
|
}
|
||||||
|
if x > 0 && y > 0 {
|
||||||
|
neighbors.push((x.checked_sub(1).unwrap(), y.checked_sub(1).unwrap(), array2d.get(y.checked_sub(1).unwrap(), x.checked_sub(1).unwrap())));
|
||||||
|
}
|
||||||
|
neighbors.push((x, y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x)));
|
||||||
|
neighbors.push((x.checked_add(1).unwrap(), y.checked_add(1).unwrap(), array2d.get(y.checked_add(1).unwrap(), x.checked_add(1).unwrap())));
|
||||||
|
|
||||||
|
neighbors
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, _, c)| c.is_some() && c.unwrap() == &'*')
|
||||||
|
.collect::<Vec<(usize, usize, Option<&char>)>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 4361);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 467835);
|
||||||
|
}
|
||||||
}
|
}
|
80
src/day4.rs
80
src/day4.rs
@@ -1,17 +1,72 @@
|
|||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
#[aoc_generator(day4)]
|
#[aoc_generator(day4)]
|
||||||
fn parse(input: &str) -> String {
|
fn parse(input: &str) -> Vec<Vec<Vec<i32>>> {
|
||||||
todo!()
|
let input: Vec<Vec<Vec<i32>>> = input.lines()
|
||||||
|
.map(|card| &card[(card.find(':').unwrap() + 1)..])
|
||||||
|
.map(|card| card.trim())
|
||||||
|
.map(|card| {
|
||||||
|
card.split('|')
|
||||||
|
.map(|numbers| {
|
||||||
|
numbers.split_whitespace()
|
||||||
|
.map(|num| num.parse::<i32>().unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day4, part1)]
|
#[aoc(day4, part1)]
|
||||||
fn part1(input: &str) -> String {
|
fn part1(input: &Vec<Vec<Vec<i32>>>) -> i32 {
|
||||||
todo!()
|
let mut total_pts = 0;
|
||||||
|
|
||||||
|
for card in input {
|
||||||
|
let mut card_pts = 0;
|
||||||
|
let winning = card.first().unwrap();
|
||||||
|
let ours = card.last().unwrap();
|
||||||
|
|
||||||
|
for num in ours {
|
||||||
|
if winning.contains(num) {
|
||||||
|
if card_pts == 0 {
|
||||||
|
card_pts = 1;
|
||||||
|
} else {
|
||||||
|
card_pts *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total_pts += card_pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_pts
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day4, part2)]
|
#[aoc(day4, part2)]
|
||||||
fn part2(input: &str) -> String {
|
fn part2(input: &Vec<Vec<Vec<i32>>>) -> i32 {
|
||||||
todo!()
|
let mut queue = VecDeque::from((0..input.len()).collect::<Vec<usize>>());
|
||||||
|
|
||||||
|
let mut total_cards = 0;
|
||||||
|
while !queue.is_empty() {
|
||||||
|
let card_num = queue.pop_front().unwrap();
|
||||||
|
let card = input.get(card_num).unwrap();
|
||||||
|
total_cards += 1;
|
||||||
|
let mut dup_cards = 0;
|
||||||
|
let winning_nums = card.first().unwrap();
|
||||||
|
let our_nums = card.last().unwrap();
|
||||||
|
|
||||||
|
for num in our_nums {
|
||||||
|
if winning_nums.contains(num) {
|
||||||
|
dup_cards += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for card in (card_num + 1)..=(card_num + dup_cards) {
|
||||||
|
if card < input.len() {
|
||||||
|
queue.push_back(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total_cards
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -19,13 +74,20 @@ fn part2(input: &str) -> String {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_example() {
|
fn part1_example() {
|
||||||
assert_eq!(part1(&parse("<EXAMPLE>")), "<RESULT>");
|
assert_eq!(part1(&parse(EX)), 13);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_example() {
|
fn part2_example() {
|
||||||
assert_eq!(part2(&parse("<EXAMPLE>")), "<RESULT>");
|
assert_eq!(part2(&parse(EX)), 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
136
src/day5.rs
Normal file
136
src/day5.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
|
||||||
|
#[aoc_generator(day5)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<Vec<i64>>> {
|
||||||
|
let mappers: Vec<Vec<Vec<i64>>> = input.split("\n\n")
|
||||||
|
.map(|maps| &maps[(maps.find(':').unwrap() + 1)..])
|
||||||
|
.map(|maps| maps.trim())
|
||||||
|
.map(|maps| {
|
||||||
|
maps.split('\n')
|
||||||
|
.map(|range| {
|
||||||
|
range.split(' ')
|
||||||
|
.map(|num| num.parse::<i64>().unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
mappers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day5, part1)]
|
||||||
|
fn part1(mappers: &Vec<Vec<Vec<i64>>>) -> i64 {
|
||||||
|
let seeds = mappers.first().unwrap().first().unwrap().clone();
|
||||||
|
let mut borrow_work_around: Vec<Vec<Vec<i64>>> = Vec::new();
|
||||||
|
mappers.clone_into(&mut borrow_work_around);
|
||||||
|
let mappers: &mut [Vec<Vec<i64>>] = &mut borrow_work_around.get_mut(1..).unwrap();
|
||||||
|
|
||||||
|
for mapper in mappers.into_iter() {
|
||||||
|
for range in mapper {
|
||||||
|
range[2] = range[1] + range[2] - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cur_vals: Vec<_> = seeds;
|
||||||
|
for mapper in mappers {
|
||||||
|
for val in cur_vals.iter_mut() {
|
||||||
|
for range in mapper.into_iter() {
|
||||||
|
if range[1] <= *val && *val <= range[2] {
|
||||||
|
let diff = *val - range[1];
|
||||||
|
*val = range[0] + diff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_vals.into_iter().min().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day5, part2)]
|
||||||
|
fn part2(mappers: &Vec<Vec<Vec<i64>>>) -> i64 {
|
||||||
|
let seeds = mappers.first().unwrap().first().unwrap().clone();
|
||||||
|
let mut borrow_work_around: Vec<Vec<Vec<i64>>> = Vec::new();
|
||||||
|
mappers.clone_into(&mut borrow_work_around);
|
||||||
|
let mappers: &mut [Vec<Vec<i64>>] = &mut borrow_work_around.get_mut(1..).unwrap();
|
||||||
|
|
||||||
|
for mapper in mappers.into_iter() {
|
||||||
|
for range in mapper {
|
||||||
|
range[2] = range[1] + range[2] - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cur_vals: Vec<_> = Vec::new();
|
||||||
|
for i in 0..seeds.len() / 2 {
|
||||||
|
let end = seeds[i * 2] + seeds[(i * 2) + 1];
|
||||||
|
let mut range: Vec<_> = (seeds[i * 2]..end).collect();
|
||||||
|
cur_vals.append(&mut range);
|
||||||
|
}
|
||||||
|
println!("{}", cur_vals.len());
|
||||||
|
for mapper in mappers {
|
||||||
|
for val in cur_vals.iter_mut() {
|
||||||
|
println!("{}", val);
|
||||||
|
for range in mapper.into_iter() {
|
||||||
|
if range[1] <= *val && *val <= range[2] {
|
||||||
|
let diff = *val - range[1];
|
||||||
|
*val = range[0] + diff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_vals.into_iter().min().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 46);
|
||||||
|
}
|
||||||
|
}
|
80
src/day6.rs
Normal file
80
src/day6.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day6, part1)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<i32>> {
|
||||||
|
let input: Vec<Vec<i32>> = input.split('\n') // Separate the Time and Distance lines
|
||||||
|
.map(|line| {
|
||||||
|
line[line.find(':').unwrap() + 1..] // Drop "Time:" and "Distance:"
|
||||||
|
.split_whitespace() // Split the numbers into their own elements.
|
||||||
|
.map(|num| num.parse::<i32>().expect("Couldn't parse number")) // Parse numbers into i32
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}).collect::<Vec<_>>(); // collect into Vec
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day6, part1)]
|
||||||
|
fn part1(input: &Vec<Vec<i32>>) -> i32 {
|
||||||
|
let mut valid_total = 1;
|
||||||
|
|
||||||
|
for round in 0..input.first().unwrap().len() {
|
||||||
|
let time = input[0][round];
|
||||||
|
let dist = input[1][round];
|
||||||
|
let mut valid = 0;
|
||||||
|
|
||||||
|
for remaining_time in 0..time {
|
||||||
|
if (time - remaining_time) * remaining_time > dist {
|
||||||
|
valid += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valid_total *= valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_total
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day6, part2)]
|
||||||
|
fn parse_part2(input: &str) -> Vec<i64> {
|
||||||
|
let input: Vec<i64> = input.split('\n') // Separate the Time and Distance lines
|
||||||
|
.map(|line| {
|
||||||
|
line[line.find(':').unwrap() + 1..] // Drop "Time:" and "Distance:"
|
||||||
|
.split_whitespace() // Split the numbers into their own elements
|
||||||
|
.flat_map(|s| s.chars()).collect::<String>() // Combine the strings into a single one
|
||||||
|
.parse::<i64>().expect("Couldn't parse number") // Parse numbers into i32
|
||||||
|
}).collect(); // Collect into Vec
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day6, part2)]
|
||||||
|
fn part2(input: &Vec<i64>) -> i32 {
|
||||||
|
let time = input[0];
|
||||||
|
let dist = input[1];
|
||||||
|
let mut valid = 0;
|
||||||
|
|
||||||
|
for remaining_time in 0..time {
|
||||||
|
if (time - remaining_time) * remaining_time > dist {
|
||||||
|
valid += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"Time: 7 15 30
|
||||||
|
Distance: 9 40 200";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 288);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse_part2(EX)), 71503);
|
||||||
|
}
|
||||||
|
}
|
207
src/day7.rs
Normal file
207
src/day7.rs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day7)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<String>> {
|
||||||
|
let input: Vec<Vec<String>> = input.split('\n')
|
||||||
|
.map(|line| line.split(' ').map(|x| x.into()).collect::<Vec<String>>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day7, part1)]
|
||||||
|
fn part1(input: &Vec<Vec<String>>) -> usize {
|
||||||
|
let mut tmp: Vec<Vec<String>> = Vec::new();
|
||||||
|
input.clone_into(&mut tmp);
|
||||||
|
let mut input = tmp;
|
||||||
|
|
||||||
|
for line in 0..input.len() {
|
||||||
|
let hand = &input[line][0];
|
||||||
|
let mut card_freq: HashMap<char, i16> = HashMap::new();
|
||||||
|
for card in hand.chars() {
|
||||||
|
card_freq.entry(card)
|
||||||
|
.and_modify(|count| *count += 1)
|
||||||
|
.or_insert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut set_count: HashMap<i16, i16> = HashMap::new();
|
||||||
|
for i in 1..=5 {
|
||||||
|
let card_count = card_freq.values().filter(|x| **x == i).count().try_into().unwrap();
|
||||||
|
if card_count != 0 {
|
||||||
|
set_count.insert(i, card_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let power = match set_count {
|
||||||
|
x if x.contains_key(&5) => "7",
|
||||||
|
x if x.contains_key(&4) => "6",
|
||||||
|
x if x.contains_key(&3) && x.contains_key(&2) => "5",
|
||||||
|
x if x.contains_key(&3) => "4",
|
||||||
|
x if x.get(&2).unwrap_or(&0) >= &2 => "3",
|
||||||
|
x if x.get(&2).unwrap_or(&0) == &1 => "2",
|
||||||
|
HashMap { .. } => "1"
|
||||||
|
};
|
||||||
|
|
||||||
|
input[line].push(power.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
input.sort_by(|lhs, rhs| {
|
||||||
|
let lhs_power: i32 = lhs[2].parse().unwrap();
|
||||||
|
let rhs_power: i32 = rhs[2].parse().unwrap();
|
||||||
|
if lhs_power != rhs_power {
|
||||||
|
return lhs_power.cmp(&rhs_power);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lhs_hand: Vec<i32> = lhs[0].chars().map(card_value).collect();
|
||||||
|
let rhs_hand: Vec<i32> = rhs[0].chars().map(card_value).collect();
|
||||||
|
for i in 0..5 {
|
||||||
|
if lhs_hand[i] == rhs_hand[i] { continue; }
|
||||||
|
return lhs_hand[i].cmp(&rhs_hand[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Should not be reachable");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let mut total_winnings = 0;
|
||||||
|
for i in 0..input.len() {
|
||||||
|
let bid: usize = input[i][1].parse().unwrap();
|
||||||
|
total_winnings += (i + 1) * bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_winnings
|
||||||
|
}
|
||||||
|
|
||||||
|
fn card_value(card: char) -> i32 {
|
||||||
|
match card {
|
||||||
|
'A' => 13,
|
||||||
|
'K' => 12,
|
||||||
|
'Q' => 11,
|
||||||
|
'J' => 10,
|
||||||
|
'T' => 9,
|
||||||
|
'9' => 8,
|
||||||
|
'8' => 7,
|
||||||
|
'7' => 6,
|
||||||
|
'6' => 5,
|
||||||
|
'5' => 4,
|
||||||
|
'4' => 3,
|
||||||
|
'3' => 2,
|
||||||
|
'2' => 1,
|
||||||
|
_ => panic!("invalid card")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day7, part2)]
|
||||||
|
fn part2(input: &Vec<Vec<String>>) -> usize {
|
||||||
|
let mut tmp: Vec<Vec<String>> = Vec::new();
|
||||||
|
input.clone_into(&mut tmp);
|
||||||
|
let mut input = tmp;
|
||||||
|
|
||||||
|
for line in 0..input.len() {
|
||||||
|
let hand = &input[line][0];
|
||||||
|
if hand == "JJJJJ" {
|
||||||
|
input[line].push("7".to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut card_freq: HashMap<char, i16> = HashMap::new();
|
||||||
|
let joker_count: i16 = hand.chars().filter(|c| c == &'J').count().try_into().unwrap();
|
||||||
|
for card in hand.chars().filter(|c| c != &'J') {
|
||||||
|
card_freq.entry(card)
|
||||||
|
.and_modify(|count| *count += 1)
|
||||||
|
.or_insert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The most helpful place for the jokers will always be with the max card count
|
||||||
|
let max = card_freq.clone().into_iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();
|
||||||
|
card_freq.entry(max.0)
|
||||||
|
.and_modify(|count| *count += joker_count);
|
||||||
|
|
||||||
|
let mut set_count: HashMap<i16, i16> = HashMap::new();
|
||||||
|
for i in 1..=5 {
|
||||||
|
let card_count = card_freq.values().filter(|x| **x == i).count().try_into().unwrap();
|
||||||
|
if card_count != 0 {
|
||||||
|
set_count.insert(i, card_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let power = match set_count {
|
||||||
|
x if x.contains_key(&5) => "7",
|
||||||
|
x if x.contains_key(&4) => "6",
|
||||||
|
x if x.contains_key(&3) && x.contains_key(&2) => "5",
|
||||||
|
x if x.contains_key(&3) => "4",
|
||||||
|
x if x.get(&2).unwrap_or(&0) >= &2 => "3",
|
||||||
|
x if x.get(&2).unwrap_or(&0) == &1 => "2",
|
||||||
|
HashMap { .. } => "1"
|
||||||
|
};
|
||||||
|
|
||||||
|
input[line].push(power.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
input.sort_by(|lhs, rhs| {
|
||||||
|
let lhs_power: i32 = lhs[2].parse().unwrap();
|
||||||
|
let rhs_power: i32 = rhs[2].parse().unwrap();
|
||||||
|
if lhs_power != rhs_power {
|
||||||
|
return lhs_power.cmp(&rhs_power);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lhs_hand: Vec<i32> = lhs[0].chars().map(card_value_pt2).collect();
|
||||||
|
let rhs_hand: Vec<i32> = rhs[0].chars().map(card_value_pt2).collect();
|
||||||
|
for i in 0..5 {
|
||||||
|
if lhs_hand[i] == rhs_hand[i] { continue; }
|
||||||
|
return lhs_hand[i].cmp(&rhs_hand[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Should not be reachable");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let mut total_winnings = 0;
|
||||||
|
for i in 0..input.len() {
|
||||||
|
let bid: usize = input[i][1].parse().unwrap();
|
||||||
|
total_winnings += (i + 1) * bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_winnings
|
||||||
|
}
|
||||||
|
|
||||||
|
fn card_value_pt2(card: char) -> i32 {
|
||||||
|
match card {
|
||||||
|
'A' => 13,
|
||||||
|
'K' => 12,
|
||||||
|
'Q' => 11,
|
||||||
|
'T' => 10,
|
||||||
|
'9' => 9,
|
||||||
|
'8' => 8,
|
||||||
|
'7' => 7,
|
||||||
|
'6' => 6,
|
||||||
|
'5' => 5,
|
||||||
|
'4' => 4,
|
||||||
|
'3' => 3,
|
||||||
|
'2' => 2,
|
||||||
|
'J' => 1,
|
||||||
|
_ => panic!("invalid card")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 6440);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 5905);
|
||||||
|
}
|
||||||
|
}
|
150
src/day8.rs
Normal file
150
src/day8.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day8)]
|
||||||
|
fn parse(input: &str) -> (Vec<Direction>, HashMap<String, (String, String)>) {
|
||||||
|
let input: Vec<_> = input.split("\n\n").collect();
|
||||||
|
let (directions, nodes) = (input[0], input[1]);
|
||||||
|
let directions: Vec<Direction> = directions.chars().map(|char| {
|
||||||
|
match char {
|
||||||
|
'L' => Direction::Left,
|
||||||
|
'R' => Direction::Right,
|
||||||
|
_ => panic!("Invalid direction!")
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let nodes: HashMap<String, (String, String)> = nodes.split('\n')
|
||||||
|
.map(|line| {
|
||||||
|
let line = line.split('=').map(|x| x.trim()).collect::<Vec<_>>();
|
||||||
|
let children = line[1].trim_matches(|c| c == '(' || c == ')').split(", ").collect::<Vec<_>>();
|
||||||
|
(line[0].into(), (children[0].into(), children[1].into()))
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
(directions, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day8, part1)]
|
||||||
|
fn part1((directions, nodes): &(Vec<Direction>, HashMap<String, (String, String)>)) -> i64 {
|
||||||
|
let mut cur_node = "AAA";
|
||||||
|
let mut step_queue = VecDeque::from(directions.clone());
|
||||||
|
let mut step_count = 0;
|
||||||
|
|
||||||
|
while cur_node != "ZZZ" {
|
||||||
|
step_count += 1;
|
||||||
|
let cur_step = step_queue.pop_front().unwrap();
|
||||||
|
match cur_step {
|
||||||
|
Direction::Left => cur_node = &nodes[cur_node].0,
|
||||||
|
Direction::Right => cur_node = &nodes[cur_node].1,
|
||||||
|
}
|
||||||
|
step_queue.push_back(cur_step);
|
||||||
|
}
|
||||||
|
println!("{}", step_count);
|
||||||
|
step_count
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day8, part2)]
|
||||||
|
fn part2((directions, nodes): &(Vec<Direction>, HashMap<String, (String, String)>)) -> i64 {
|
||||||
|
let starts: Vec<_> = nodes.keys().filter(|x| x.ends_with('A')).collect();
|
||||||
|
let dists: Vec<_> = starts.iter().map(|start| dist(&start, &directions, nodes)).collect();
|
||||||
|
|
||||||
|
let gcf = gcf(&dists);
|
||||||
|
|
||||||
|
let step_count = gcf * dists.iter().map(|value| value / gcf).product::<i64>();
|
||||||
|
|
||||||
|
step_count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gcf(values: &Vec<i64>) -> i64 {
|
||||||
|
let mut gcf = values[0];
|
||||||
|
|
||||||
|
for val in values {
|
||||||
|
gcf = find_gcf(gcf, *val);
|
||||||
|
|
||||||
|
if gcf == 1 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gcf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_gcf(a: i64, b: i64) -> i64 {
|
||||||
|
if a == 0 {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
find_gcf(b % a, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dist(cur_node: &str, directions: &Vec<Direction>, nodes: &HashMap<String, (String, String)>) -> i64 {
|
||||||
|
let mut cur_node = cur_node;
|
||||||
|
let mut step_queue: VecDeque<Direction> = VecDeque::from(directions.clone());
|
||||||
|
let mut step_count = 0;
|
||||||
|
|
||||||
|
while !cur_node.ends_with('Z') {
|
||||||
|
step_count += 1;
|
||||||
|
let cur_step = step_queue.pop_front().unwrap();
|
||||||
|
match cur_step {
|
||||||
|
Direction::Left => cur_node = &nodes[cur_node].0,
|
||||||
|
Direction::Right => cur_node = &nodes[cur_node].1,
|
||||||
|
}
|
||||||
|
step_queue.push_back(cur_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
return step_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX_1: &str = r"RL
|
||||||
|
|
||||||
|
AAA = (BBB, CCC)
|
||||||
|
BBB = (DDD, EEE)
|
||||||
|
CCC = (ZZZ, GGG)
|
||||||
|
DDD = (DDD, DDD)
|
||||||
|
EEE = (EEE, EEE)
|
||||||
|
GGG = (GGG, GGG)
|
||||||
|
ZZZ = (ZZZ, ZZZ)";
|
||||||
|
|
||||||
|
const EX_2: &str = r"LLR
|
||||||
|
|
||||||
|
AAA = (BBB, BBB)
|
||||||
|
BBB = (AAA, ZZZ)
|
||||||
|
ZZZ = (ZZZ, ZZZ)";
|
||||||
|
|
||||||
|
const EX_3: &str = r"LR
|
||||||
|
|
||||||
|
11A = (11B, XXX)
|
||||||
|
11B = (XXX, 11Z)
|
||||||
|
11Z = (11B, XXX)
|
||||||
|
22A = (22B, XXX)
|
||||||
|
22B = (22C, 22C)
|
||||||
|
22C = (22Z, 22Z)
|
||||||
|
22Z = (22B, 22B)
|
||||||
|
XXX = (XXX, XXX)";
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example1() {
|
||||||
|
assert_eq!(part1(&parse(EX_1)), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example2() {
|
||||||
|
assert_eq!(part1(&parse(EX_2)), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX_3)), 6);
|
||||||
|
}
|
||||||
|
}
|
85
src/day9.rs
Normal file
85
src/day9.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
#[aoc_generator(day9)]
|
||||||
|
fn parse(input: &str) -> Vec<Vec<i64>> {
|
||||||
|
let input: Vec<_> = input.split('\n')
|
||||||
|
.map(|line| {
|
||||||
|
line.split_whitespace()
|
||||||
|
.map(|num| num.parse::<i64>().unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day9, part1)]
|
||||||
|
fn part1(input: &Vec<Vec<i64>>) -> i64 {
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for line in input {
|
||||||
|
let mut line_enders: Vec<i64> = vec![*line.last().unwrap()];
|
||||||
|
let mut cur_line: Vec<i64> = line.to_vec();
|
||||||
|
while !cur_line.iter().all(|x| *x == 0) {
|
||||||
|
let mut next_line: Vec<i64> = vec![];
|
||||||
|
for i in 1..cur_line.len() {
|
||||||
|
let diff = cur_line[i] - cur_line[i-1];
|
||||||
|
next_line.push(diff)
|
||||||
|
}
|
||||||
|
line_enders.push(*next_line.last().unwrap());
|
||||||
|
cur_line = next_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sum = 0;
|
||||||
|
for i in (1..line_enders.len()).rev() {
|
||||||
|
sum = sum + line_enders[i - 1];
|
||||||
|
}
|
||||||
|
total += sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day9, part2)]
|
||||||
|
fn part2(input: &Vec<Vec<i64>>) -> i64 {
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for line in input {
|
||||||
|
let mut line_starters: Vec<i64> = vec![*line.first().unwrap()];
|
||||||
|
let mut cur_line: Vec<i64> = line.to_vec();
|
||||||
|
while !cur_line.iter().all(|x| *x == 0) {
|
||||||
|
let mut next_line: Vec<i64> = vec![];
|
||||||
|
for i in 1..cur_line.len() {
|
||||||
|
let diff = cur_line[i] - cur_line[i-1];
|
||||||
|
next_line.push(diff)
|
||||||
|
}
|
||||||
|
line_starters.push(*next_line.first().unwrap());
|
||||||
|
cur_line = next_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sum = 0;
|
||||||
|
for i in (1..line_starters.len()).rev() {
|
||||||
|
sum = line_starters[i - 1] - sum;
|
||||||
|
}
|
||||||
|
total += sum;
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EX: &str = r"0 3 6 9 12 15
|
||||||
|
1 3 6 10 15 21
|
||||||
|
10 13 16 21 30 45";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(&parse(EX)), 114);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
assert_eq!(part2(&parse(EX)), 2);
|
||||||
|
}
|
||||||
|
}
|
22
src/lib.rs
22
src/lib.rs
@@ -1,4 +1,26 @@
|
|||||||
|
mod day25;
|
||||||
|
mod day24;
|
||||||
|
mod day23;
|
||||||
|
mod day22;
|
||||||
|
mod day21;
|
||||||
|
mod day20;
|
||||||
|
mod day19;
|
||||||
|
mod day18;
|
||||||
|
mod day17;
|
||||||
|
mod day16;
|
||||||
|
mod day15;
|
||||||
|
mod day14;
|
||||||
|
mod day13;
|
||||||
|
mod day12;
|
||||||
|
mod day11;
|
||||||
|
mod day10;
|
||||||
|
mod day9;
|
||||||
|
mod day8;
|
||||||
|
mod day7;
|
||||||
|
mod day6;
|
||||||
|
mod day5;
|
||||||
mod day4;
|
mod day4;
|
||||||
|
mod day3;
|
||||||
mod day2;
|
mod day2;
|
||||||
mod day1;
|
mod day1;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user