Day 7 horrors

This commit is contained in:
Tyler Beckman 2023-12-06 23:28:51 -07:00
parent e5a8846271
commit e2bdeda5c7
Signed by: Ty
GPG key ID: 2813440C772555A4
3 changed files with 233 additions and 11 deletions

217
src/day7/mod.rs Normal file
View file

@ -0,0 +1,217 @@
use std::{cmp::Ordering, collections::HashMap};
use crate::utils::Day;
#[derive(Debug, Default)]
pub struct Day7 {
pub input: String,
pub hands: Vec<Hand>,
}
#[derive(Debug)]
pub struct Hand {
cards: String,
bid: usize,
}
impl Day7 {
fn classify_hand(hand: &Hand, part: u8) -> u8 {
let parsed = hand
.cards
.chars()
.fold(HashMap::<char, u8>::new(), |mut acc, card| {
if let Some(count) = acc.get_mut(&card) {
*count += 1;
} else {
acc.insert(card, 1);
}
acc
});
let mut repetitions = parsed
.iter()
.fold(Vec::<u8>::new(), |mut acc, (card, count)| {
if !(part == 2 && card == &'J') {
acc.push(*count);
}
acc
});
repetitions.sort();
repetitions.reverse();
match part {
1 => match repetitions.as_slice() {
&[5] => 7, // 5 of a kind
&[4, 1] => 6, // 4 of a kind
&[3, 2] => 5, // full house
&[3, 1, 1] => 4, // 3 of a kind
&[2, 2, 1] => 3, // 2 pair
&[2, 1, 1, 1] => 2, // 1 pair
&[1, 1, 1, 1, 1] => 1, // high card
_ => 0,
},
2 => {
let jokers = *parsed.get(&'J').unwrap_or(&0);
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 7) == &[5] {
return 7;
} // 5 of a kind
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 6) == &[4, 1] {
return 6;
} // 4 of a kind
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 5) == &[3, 2] {
return 5;
} // full house
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 4) == &[3, 1, 1] {
return 4;
} // 3 of a kind
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 3) == &[2, 2, 1] {
return 3;
} // 2 pair
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 2) == &[2, 1, 1, 1] {
return 2;
} // 1 pair
if Self::fill_repetitions_with_jokers(repetitions.clone(), jokers, 1) == &[1, 1, 1, 1, 1] {
return 1;
} // high card
0
}
_ => panic!(),
}
}
fn fill_repetitions_with_jokers(
mut repetitions: Vec<u8>,
mut jokers: u8,
class: u8,
) -> Vec<u8> {
for _ in 0..(5 - repetitions.len()) { repetitions.push(0) }
match class {
7 | 6 => {
repetitions[0] += jokers;
jokers = 0;
}
5 => {
let using = (3 - repetitions[0].min(3)).min(jokers); // amnt of jokers needed for 3 repetitions
jokers -= using;
repetitions[0] += using;
let using = (2 - repetitions[1].min(2)).min(jokers); // amnt of jokers needed for 2 repetitions
jokers -= using;
repetitions[1] += using;
}
4 => {
let using = (3 - repetitions[0].min(3)).min(jokers);
jokers -= using;
repetitions[0] += using;
}
3 => {
let using = (2 - repetitions[0].min(2)).min(jokers); // amnt of jokers needed for 2 repetitions
jokers -= using;
repetitions[0] += using;
let using = (2 - repetitions[1].min(2)).min(jokers); // amnt of jokers needed for 2 repetitions
jokers -= using;
repetitions[1] += using;
}
2 => {
let using = (2 - repetitions[0].min(2)).min(jokers);
jokers -= using;
repetitions[0] += using;
}
1 => (),
_ => panic!(),
}
repetitions.push(jokers); // Add unused jokers
repetitions.sort();
repetitions.reverse();
repetitions = repetitions.into_iter().filter(|n| n != &0).collect();
repetitions
}
}
impl Day for Day7 {
fn part1(&mut self) -> String {
self.hands.clear();
self.hands = self
.input
.lines()
.map(|line| {
let (cards, bid) = line.split_once(' ').unwrap();
Hand {
cards: cards.to_string(),
bid: bid.parse().unwrap(),
}
})
.collect();
let order = "AKQJT98765432".to_string();
self.hands.sort_by(|a, b| {
let (a_class, b_class) = (Self::classify_hand(a, 1), Self::classify_hand(b, 1));
if a_class > b_class {
Ordering::Less
} else if a_class < b_class {
Ordering::Greater
} else {
let b_chars = b.cards.chars().collect::<Vec<_>>();
for (i, card) in a.cards.char_indices() {
if order.find(card).unwrap() < order.find(b_chars[i]).unwrap() {
return Ordering::Less;
} else if order.find(card).unwrap() > order.find(b_chars[i]).unwrap() {
return Ordering::Greater;
} else {
continue;
}
}
panic!("TIE");
}
});
self.hands.reverse();
self.hands
.iter()
.enumerate()
.fold(0usize, |acc, (i, hand)| (i + 1) * hand.bid + acc)
.to_string()
}
fn part2(&mut self) -> String {
let order = "AKQT98765432J".to_string();
self.hands.sort_by(|a, b| {
let (a_class, b_class) = (Self::classify_hand(a, 2), Self::classify_hand(b, 2));
if a_class > b_class {
Ordering::Less
} else if a_class < b_class {
Ordering::Greater
} else {
let b_chars = b.cards.chars().collect::<Vec<_>>();
for (i, card) in a.cards.char_indices() {
if order.find(card).unwrap() < order.find(b_chars[i]).unwrap() {
return Ordering::Less;
} else if order.find(card).unwrap() > order.find(b_chars[i]).unwrap() {
return Ordering::Greater;
} else {
continue;
}
}
panic!("TIE");
}
});
self.hands.reverse();
self.hands
.iter()
.enumerate()
.fold(0usize, |acc, (i, hand)| (i + 1) * hand.bid + acc)
.to_string()
}
}

View file

@ -56,7 +56,7 @@ pub async fn fetch_large(day: u8, part: u8) -> (String, Option<String>) {
) )
} }
pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, String) { pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, Option<String>) {
let html = reqwest::Client::new() let html = reqwest::Client::new()
.get(format!("https://adventofcode.com/2023/day/{day}")) .get(format!("https://adventofcode.com/2023/day/{day}"))
.header("Cookie", format!("session={auth}")) .header("Cookie", format!("session={auth}"))
@ -86,10 +86,7 @@ pub async fn fetch_example(auth: String, day: u8, part: u8) -> (String, String)
let solution = dom let solution = dom
.select(&solution_selector) .select(&solution_selector)
.last() .last()
.expect("Unable to find example solution for part, is auth correct?") .map(|s| s.inner_html().trim_end().to_string());
.inner_html()
.trim_end()
.to_string();
(input, solution) (input, solution)
} }

View file

@ -6,7 +6,9 @@ use std::{
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use crate::{day1::Day1, day2::Day2, day3::Day3, day4::Day4, day5::Day5, day6::Day6, utils::Day}; use crate::{
day1::Day1, day2::Day2, day3::Day3, day4::Day4, day5::Day5, day6::Day6, day7::Day7, utils::Day,
};
pub mod day1; pub mod day1;
pub mod day2; pub mod day2;
@ -14,6 +16,7 @@ pub mod day3;
pub mod day4; pub mod day4;
pub mod day5; pub mod day5;
pub mod day6; pub mod day6;
pub mod day7;
pub mod fetcher; pub mod fetcher;
pub mod utils; pub mod utils;
@ -88,10 +91,7 @@ async fn main() {
false => (fetcher::fetch_input(cli.auth, day).await, None), false => (fetcher::fetch_input(cli.auth, day).await, None),
true => fetcher::fetch_large(day, part).await, true => fetcher::fetch_large(day, part).await,
}, },
true => { true => fetcher::fetch_example(cli.auth, day, part).await
let example = fetcher::fetch_example(cli.auth, day, part).await;
(example.0, Some(example.1))
}
}; };
let mut day: Box<dyn Day> = match day { let mut day: Box<dyn Day> = match day {
@ -108,6 +108,10 @@ async fn main() {
}), }),
5 => Box::new(Day5 { input }), 5 => Box::new(Day5 { input }),
6 => Box::new(Day6 { input }), 6 => Box::new(Day6 { input }),
7 => Box::new(Day7 {
input,
hands: vec![],
}),
_ => panic!("Invalid day #"), _ => panic!("Invalid day #"),
}; };
let start = std::time::Instant::now(); let start = std::time::Instant::now();
@ -160,6 +164,10 @@ async fn main() {
}), }),
5 => Box::new(Day5 { input }), 5 => Box::new(Day5 { input }),
6 => Box::new(Day6 { input }), 6 => Box::new(Day6 { input }),
7 => Box::new(Day7 {
input,
hands: vec![],
}),
_ => panic!("Invalid day #"), _ => panic!("Invalid day #"),
}; };
let mut timings = Vec::<Duration>::new(); let mut timings = Vec::<Duration>::new();
@ -233,7 +241,7 @@ async fn main() {
stylized_input.push_str("\n+"); stylized_input.push_str("\n+");
stylized_input.push_str(&"-".repeat(input_width + 2)); stylized_input.push_str(&"-".repeat(input_width + 2));
stylized_input.push('+'); stylized_input.push('+');
println!("Input:\n{stylized_input}\nSolution: {solution}"); println!("Input:\n{stylized_input}\nSolution: {solution:?}");
} }
} }
} }