Day 7 horrors
This commit is contained in:
parent
e5a8846271
commit
e2bdeda5c7
3 changed files with 233 additions and 11 deletions
217
src/day7/mod.rs
Normal file
217
src/day7/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
.get(format!("https://adventofcode.com/2023/day/{day}"))
|
||||
.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
|
||||
.select(&solution_selector)
|
||||
.last()
|
||||
.expect("Unable to find example solution for part, is auth correct?")
|
||||
.inner_html()
|
||||
.trim_end()
|
||||
.to_string();
|
||||
.map(|s| s.inner_html().trim_end().to_string());
|
||||
|
||||
(input, solution)
|
||||
}
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -6,7 +6,9 @@ use std::{
|
|||
|
||||
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 day2;
|
||||
|
@ -14,6 +16,7 @@ pub mod day3;
|
|||
pub mod day4;
|
||||
pub mod day5;
|
||||
pub mod day6;
|
||||
pub mod day7;
|
||||
pub mod fetcher;
|
||||
pub mod utils;
|
||||
|
||||
|
@ -88,10 +91,7 @@ async fn main() {
|
|||
false => (fetcher::fetch_input(cli.auth, day).await, None),
|
||||
true => fetcher::fetch_large(day, part).await,
|
||||
},
|
||||
true => {
|
||||
let example = fetcher::fetch_example(cli.auth, day, part).await;
|
||||
(example.0, Some(example.1))
|
||||
}
|
||||
true => fetcher::fetch_example(cli.auth, day, part).await
|
||||
};
|
||||
|
||||
let mut day: Box<dyn Day> = match day {
|
||||
|
@ -108,6 +108,10 @@ async fn main() {
|
|||
}),
|
||||
5 => Box::new(Day5 { input }),
|
||||
6 => Box::new(Day6 { input }),
|
||||
7 => Box::new(Day7 {
|
||||
input,
|
||||
hands: vec![],
|
||||
}),
|
||||
_ => panic!("Invalid day #"),
|
||||
};
|
||||
let start = std::time::Instant::now();
|
||||
|
@ -160,6 +164,10 @@ async fn main() {
|
|||
}),
|
||||
5 => Box::new(Day5 { input }),
|
||||
6 => Box::new(Day6 { input }),
|
||||
7 => Box::new(Day7 {
|
||||
input,
|
||||
hands: vec![],
|
||||
}),
|
||||
_ => panic!("Invalid day #"),
|
||||
};
|
||||
let mut timings = Vec::<Duration>::new();
|
||||
|
@ -233,7 +241,7 @@ async fn main() {
|
|||
stylized_input.push_str("\n+");
|
||||
stylized_input.push_str(&"-".repeat(input_width + 2));
|
||||
stylized_input.push('+');
|
||||
println!("Input:\n{stylized_input}\nSolution: {solution}");
|
||||
println!("Input:\n{stylized_input}\nSolution: {solution:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue