I love overcomplicating an advent calendar

This commit is contained in:
Tyler Beckman 2023-12-02 12:01:30 -07:00
parent 19af7422b2
commit a852107c7f
Signed by: Ty
GPG key ID: 2813440C772555A4
4 changed files with 222 additions and 147 deletions

View file

@ -1,66 +1,75 @@
pub fn part1(input: String) {
let mut values = Vec::<usize>::new();
use crate::utils::Day;
for line in input.lines() {
let digits = line.chars().filter(|char| char.is_numeric()).collect::<Vec<_>>();
let calibrartion_value = format!("{}{}", digits.first().unwrap(), digits.last().unwrap());
values.push(calibrartion_value.parse().unwrap());
}
println!("{}", values.into_iter().reduce(|acc, e| acc + e).unwrap());
#[derive(Debug, Default)]
pub struct Day1 {
pub input: String
}
pub fn part2(input: String) {
let mut values = Vec::<usize>::new();
for line in input.lines() {
let line = line.to_owned();
// Find instances of number words and choose the closest, recursively replacing (Can't just .replace b/c of shit like "twone")
let digits = vec![
line.match_indices("one").collect::<Vec<_>>(),
line.match_indices("two").collect::<Vec<_>>(),
line.match_indices("three").collect::<Vec<_>>(),
line.match_indices("four").collect::<Vec<_>>(),
line.match_indices("five").collect::<Vec<_>>(),
line.match_indices("six").collect::<Vec<_>>(),
line.match_indices("seven").collect::<Vec<_>>(),
line.match_indices("eight").collect::<Vec<_>>(),
line.match_indices("nine").collect::<Vec<_>>(),
line.match_indices("1").collect::<Vec<_>>(),
line.match_indices("2").collect::<Vec<_>>(),
line.match_indices("3").collect::<Vec<_>>(),
line.match_indices("4").collect::<Vec<_>>(),
line.match_indices("5").collect::<Vec<_>>(),
line.match_indices("6").collect::<Vec<_>>(),
line.match_indices("7").collect::<Vec<_>>(),
line.match_indices("8").collect::<Vec<_>>(),
line.match_indices("9").collect::<Vec<_>>(),
].into_iter();
let mut flat = Vec::<(usize, String)>::new();
for digit in digits {
for (index, text) in digit {
let text = text
.to_owned()
.replace("one", "1")
.replace("two", "2")
.replace("three", "3")
.replace("four", "4")
.replace("five", "5")
.replace("six", "6")
.replace("seven", "7")
.replace("eight", "8")
.replace("nine", "9");
flat.push((index, text));
}
impl Day for Day1 {
fn part1(&mut self) -> String {
let mut values = Vec::<usize>::new();
for line in self.input.lines() {
let digits = line.chars().filter(|char| char.is_numeric()).collect::<Vec<_>>();
let calibrartion_value = format!("{}{}", digits.first().unwrap(), digits.last().unwrap());
values.push(calibrartion_value.parse().unwrap());
}
flat.sort_by(|a, b| a.0.cmp(&b.0));
let calibration_value = format!("{}{}", flat.first().unwrap().1, flat.last().unwrap().1);
values.push(calibration_value.parse().unwrap());
return values.into_iter().reduce(|acc, e| acc + e).unwrap().to_string();
}
fn part2(&mut self) -> String {
let mut values = Vec::<usize>::new();
for line in self.input.lines() {
let line = line.to_owned();
// Find instances of number words and choose the closest, recursively replacing (Can't just .replace b/c of shit like "twone")
let digits = vec![
line.match_indices("one").collect::<Vec<_>>(),
line.match_indices("two").collect::<Vec<_>>(),
line.match_indices("three").collect::<Vec<_>>(),
line.match_indices("four").collect::<Vec<_>>(),
line.match_indices("five").collect::<Vec<_>>(),
line.match_indices("six").collect::<Vec<_>>(),
line.match_indices("seven").collect::<Vec<_>>(),
line.match_indices("eight").collect::<Vec<_>>(),
line.match_indices("nine").collect::<Vec<_>>(),
line.match_indices("1").collect::<Vec<_>>(),
line.match_indices("2").collect::<Vec<_>>(),
line.match_indices("3").collect::<Vec<_>>(),
line.match_indices("4").collect::<Vec<_>>(),
line.match_indices("5").collect::<Vec<_>>(),
line.match_indices("6").collect::<Vec<_>>(),
line.match_indices("7").collect::<Vec<_>>(),
line.match_indices("8").collect::<Vec<_>>(),
line.match_indices("9").collect::<Vec<_>>(),
].into_iter();
let mut flat = Vec::<(usize, String)>::new();
for digit in digits {
for (index, text) in digit {
let text = text
.to_owned()
.replace("one", "1")
.replace("two", "2")
.replace("three", "3")
.replace("four", "4")
.replace("five", "5")
.replace("six", "6")
.replace("seven", "7")
.replace("eight", "8")
.replace("nine", "9");
flat.push((index, text));
}
}
flat.sort_by(|a, b| a.0.cmp(&b.0));
let calibration_value = format!("{}{}", flat.first().unwrap().1, flat.last().unwrap().1);
values.push(calibration_value.parse().unwrap());
}
return values.into_iter().reduce(|acc, e| acc + e).unwrap().to_string();
}
println!("{}", values.into_iter().reduce(|acc, e| acc + e).unwrap());
}

View file

@ -1,7 +1,15 @@
use regex::Regex;
use crate::utils::Day;
#[derive(Debug, Default)]
pub struct Day2 {
pub input: String,
pub games: Vec<Game>
}
#[derive(Debug)]
struct Game {
pub struct Game {
id: usize,
picks: Vec<CubeCombo>
}
@ -13,88 +21,87 @@ struct CubeCombo {
green: usize
}
fn parse_games(input: String) -> Vec<Game> {
let game_re = Regex::new(r"Game (\d+): (.+)").unwrap();
let pick_re = Regex::new(r"(?<first>\d+ (?:red|green|blue))(?:, (?<second>\d+ (?:red|green|blue)))?(?:, (?<third>\d+ (?:red|green|blue)))?").unwrap();
let mut games = Vec::<Game>::new();
impl Day for Day2 {
fn parse(&mut self) {
self.games.clear();
let game_re = Regex::new(r"Game (\d+): (.+)").unwrap();
let pick_re = Regex::new(r"(?<first>\d+ (?:red|green|blue))(?:, (?<second>\d+ (?:red|green|blue)))?(?:, (?<third>\d+ (?:red|green|blue)))?").unwrap();
for line in input.lines() {
let line = line.to_string();
let captures = game_re.captures(&line).unwrap();
let mut game = Game {
id: captures.get(1).unwrap().as_str().parse().unwrap(),
picks: vec![]
};
let picks = captures.get(2).unwrap().as_str().split("; ");
for pick in picks {
let captures = pick_re.captures(pick).unwrap();
let captures = vec![
captures.name("first"),
captures.name("second"),
captures.name("third"),
];
let mut pick = CubeCombo {
red: 0,
blue: 0,
green: 0
for line in self.input.lines() {
let line = line.to_string();
let captures = game_re.captures(&line).unwrap();
let mut game = Game {
id: captures.get(1).unwrap().as_str().parse().unwrap(),
picks: vec![]
};
for capture in captures.iter().filter(|e| e.is_some()).map(|e| e.unwrap()) {
let split = capture.as_str().split(" ").collect::<Vec<_>>();
match split[1] {
"red" => pick.red = split[0].parse().unwrap(),
"blue" => pick.blue = split[0].parse().unwrap(),
"green" => pick.green = split[0].parse().unwrap(),
_ => panic!("impossible wtf")
let picks = captures.get(2).unwrap().as_str().split("; ");
for pick in picks {
let captures = pick_re.captures(pick).unwrap();
let captures = vec![
captures.name("first"),
captures.name("second"),
captures.name("third"),
];
let mut pick = CubeCombo {
red: 0,
blue: 0,
green: 0
};
for capture in captures.iter().filter(|e| e.is_some()).map(|e| e.unwrap()) {
let split = capture.as_str().split(" ").collect::<Vec<_>>();
match split[1] {
"red" => pick.red = split[0].parse().unwrap(),
"blue" => pick.blue = split[0].parse().unwrap(),
"green" => pick.green = split[0].parse().unwrap(),
_ => panic!("impossible wtf")
}
}
game.picks.push(pick);
}
game.picks.push(pick);
self.games.push(game);
}
games.push(game);
}
games
}
pub fn part1(input: String) {
let games = parse_games(input);
// Sum the illegal game IDs
let sum = games
.iter()
.filter(|g|
g.picks.iter().all(|pick| pick.red <= 12 && pick.blue <= 14 && pick.green <= 13 )
)
.fold(0, |acc, e| acc + e.id);
println!("{sum}");
}
pub fn part2(input: String) {
let games = parse_games(input);
fn part1(&mut self) -> String {
// Sum the illegal game IDs
let sum = self.games
.iter()
.filter(|g|
g.picks.iter().all(|pick| pick.red <= 12 && pick.blue <= 14 && pick.green <= 13 )
)
.fold(0, |acc, e| acc + e.id);
sum.to_string()
}
// Calculate the least amount of dice possible for all games
let sum = games.iter().map(|game| {
let mut combo = CubeCombo {
red: 0,
green: 0,
blue: 0
};
for pick in &game.picks {
if combo.red < pick.red {
combo.red = pick.red;
fn part2(&mut self) -> String {
// Calculate the least amount of dice possible for all games
let sum = self.games.iter().map(|game| {
let mut combo = CubeCombo {
red: 0,
green: 0,
blue: 0
};
for pick in &game.picks {
if combo.red < pick.red {
combo.red = pick.red;
}
if combo.green < pick.green {
combo.green = pick.green;
}
if combo.blue < pick.blue {
combo.blue = pick.blue;
}
}
if combo.green < pick.green {
combo.green = pick.green;
}
if combo.blue < pick.blue {
combo.blue = pick.blue;
}
}
combo
}).fold(0, |acc, combo| acc + (combo.red * combo.blue * combo.green));
println!("{sum}");
combo
}).fold(0, |acc, combo| acc + (combo.red * combo.blue * combo.green));
sum.to_string()
}
}

View file

@ -1,5 +1,9 @@
use std::{io::Write, time::{Duration, Instant}};
use clap::{Parser, Subcommand};
use crate::{day1::Day1, day2::Day2, utils::Day};
pub mod utils;
pub mod day1;
pub mod day2;
@ -24,6 +28,17 @@ enum Subcommands {
/// The part to solve for
part: u8
},
/// Runs solution N amount of times, averaging speed
Benchmark {
/// The day to solve for
day: u8,
/// The part to solve for
part: u8,
/// The amount of times to run before actually keeping track
warmup: u32,
/// The amount of times to run
n: u32
},
/// Fetches and prints the given input for a specified day
Input {
/// The day to fetch
@ -50,13 +65,52 @@ async fn main() {
.trim_end()
.to_owned();
match (day, part) {
(1, 1) => day1::part1(response),
(1, 2) => day1::part2(response),
(2, 1) => day2::part1(response),
(2, 2) => day2::part2(response),
let mut day: Box<dyn Day> = match day {
1 => Box::new(Day1 { input: response }),
2 => Box::new(Day2 { input: response, games: vec![] }),
_ => panic!("Invalid day or part #")
};
let start = std::time::Instant::now();
let result = day.solve(part);
let end = std::time::Instant::now();
println!("Solution: {result}");
println!("Solved in {}ns", (end - start).as_nanos() as f64 / 1000.0);
},
Subcommands::Benchmark { day, part, warmup, n } => {
let response = client
.get(format!("https://adventofcode.com/2023/day/{day}/input"))
.header("Cookie", format!("session={auth}", auth = cli.auth))
.send()
.await
.unwrap()
.text()
.await
.unwrap()
.trim_end()
.to_owned();
let mut day: Box<dyn Day> = match day {
1 => Box::new(Day1 { input: response }),
2 => Box::new(Day2 { input: response, games: vec![] }),
_ => panic!("Invalid day #")
};
let mut timings = Vec::<Duration>::new();
for _ in 1..warmup {
day.solve(part);
}
for i in 1..=n {
let start = Instant::now();
day.solve(part);
let end = Instant::now();
timings.push(end - start);
if i % (n / 100) == 0 {
print!("\r{}%", ((i as f32 / n as f32) * 100.0).round());
let _ = std::io::stdout().flush();
}
}
let avg = timings.into_iter().sum::<Duration>() / n;
println!("\nAverage timing: {}µs", avg.as_nanos() as f64 / 1000.0);
},
Subcommands::Input { day } => {
let response = client

View file

@ -1,8 +1,13 @@
pub enum CoroutineYield {
Data,
Output(String)
}
pub enum CoroutineYieldResponse {
Data(String),
pub trait Day: std::fmt::Debug {
fn part1(&mut self) -> String;
fn part2(&mut self) -> String;
fn parse(&mut self) { }
fn solve(&mut self, part: u8) -> String {
self.parse();
match part {
1 => self.part1(),
2 => self.part2(),
_ => panic!("Invalid part #")
}
}
}