Compare commits

...

26 Commits

Author SHA1 Message Date
50472363cf cleanup 2023-12-27 12:33:32 -05:00
2c4288de42 day25 2023-12-27 12:28:30 -05:00
14c6da72d9 day24 2023-12-27 10:20:20 -05:00
bdc150d0ed day23 2023-12-27 09:04:34 -05:00
b802cf1d16 day22 2023-12-22 10:31:00 -05:00
9db0e3f054 day21 2023-12-21 15:21:44 -05:00
a19a3258c2 day20 2023-12-21 10:10:57 -05:00
d3c11dcbed day19 2023-12-20 12:05:52 -05:00
e3f0f932e4 day18 part2 2023-12-20 10:10:02 -05:00
8b3bbab954 day18 part1 2023-12-20 09:34:15 -05:00
05a71ce349 day17 2023-12-19 16:10:35 -05:00
6e0f7b3c2f day16 2023-12-19 10:57:36 -05:00
e0655bd700 day15 2023-12-18 16:16:26 -05:00
7e082e6558 update day11 2023-12-18 12:16:50 -05:00
4e494e5596 update day10 2023-12-18 12:08:43 -05:00
0645f39e51 update day9 2023-12-18 11:50:21 -05:00
831d97d5b4 update day8 2023-12-18 11:43:37 -05:00
1c4bf97e07 update day7 2023-12-18 09:38:38 -05:00
593851daee update day6 2023-12-18 09:37:54 -05:00
c3849c1ad4 update day5 2023-12-18 09:37:35 -05:00
4980b285e8 update day3 2023-12-18 09:36:31 -05:00
bece6b708b day14 part2 2023-12-14 15:26:10 -05:00
97d8acbad8 day14 part1 2023-12-14 14:31:43 -05:00
ffb43d4860 day13 2023-12-14 12:54:47 -05:00
be7ca6666d day12 2023-12-14 11:39:20 -05:00
39633cc659 rewrote day4 2023-12-14 11:03:55 -05:00
61 changed files with 3619 additions and 1532 deletions

237
Cargo.lock generated
View File

@@ -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"

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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",
]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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"

View File

@@ -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"

View File

@@ -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
View File

@@ -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"

View File

@@ -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]

View File

@@ -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
View File

@@ -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",
]

View File

@@ -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"

View File

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

View File

@@ -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);
}
} }

View File

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

View File

@@ -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;