Compare commits
9 Commits
8f46feaf69
...
b750d99261
Author | SHA1 | Date |
---|---|---|
|
b750d99261 | |
|
45481154d1 | |
|
cf3d410dc7 | |
|
428efcbfc1 | |
|
d2fdbae3ae | |
|
d79b52774b | |
|
b99c652662 | |
|
b3ffab172d | |
|
b531b180e8 |
|
@ -1,3 +1,4 @@
|
||||||
*.beam
|
*.beam
|
||||||
*.dump
|
*.dump
|
||||||
*.swp
|
*.swp
|
||||||
|
target
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
input
|
|
@ -0,0 +1,129 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d21ef9204ad206a5a3e918e9920da04e1118ad91ce4f23570be964b9d6b9dfcb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner-derive"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba8b944269d3fee645d281b1335e1797044db497bb02d0098cc3fdb8900069cc"
|
||||||
|
dependencies = [
|
||||||
|
"aoc-runner-internal",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc-runner-internal"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "274b0ba7f3669a45ec0aaacf94eb032a749de880ab776091576cca94037c9982"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc2023"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc-runner",
|
||||||
|
"aoc-runner-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
|
[[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 = "ryu"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.193"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.193"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.108"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "aoc2023"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "aoc2023"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aoc-runner = "0.3.0"
|
||||||
|
aoc-runner-derive = "0.3.0"
|
|
@ -0,0 +1,139 @@
|
||||||
|
use aoc_runner_derive::aoc;
|
||||||
|
|
||||||
|
fn parse(input: &str) -> Vec<u32> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| {
|
||||||
|
let v: Vec<&str> = l.matches(char::is_numeric).collect();
|
||||||
|
if !v.is_empty() {
|
||||||
|
let tens = v[0].parse::<u32>().unwrap() * 10;
|
||||||
|
let ones = v[v.len() - 1].parse::<u32>().unwrap();
|
||||||
|
Some(tens + ones)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day1, part1)]
|
||||||
|
fn part1_sum(input: &str) -> u32 {
|
||||||
|
let lines = parse(input);
|
||||||
|
lines.iter().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day1, part2)]
|
||||||
|
fn part2_sum(input: &str) -> u32 {
|
||||||
|
sub_letters_for_digits(input).iter().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn char_to_num(c: char) -> Option<u32> {
|
||||||
|
match c {
|
||||||
|
'0' => Some(0),
|
||||||
|
'1' => Some(1),
|
||||||
|
'2' => Some(2),
|
||||||
|
'3' => Some(3),
|
||||||
|
'4' => Some(4),
|
||||||
|
'5' => Some(5),
|
||||||
|
'6' => Some(6),
|
||||||
|
'7' => Some(7),
|
||||||
|
'8' => Some(8),
|
||||||
|
'9' => Some(9),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIGITS: [&'static str; 10] = [
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
];
|
||||||
|
|
||||||
|
fn letter_digit_at(line: &str, index: usize) -> Option<u32> {
|
||||||
|
for (dindex, d) in DIGITS.iter().enumerate() {
|
||||||
|
if index + d.len() > line.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let index2 = index + d.len();
|
||||||
|
let substring: &str = line[index..index2].as_ref();
|
||||||
|
if *d == substring {
|
||||||
|
// println!("compare {} dindex {} d {}", index, dindex, d);
|
||||||
|
return Some(dindex as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn digit_for_char(line: &str, index: usize, c: char) -> Option<u32> {
|
||||||
|
if c.is_ascii_digit() {
|
||||||
|
char_to_num(c)
|
||||||
|
} else {
|
||||||
|
letter_digit_at(line, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_letters_for_digits(input: &str) -> Vec<u32> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let mut first_digit = 0;
|
||||||
|
let mut second_digit = 0;
|
||||||
|
for (index, c) in line.chars().enumerate() {
|
||||||
|
if let Some(digit) = digit_for_char(line, index, c) {
|
||||||
|
first_digit = digit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, c) in line.chars().rev().enumerate() {
|
||||||
|
if let Some(digit) = digit_for_char(line, line.len() - index - 1, c) {
|
||||||
|
second_digit = digit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num = first_digit * 10 + second_digit;
|
||||||
|
println!("line {} num {}", line, num);
|
||||||
|
Some(num)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const SAMPLE_DATA: &'static str = r#"
|
||||||
|
1abc2
|
||||||
|
pqr3stu8vwx
|
||||||
|
a1b2c3d4e5f
|
||||||
|
treb7uchet
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data() {
|
||||||
|
let expected = [12, 38, 15, 77];
|
||||||
|
let data = parse(SAMPLE_DATA);
|
||||||
|
assert_eq!(data, expected);
|
||||||
|
let part1 = part1_sum(SAMPLE_DATA);
|
||||||
|
assert_eq!(part1, 142);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SAMPLE_DATA2: &'static str = r#"
|
||||||
|
two1nine
|
||||||
|
eightwothree
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data_part2() {
|
||||||
|
let data = sub_letters_for_digits(SAMPLE_DATA2);
|
||||||
|
assert_eq!(data, [29, 83, 13, 24, 42, 14, 76]);
|
||||||
|
let part2 = part2_sum(SAMPLE_DATA2);
|
||||||
|
assert_eq!(part2, 281);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
|
||||||
|
const MAX_RED: u32 = 12;
|
||||||
|
const MAX_GREEN: u32 = 13;
|
||||||
|
const MAX_BLUE: u32 = 14;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Turn {
|
||||||
|
red: u32,
|
||||||
|
green: u32,
|
||||||
|
blue: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Game {
|
||||||
|
id: u32,
|
||||||
|
turns: Vec<Turn>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
fn part1_possible(&self) -> Option<u32> {
|
||||||
|
for turn in self.turns.iter() {
|
||||||
|
if turn.red > MAX_RED || turn.green > MAX_GREEN || turn.blue > MAX_BLUE {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(self.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2_power(&self) -> u32 {
|
||||||
|
let mut max_red = 0;
|
||||||
|
let mut max_green = 0;
|
||||||
|
let mut max_blue = 0;
|
||||||
|
|
||||||
|
for turn in self.turns.iter() {
|
||||||
|
if turn.red > max_red {
|
||||||
|
max_red = turn.red;
|
||||||
|
}
|
||||||
|
if turn.green > max_green {
|
||||||
|
max_green = turn.green;
|
||||||
|
}
|
||||||
|
if turn.blue > max_blue {
|
||||||
|
max_blue = turn.blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max_red * max_green * max_blue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day2)]
|
||||||
|
fn parse(input: &str) -> Vec<Game> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let gparts: Vec<&str> = line.split(": ").collect();
|
||||||
|
let game_id = gparts[0].split(" ").collect::<Vec<&str>>()[1]
|
||||||
|
.parse::<u32>()
|
||||||
|
.unwrap();
|
||||||
|
let mut turns: Vec<Turn> = vec![];
|
||||||
|
for whole_turn in gparts[1].split("; ") {
|
||||||
|
let mut red = 0;
|
||||||
|
let mut green = 0;
|
||||||
|
let mut blue = 0;
|
||||||
|
for dice_group in whole_turn.split(", ") {
|
||||||
|
let dice_parts: Vec<&str> = dice_group.split(" ").collect();
|
||||||
|
let count = dice_parts[0].parse::<u32>().unwrap();
|
||||||
|
match dice_parts[1] {
|
||||||
|
"red" => red = count,
|
||||||
|
"green" => green = count,
|
||||||
|
"blue" => blue = count,
|
||||||
|
_ => println!("invalid color! {}", dice_parts[0]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
turns.push(Turn { red, green, blue });
|
||||||
|
}
|
||||||
|
Some(Game { id: game_id, turns })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day2, part1)]
|
||||||
|
fn part1_valid_game_sum(input: &[Game]) -> u32 {
|
||||||
|
input.iter().filter_map(|game| game.part1_possible()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day2, part2)]
|
||||||
|
fn part2_power_sum(input: &[Game]) -> u32 {
|
||||||
|
input.iter().map(|game| game.part2_power()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
// use super::*;
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
type PartList = BTreeMap<(usize, usize, char), Vec<u32>>;
|
||||||
|
|
||||||
|
fn parse(input: &str) -> Vec<Vec<char>> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
if !line.is_empty() {
|
||||||
|
Some(line.chars().collect::<Vec<char>>())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day3)]
|
||||||
|
fn associate_parts(input: &str) -> PartList {
|
||||||
|
let parsed_input = parse(input);
|
||||||
|
let mut result: PartList = BTreeMap::new();
|
||||||
|
for (line_index, line) in parsed_input.iter().enumerate() {
|
||||||
|
let mut parse_end = 0;
|
||||||
|
for (index, c) in line.iter().enumerate() {
|
||||||
|
if index < parse_end {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if c.is_ascii_digit() {
|
||||||
|
let mut n: Vec<char> = vec![];
|
||||||
|
n.push(*c);
|
||||||
|
if index + 1 < line.len() && line[index + 1].is_ascii_digit() {
|
||||||
|
n.push(line[index + 1]);
|
||||||
|
if index + 2 < line.len() && line[index + 2].is_ascii_digit() {
|
||||||
|
n.push(line[index + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let part_num = n
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
.parse::<u32>()
|
||||||
|
.unwrap();
|
||||||
|
let mut adjacent: Vec<(usize, usize)> = vec![];
|
||||||
|
let mut left_side = index;
|
||||||
|
if index > 1 {
|
||||||
|
left_side -= 1;
|
||||||
|
}
|
||||||
|
let mut right_side = index + n.len() - 1;
|
||||||
|
if right_side < line.len() - 1 {
|
||||||
|
right_side += 1;
|
||||||
|
}
|
||||||
|
parse_end = right_side;
|
||||||
|
let mut top_side = line_index;
|
||||||
|
if top_side > 1 {
|
||||||
|
top_side -= 1;
|
||||||
|
}
|
||||||
|
let mut bottom_side = line_index;
|
||||||
|
if bottom_side + 1 <= parsed_input.len() - 1 {
|
||||||
|
bottom_side += 1;
|
||||||
|
}
|
||||||
|
adjacent.push((line_index, left_side));
|
||||||
|
adjacent.push((line_index, right_side));
|
||||||
|
for col in left_side..=right_side {
|
||||||
|
adjacent.push((top_side, col));
|
||||||
|
adjacent.push((bottom_side, col));
|
||||||
|
}
|
||||||
|
if let Some(part_coord) = adjacent.iter().find(|coord| {
|
||||||
|
!parsed_input[coord.0][coord.1].is_ascii_digit()
|
||||||
|
&& parsed_input[coord.0][coord.1] != '.'
|
||||||
|
}) {
|
||||||
|
let hash_key = (
|
||||||
|
part_coord.0,
|
||||||
|
part_coord.1,
|
||||||
|
parsed_input[part_coord.0][part_coord.1],
|
||||||
|
);
|
||||||
|
if !result.contains_key(&hash_key) {
|
||||||
|
result.insert(hash_key, vec![]);
|
||||||
|
}
|
||||||
|
let plist = result.get_mut(&hash_key).unwrap();
|
||||||
|
plist.push(part_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("{:?}", result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day3, part1)]
|
||||||
|
fn part1(input: &PartList) -> u32 {
|
||||||
|
let mut sum: u32 = 0;
|
||||||
|
for (_coord, plist) in input.iter() {
|
||||||
|
sum += plist.iter().sum::<u32>();
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day3, part2)]
|
||||||
|
fn part2(input: &PartList) -> u32 {
|
||||||
|
let mut sum: u32 = 0;
|
||||||
|
for (coord, plist) in input.iter() {
|
||||||
|
if coord.2 == '*' && plist.len() == 2 {
|
||||||
|
sum += plist.iter().product::<u32>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const SAMPLE_DATA: &'static str = r#"467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data() {
|
||||||
|
let parts = associate_parts(&SAMPLE_DATA);
|
||||||
|
assert_eq!(part1(&parts), 4361);
|
||||||
|
assert_eq!(part2(&parts), 467835);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Card {
|
||||||
|
id: usize,
|
||||||
|
winning: HashSet<u32>,
|
||||||
|
present: HashSet<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Card {
|
||||||
|
fn matching_numbers(&self) -> Vec<&u32> {
|
||||||
|
self.winning
|
||||||
|
.intersection(&self.present)
|
||||||
|
.collect::<Vec<&u32>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day4)]
|
||||||
|
fn parse(input: &str) -> Vec<Card> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let cparts: Vec<&str> = line.split(": ").collect();
|
||||||
|
let id = cparts[0]
|
||||||
|
.split(" ")
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.last()?
|
||||||
|
.trim_start()
|
||||||
|
.parse::<usize>()
|
||||||
|
.unwrap();
|
||||||
|
let cnums: Vec<&str> = cparts[1].split(" | ").collect();
|
||||||
|
let winning: HashSet<u32> = cnums[0]
|
||||||
|
.replace(" ", " ")
|
||||||
|
.trim_start()
|
||||||
|
.split(" ")
|
||||||
|
.map(|n| n.parse::<u32>().unwrap())
|
||||||
|
.collect();
|
||||||
|
let present: HashSet<u32> = cnums[1]
|
||||||
|
.replace(" ", " ")
|
||||||
|
.trim_start()
|
||||||
|
.split(" ")
|
||||||
|
.map(|n| n.parse::<u32>().unwrap())
|
||||||
|
.collect();
|
||||||
|
Some(Card {
|
||||||
|
id,
|
||||||
|
winning,
|
||||||
|
present,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day4, part1)]
|
||||||
|
fn part1(input: &[Card]) -> i32 {
|
||||||
|
let mut sum = 0;
|
||||||
|
let base: i32 = 2;
|
||||||
|
for c in input.iter() {
|
||||||
|
let m = c.matching_numbers();
|
||||||
|
if m.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let p = base.pow(m.len() as u32 - 1);
|
||||||
|
if m.len() > 0 {
|
||||||
|
sum += p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day4, part2)]
|
||||||
|
fn part2(input: &[Card]) -> u32 {
|
||||||
|
let mut cards: [u32; 202] = [1; 202];
|
||||||
|
for i in input.len()..cards.len() {
|
||||||
|
cards[i] = 0;
|
||||||
|
}
|
||||||
|
for c in input.iter() {
|
||||||
|
let m = c.matching_numbers();
|
||||||
|
let cur = c.id - 1;
|
||||||
|
for i in 1..=m.len() {
|
||||||
|
let dest = cur + i;
|
||||||
|
if dest >= input.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cards[dest] += cards[cur];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cards.iter().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const SAMPLE_DATA: &'static 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]
|
||||||
|
fn sample_data() {
|
||||||
|
let cards = parse(&SAMPLE_DATA);
|
||||||
|
assert_eq!(part1(&cards), 13);
|
||||||
|
assert_eq!(part2(&cards), 30);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
use std::str::Lines;
|
||||||
|
|
||||||
|
struct RangeMap {
|
||||||
|
source: RangeInclusive<u64>,
|
||||||
|
destination: RangeInclusive<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Almanac {
|
||||||
|
seeds: Vec<u64>,
|
||||||
|
seed_to_soil: Vec<RangeMap>,
|
||||||
|
soil_to_fertilizer: Vec<RangeMap>,
|
||||||
|
fertilizer_to_water: Vec<RangeMap>,
|
||||||
|
water_to_light: Vec<RangeMap>,
|
||||||
|
light_to_temperature: Vec<RangeMap>,
|
||||||
|
temperature_to_humidity: Vec<RangeMap>,
|
||||||
|
humidity_to_location: Vec<RangeMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_to_triple(input: &str) -> [u64; 3] {
|
||||||
|
let mut vals: [u64; 3] = [0; 3];
|
||||||
|
for (index, i) in input.split(" ").enumerate() {
|
||||||
|
vals[index] = i.parse::<u64>().unwrap();
|
||||||
|
}
|
||||||
|
vals
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_rangemap(lines: &mut Lines) -> Vec<RangeMap> {
|
||||||
|
let mut rm: Vec<RangeMap> = vec![];
|
||||||
|
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let triple = line_to_triple(&line);
|
||||||
|
rm.push(RangeMap {
|
||||||
|
source: triple[1]..=(triple[1] + triple[2]),
|
||||||
|
destination: triple[0]..=(triple[0] + triple[2]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rm
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day5)]
|
||||||
|
fn parse(input: &str) -> Almanac {
|
||||||
|
let mut lines = input.lines();
|
||||||
|
let seeds = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
|
||||||
|
.split(" ")
|
||||||
|
.map(|s| s.parse::<u64>().unwrap())
|
||||||
|
.collect();
|
||||||
|
lines.next();
|
||||||
|
lines.next();
|
||||||
|
|
||||||
|
let seed_to_soil = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let soil_to_fertilizer = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let fertilizer_to_water = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let water_to_light = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let light_to_temperature = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let temperature_to_humidity = next_rangemap(&mut lines);
|
||||||
|
lines.next();
|
||||||
|
let humidity_to_location = next_rangemap(&mut lines);
|
||||||
|
|
||||||
|
Almanac {
|
||||||
|
seeds,
|
||||||
|
seed_to_soil,
|
||||||
|
soil_to_fertilizer,
|
||||||
|
fertilizer_to_water,
|
||||||
|
water_to_light,
|
||||||
|
light_to_temperature,
|
||||||
|
temperature_to_humidity,
|
||||||
|
humidity_to_location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_to(s: u64, maps: &Vec<RangeMap>) -> u64 {
|
||||||
|
if let Some(rm) = maps.iter().find(|m| m.source.contains(&s)) {
|
||||||
|
let offset = s - rm.source.start();
|
||||||
|
rm.destination.start() + offset
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min_location(seeds: &Vec<u64>, input: &Almanac) -> u64 {
|
||||||
|
let locations = seeds
|
||||||
|
.iter()
|
||||||
|
.map(|s| match_to(*s, &input.seed_to_soil))
|
||||||
|
.map(|s| match_to(s, &input.soil_to_fertilizer))
|
||||||
|
.map(|s| match_to(s, &input.fertilizer_to_water))
|
||||||
|
.map(|s| match_to(s, &input.water_to_light))
|
||||||
|
.map(|s| match_to(s, &input.light_to_temperature))
|
||||||
|
.map(|s| match_to(s, &input.temperature_to_humidity))
|
||||||
|
.map(|s| match_to(s, &input.humidity_to_location))
|
||||||
|
.collect::<Vec<u64>>();
|
||||||
|
*locations.iter().min().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day5, part1)]
|
||||||
|
fn part1(input: &Almanac) -> u64 {
|
||||||
|
min_location(&input.seeds, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day5, part2, bf)]
|
||||||
|
fn part2(input: &Almanac) -> u64 {
|
||||||
|
let mut seeds: Vec<u64> = vec![];
|
||||||
|
let mut index = 0;
|
||||||
|
while index < input.seeds.len() {
|
||||||
|
let start = input.seeds[index];
|
||||||
|
let count = input.seeds[index + 1];
|
||||||
|
index += 2;
|
||||||
|
for n in start..(start + count) {
|
||||||
|
seeds.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min_location(&seeds, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const SAMPLE_DATA: &'static 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 sample_data() {
|
||||||
|
let maps = parse(&SAMPLE_DATA);
|
||||||
|
assert_eq!(part1(&maps), 35);
|
||||||
|
assert_eq!(part2(&maps), 46);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use aoc_runner_derive::aoc_lib;
|
||||||
|
|
||||||
|
mod day01;
|
||||||
|
mod day02;
|
||||||
|
mod day03;
|
||||||
|
mod day04;
|
||||||
|
mod day05;
|
||||||
|
|
||||||
|
aoc_lib! { year = 2023 }
|
|
@ -0,0 +1,7 @@
|
||||||
|
extern crate aoc2023;
|
||||||
|
extern crate aoc_runner;
|
||||||
|
extern crate aoc_runner_derive;
|
||||||
|
|
||||||
|
use aoc_runner_derive::aoc_main;
|
||||||
|
|
||||||
|
aoc_main! { lib = aoc2023; }
|
Loading…
Reference in New Issue