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()
|
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)
|
||||||
}
|
}
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -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:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue