making 2023 a generic aoc repo

This commit is contained in:
Dylan Thies
2023-12-04 19:40:31 -05:00
parent 09f1da1295
commit 51cf3fa8e7
28 changed files with 2 additions and 2 deletions

50
2023/.gitignore vendored Normal file
View File

@@ -0,0 +1,50 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
#Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
.vscode/settings.json
input.txt

458
2023/Cargo.lock generated Normal file
View File

@@ -0,0 +1,458 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[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]]
name = "day-1"
version = "2023.0.0"
dependencies = [
"derive-getters",
"error-stack",
"itertools",
"log",
"nom",
]
[[package]]
name = "day-2"
version = "2023.0.0"
dependencies = [
"itertools",
"log",
"nom",
]
[[package]]
name = "day-3"
version = "2023.0.0"
dependencies = [
"itertools",
"log",
]
[[package]]
name = "day-4"
version = "2023.0.0"
dependencies = [
"itertools",
"nom",
"rstest",
"rstest_reuse",
]
[[package]]
name = "day-5"
version = "2023.0.0"
dependencies = [
"itertools",
"nom",
]
[[package]]
name = "derive-getters"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "error-stack"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27a72baa257b5e0e2de241967bc5ee8f855d6072351042688621081d66b2a76b"
dependencies = [
"anyhow",
"rustc_version",
]
[[package]]
name = "futures"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-macro"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "futures-sink"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]]
name = "futures-task"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]]
name = "futures-util"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[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 = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "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]]
name = "relative-path"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca"
[[package]]
name = "rstest"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
dependencies = [
"futures",
"futures-timer",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
dependencies = [
"cfg-if",
"glob",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn 2.0.39",
"unicode-ident",
]
[[package]]
name = "rstest_reuse"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4"
dependencies = [
"quote",
"rand",
"rustc_version",
"syn 2.0.39",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[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"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

18
2023/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[workspace]
resolver = "2"
members = [ "day-*" ]
[workspace.package]
version = "2023.0.0"
edition = "2021"
authors = [ "Dylan Thies" ]
repository = "https://github.com/smellyfis/AOC-2023.git"
[workspace.dependencies]
derive-getters = "0.3.0"
error-stack = "0.4.1"
itertools = "0.12.0"
log = "0.4.20"
nom = "7.1.3"
rstest = "0.18.2"
rstest_reuse = "0.6.0"

16
2023/day-1/Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[package]
name = "day-1"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
derive-getters.workspace = true
error-stack.workspace = true
itertools.workspace = true
log.workspace = true
nom.workspace = true

2
2023/day-1/src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod part1;
pub mod part2;

12
2023/day-1/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_1::part1::part1;
use day_1::part2::part2;
fn main() {
let input = include_str!("./input.txt");
let (_, part1_result) = part1(input).unwrap();
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

59
2023/day-1/src/part1.rs Normal file
View File

@@ -0,0 +1,59 @@
#![warn(clippy::all, clippy::pedantic)]
use nom::{
self,
character::complete::{alphanumeric1, newline},
multi::separated_list1,
};
/// Day-1 part 1 of AC2023
///
/// # Arguments
/// - input the input for day1 as a string
///
/// # Panics
/// This panics whenever a number isn't present in a line of the input
///
/// # Errors
/// errors when can't parse the input
pub fn part1(input: &str) -> nom::IResult<&str, String> {
let (_, values) = parse_input(input)?;
println!("{values:?}");
Ok((
"",
values
.iter()
.map(|v| v.first().expect("always at least one number") * 10 + v.last().expect("always atleast one number"))
.sum::<u32>()
.to_string(),
))
}
fn parse_input(input: &str) -> nom::IResult<&str, Vec<Vec<u32>>> {
let (i, j) = separated_list1(newline, alphanumeric1)(input)?;
let res = j
.iter()
.map(|v| {
v.chars()
.filter_map(|x| x.to_digit(10))
.collect::<Vec<u32>>()
})
.collect::<Vec<Vec<u32>>>();
Ok((i, res))
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet";
#[test]
fn part1_works() {
let (_, result) = part1(INPUT).unwrap();
assert_eq!(result, "142".to_string());
}
}

70
2023/day-1/src/part2.rs Normal file
View File

@@ -0,0 +1,70 @@
#![warn(clippy::all, clippy::pedantic)]
/// Day 1 Part 2 of AOC2023
///
/// # Arguments
/// - puzzle input
///
/// # Panics
/// this panics if there is no numbers in a line
pub fn part2(input: &str) -> String {
let values = input.lines().map(parse_line).collect::<Vec<Vec<u32>>>();
println!("{values:?}");
values
.iter()
.map(|v| v.first().expect("There is always at least one number") * 10 + v.last().expect("there is always at least one number"))
.sum::<u32>()
.to_string()
}
fn parse_line(line: &str) -> Vec<u32> {
(0..line.len())
.filter_map(|index| {
let reduced_line = &line[index..];
let result = if reduced_line.starts_with("one") {
Some(1)
} else if reduced_line.starts_with("two") {
Some(2)
} else if reduced_line.starts_with("three") {
Some(3)
} else if reduced_line.starts_with("four") {
Some(4)
} else if reduced_line.starts_with("five") {
Some(5)
} else if reduced_line.starts_with("six") {
Some(6)
} else if reduced_line.starts_with("seven") {
Some(7)
} else if reduced_line.starts_with("eight") {
Some(8)
} else if reduced_line.starts_with("nine") {
Some(9)
} else if reduced_line.starts_with("zero") {
Some(0)
} else {
reduced_line.chars().next().expect("there is alwayss a character").to_digit(10)
};
result
})
.collect()
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen";
#[test]
fn part2_works() {
let result = part2(INPUT);
assert_eq!(result, "281".to_string());
}
}

13
2023/day-2/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "day-2"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
itertools.workspace = true
log.workspace = true

4
2023/day-2/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::part1;
pub mod part2;
pub use crate::part2::part2;

12
2023/day-2/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_2::part1;
use day_2::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

115
2023/day-2/src/part1.rs Normal file
View File

@@ -0,0 +1,115 @@
#![warn(clippy::all, clippy::pedantic)]
use log::debug;
use nom::{
bytes::complete::tag,
character::complete::{self, newline},
multi::separated_list1,
sequence::{preceded, separated_pair},
};
#[derive(Debug)]
struct Round {
pub red_n: u32,
pub green_n: u32,
pub blue_n: u32,
}
#[derive(Debug)]
struct Game {
pub id: u32,
pub rounds: Vec<Round>,
}
impl Game {
fn to_part1(&self) -> Option<u32> {
if self
.rounds
.iter()
.find_map(|r| {
//TODO if inverted use find_map
if r.red_n > 12 || r.green_n > 13 || r.blue_n > 14 {
Some(self.id)
} else {
None
}
})
.is_some()
{
None
} else {
Some(self.id)
}
}
}
/// part2 of day 2 of AOC 2023
///
/// # Arguments
/// - input the puszzle input
///
/// # Panics
/// panics whenever the input isn't parsable
pub fn part1(input: &str) -> String {
let (_, games) = process_input(input).expect("there should be input");
debug!("{games:?}");
games
.iter()
.filter_map(Game::to_part1)
.sum::<u32>()
.to_string()
}
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
let (i, (cnt, color)) =
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
Ok((i, (cnt, color.to_owned())))
}
fn process_round(input: &str) -> nom::IResult<&str, Round> {
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
let mut round = Round {
red_n: 0,
green_n: 0,
blue_n: 0,
};
for (cnt, color) in blocks {
match color.as_str() {
"red" => round.red_n = cnt,
"green" => round.green_n = cnt,
"blue" => round.blue_n = cnt,
_ => panic!("this should be a color name"),
};
}
Ok((i, round))
}
fn process_game(input: &str) -> nom::IResult<&str, Game> {
let (i, (id, rounds)) = separated_pair(
preceded(tag("Game "), complete::u32),
tag(": "),
separated_list1(tag("; "), process_round),
)(input)?;
Ok((i, Game { id, rounds }))
}
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
separated_list1(newline, process_game)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green";
#[test]
fn part1_works() {
let result = part1(INPUT);
assert_eq!(result, "8".to_string());
}
}

106
2023/day-2/src/part2.rs Normal file
View File

@@ -0,0 +1,106 @@
#![warn(clippy::all, clippy::pedantic)]
use nom::{
bytes::complete::tag,
character::complete::{self, newline},
multi::separated_list1,
sequence::{preceded, separated_pair},
};
#[derive(Debug)]
struct Round {
pub red_n: u32,
pub green_n: u32,
pub blue_n: u32,
}
#[derive(Debug)]
struct Game {
pub _id: u32,
pub rounds: Vec<Round>,
}
impl Game {
fn to_power(&self) -> u64 {
let (r, g, b) = self.rounds.iter().fold((0_u64, 0_u64, 0_u64), |acc, x| {
let (mut val_r, mut val_g, mut val_b) = acc;
if u64::from(x.red_n) > acc.0 {
val_r = x.red_n.into();
}
if u64::from(x.green_n) > acc.1 {
val_g = x.green_n.into();
}
if u64::from(x.blue_n) > acc.2 {
val_b = x.blue_n.into();
}
(val_r, val_g, val_b)
});
r * g * b
}
}
/// part2 of day 2 of AOC 2023
///
/// # Arguments
/// - input the puszzle input
///
/// # Panics
/// panics whenever the input isn't parsable
pub fn part2(input: &str) -> String {
let (_, games) = process_input(input).expect("there should be input");
games.iter().map(Game::to_power).sum::<u64>().to_string()
}
fn process_block(input: &str) -> nom::IResult<&str, (u32, String)> {
let (i, (cnt, color)) =
separated_pair(complete::u32, complete::space1, complete::alpha1)(input)?;
Ok((i, (cnt, color.to_owned())))
}
fn process_round(input: &str) -> nom::IResult<&str, Round> {
let (i, blocks) = separated_list1(tag(", "), process_block)(input)?;
let mut round = Round {
red_n: 0,
green_n: 0,
blue_n: 0,
};
for (cnt, color) in blocks {
match color.as_str() {
"red" => round.red_n = cnt,
"green" => round.green_n = cnt,
"blue" => round.blue_n = cnt,
_ => panic!("this should be a color name"),
};
}
Ok((i, round))
}
fn process_game(input: &str) -> nom::IResult<&str, Game> {
let (i, (id, rounds)) = separated_pair(
preceded(tag("Game "), complete::u32),
tag(": "),
separated_list1(tag("; "), process_round),
)(input)?;
Ok((i, Game { _id: id, rounds }))
}
fn process_input(input: &str) -> nom::IResult<&str, Vec<Game>> {
separated_list1(newline, process_game)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green";
#[test]
fn part2_works() {
let result = part2(INPUT);
assert_eq!(result, "2286".to_string());
}
}

12
2023/day-3/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "day-3"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools.workspace = true
log.workspace = true

4
2023/day-3/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

12
2023/day-3/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_3::part1;
use day_3::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

129
2023/day-3/src/part1.rs Normal file
View File

@@ -0,0 +1,129 @@
#![warn(clippy::all, clippy::pedantic)]
use std::collections::BTreeMap;
#[derive(Debug)]
struct SerialNumber {
pub no: u64,
pub start: (usize, usize),
pub end: (usize, usize),
}
impl SerialNumber {
fn generate_adjacent(&self) -> Vec<(usize, usize)> {
let start_row = if self.start.0 == 0 {
0
} else {
self.start.0 - 1
};
let start_line = if self.start.1 == 0 {
0
} else {
self.start.1 - 1
};
(start_row..=(self.end.0 + 1))
.flat_map(|x| (start_line..=(self.end.1 + 1)).map(move |y| (x, y)))
.collect()
}
}
#[must_use]
pub fn part1(input: &str) -> String {
let (serials, symbols) = parse_input(input);
serials
.iter()
.filter(|x| {
x.generate_adjacent()
.iter()
.any(|t| symbols.get(t).is_some())
})
.map(|x| x.no)
.sum::<u64>()
.to_string()
}
fn parse_input(input: &str) -> (Vec<SerialNumber>, BTreeMap<(usize, usize), char>) {
let mut numbers = Vec::new();
let mut symbols = BTreeMap::new();
for (line_no, line) in input.lines().enumerate() {
let mut prev_char = None;
let mut cur_no = 0_u64;
let mut cur_no_row_start = 0_usize;
for (row_no, c) in line.chars().enumerate() {
if let Some(d) = c.to_digit(10) {
if prev_char.is_some() {
cur_no = cur_no * 10 + u64::from(d);
} else {
cur_no = u64::from(d);
cur_no_row_start = row_no;
}
prev_char = Some(c);
} else {
if prev_char.is_some() {
//handle saving number off
numbers.push(SerialNumber {
no: cur_no,
start: (cur_no_row_start, line_no),
end: (row_no - 1, line_no),
});
}
prev_char = None;
if c == '.' {
//move along space
continue;
}
//store symbol
let _ = symbols.insert((row_no, line_no), c);
}
}
//need to account for new line numbers
if prev_char.is_some() {
numbers.push(SerialNumber {
no: cur_no,
start: (cur_no_row_start, line_no),
end: (line.len() - 1, line_no),
});
}
}
(numbers, symbols)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..";
const INPUT2: &str = "12.......*..
+.........34
.......-12..
..78........
..*....60...
78.........9
.5.....23..$
8...90*12...
............
2.2......12.
.*.........*
1.1..503+.56";
#[test]
fn part1_works() {
let result = part1(INPUT);
assert_eq!(result, "4361".to_string());
}
#[test]
fn part1_works_more() {
let result = part1(INPUT2);
assert_eq!(result, "925".to_string());
}
}

113
2023/day-3/src/part2.rs Normal file
View File

@@ -0,0 +1,113 @@
#![warn(clippy::all, clippy::pedantic)]
use std::collections::BTreeMap;
#[derive(Debug)]
struct SerialNumber {
pub no: u64,
pub start: (usize, usize),
pub end: (usize, usize),
}
impl SerialNumber {
fn is_adjacent(&self, pos: (usize, usize)) -> bool {
usize::abs_diff(self.start.1, pos.1) < 2
&& self.start.0 < 2 + pos.0
&& pos.0 < 2 + self.end.0
}
}
#[must_use]
pub fn part2(input: &str) -> String {
let (serials, symbols) = parse_input(input);
symbols
.iter()
.filter_map(|(key, value)| if *value == '*' { Some(*key) } else { None })
.filter_map(|pos| {
let serials = serials
.iter()
.filter_map(|serial| {
if serial.is_adjacent(pos) {
Some(serial.no)
} else {
None
}
})
.collect::<Vec<u64>>();
if serials.len() == 2 {
Some(serials[0] * serials[1])
} else {
None
}
})
.sum::<u64>()
.to_string()
//find all serials next to '*' and map with '*' location
}
fn parse_input(input: &str) -> (Vec<SerialNumber>, BTreeMap<(usize, usize), char>) {
let mut numbers = Vec::new();
let mut symbols = BTreeMap::new();
for (line_no, line) in input.lines().enumerate() {
let mut prev_char = None;
let mut cur_no = 0_u64;
let mut cur_no_row_start = 0_usize;
for (row_no, c) in line.chars().enumerate() {
if let Some(d) = c.to_digit(10) {
if prev_char.is_some() {
cur_no = cur_no * 10 + u64::from(d);
} else {
cur_no = u64::from(d);
cur_no_row_start = row_no;
}
prev_char = Some(c);
} else {
if prev_char.is_some() {
//handle saving number off
numbers.push(SerialNumber {
no: cur_no,
start: (cur_no_row_start, line_no),
end: (row_no - 1, line_no),
});
}
prev_char = None;
if c == '.' {
//move along space
continue;
}
//store symbol
let _ = symbols.insert((row_no, line_no), c);
}
}
//need to account for new line numbers
if prev_char.is_some() {
numbers.push(SerialNumber {
no: cur_no,
start: (cur_no_row_start, line_no),
end: (line.len() - 1, line_no),
});
}
}
(numbers, symbols)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..";
#[test]
fn part2_works() {
let result = part2(INPUT);
assert_eq!(result, "467835".to_string());
}
}

14
2023/day-4/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "day-4"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
itertools.workspace = true
rstest = {workspace = true}
rstest_reuse = {workspace = true}

4
2023/day-4/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

12
2023/day-4/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_4::part1;
use day_4::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

106
2023/day-4/src/part1.rs Normal file
View File

@@ -0,0 +1,106 @@
#![warn(clippy::all, clippy::pedantic)]
use nom::{
bytes::complete::tag,
character::complete,
multi::{fold_many1, separated_list1},
sequence::{preceded, separated_pair, tuple},
IResult,
};
use std::collections::HashSet;
struct Card {
pub _id: u8,
pub game_numbers: HashSet<u8>,
pub my_numbers: HashSet<u8>,
}
impl Card {
fn get_win_count(&self) -> usize {
self.my_numbers.intersection(&self.game_numbers).count()
}
fn get_score(&self) -> Option<usize> {
let count = self.get_win_count();
if count == 0 {
None
} else {
Some(2_usize.pow(u32::try_from(count).expect("shouldn't have a lot of cards") - 1))
}
}
}
/// day 4 part 1 of aoc 2023
///
/// # Arguments
/// - input the input for today's puzzle
///
/// # Panics
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
/// usize
#[must_use]
pub fn part1(input: &str) -> String {
let (_, cards) = parse_input(input).expect("there should be input");
cards
.iter()
.filter_map(Card::get_score)
.sum::<usize>()
.to_string()
}
fn parse_num_list(input: &str) -> IResult<&str, HashSet<u8>> {
fold_many1(
tuple((complete::u8, complete::space0)),
HashSet::new,
|mut acc, (x, _)| {
acc.insert(x);
acc
},
)(input)
}
fn parse_numbers(input: &str) -> IResult<&str, (HashSet<u8>, HashSet<u8>)> {
separated_pair(
parse_num_list,
tuple((tag("|"), complete::space1)),
parse_num_list,
)(input)
}
fn parse_card(input: &str) -> IResult<&str, Card> {
let (input, (id, (my_numbers, game_numbers))) = separated_pair(
preceded(tuple((tag("Card"), complete::space1)), complete::u8),
tuple((tag(":"), complete::space1)),
parse_numbers,
)(input)?;
Ok((
input,
Card {
_id: id,
game_numbers,
my_numbers,
},
))
}
fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
separated_list1(complete::line_ending, parse_card)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "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 part1_works() {
let result = part1(INPUT);
assert_eq!(result, "13".to_string());
}
}

121
2023/day-4/src/part2.rs Normal file
View File

@@ -0,0 +1,121 @@
#![warn(clippy::all, clippy::pedantic)]
use nom::{
bytes::complete::tag,
character::complete,
combinator::map,
multi::{fold_many1, separated_list1},
sequence::{preceded, separated_pair, tuple},
IResult,
};
use std::collections::{BTreeMap, HashSet};
struct Card {
pub id: usize,
pub game_numbers: HashSet<u8>,
pub my_numbers: HashSet<u8>,
}
impl Card {
fn get_win_count(&self) -> usize {
self.my_numbers.intersection(&self.game_numbers).count()
}
}
/// day 4 part 1 of aoc 2023
///
/// # Arguments
/// - input the input for today's puzzle
///
/// # Panics
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
/// usize
#[must_use]
pub fn part2(input: &str) -> String {
let (_, cards) = parse_input(input).expect("there should be input");
let mut cards_had = BTreeMap::new();
for card in cards {
if let Some(x) = cards_had.get_mut(&card.id) {
*x += 1;
} else {
cards_had.insert(card.id, 1);
}
let next_id = card.id + 1;
let last_winner = card.id + card.get_win_count();
//println!("{} - {next_id} {last_winner}", card.id);
if last_winner < next_id {
continue;
}
let card_count = *cards_had.get(&card.id).expect("already should have cards");
for id in next_id..=last_winner {
if let Some(x) = cards_had.get_mut(&id) {
*x += card_count;
} else {
cards_had.insert(id, card_count);
}
}
}
//println!("{cards_had:#?}");
cards_had.values().sum::<usize>().to_string()
}
fn parse_num_list(input: &str) -> IResult<&str, HashSet<u8>> {
fold_many1(
tuple((complete::u8, complete::space0)),
HashSet::new,
|mut acc, (x, _)| {
acc.insert(x);
acc
},
)(input)
}
fn parse_numbers(input: &str) -> IResult<&str, (HashSet<u8>, HashSet<u8>)> {
separated_pair(
parse_num_list,
tuple((tag("|"), complete::space1)),
parse_num_list,
)(input)
}
fn parse_card(input: &str) -> IResult<&str, Card> {
let (input, (id, (my_numbers, game_numbers))) = separated_pair(
preceded(
tuple((tag("Card"), complete::space1)),
map(complete::u8, usize::from),
),
tuple((tag(":"), complete::space1)),
parse_numbers,
)(input)?;
Ok((
input,
Card {
id,
game_numbers,
my_numbers,
},
))
}
fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
separated_list1(complete::line_ending, parse_card)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "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 part2_works() {
let result = part2(INPUT);
assert_eq!(result, "30".to_string());
}
}

12
2023/day-5/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "day-5"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
itertools.workspace = true

4
2023/day-5/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

12
2023/day-5/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_5::part1;
use day_5::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

18
2023/day-5/src/part1.rs Normal file
View File

@@ -0,0 +1,18 @@
#![warn(clippy::all, clippy::pedantic)]
#[must_use] pub fn part1(_input: &str) -> String {
"Not Finished".to_string()
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "";
#[test]
fn part1_works() {
let result = part1(INPUT);
assert_eq!(result, "Not Finished".to_string());
}
}

18
2023/day-5/src/part2.rs Normal file
View File

@@ -0,0 +1,18 @@
#![warn(clippy::all, clippy::pedantic)]
#[must_use] pub fn part2(_input: &str) -> String {
"Not Finished".to_string()
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "";
#[test]
fn part2_works() {
let result = part2(INPUT);
assert_eq!(result, "Not Finished".to_string());
}
}