From 3523330c422d8d722e188b4ebd8da53b838b9046 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Thu, 7 Dec 2023 19:38:00 -0600 Subject: feat: impl day 7 --- src/day_07.rs | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/main.rs | 3 +- 3 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 src/day_07.rs (limited to 'src') diff --git a/src/day_07.rs b/src/day_07.rs new file mode 100644 index 0000000..e688d16 --- /dev/null +++ b/src/day_07.rs @@ -0,0 +1,186 @@ +use std::{cmp::Reverse, collections::BTreeMap, str::FromStr}; + +use anyhow::Context; + +use crate::{Problem, Solution}; + +pub struct Day07; + +impl Problem for Day07 { + const DAY: u8 = 7; + + const INPUT: &'static str = include_str!("../input/day_07.txt"); +} + +impl Solution for Day07 { + type Answer1 = usize; + + type Answer2 = usize; + + fn part_1(input: &str) -> anyhow::Result { + let hand_bets = input + .lines() + .map(|s| { + s.trim() + .split_once(' ') + .with_context(|| format!("Invalid had format: {s}")) + .and_then(|(hand, bet)| anyhow::Ok((Reverse(hand.parse()?), bet.parse()?))) + }) + .try_collect::>, usize>>()?; + + Ok(hand_bets + .into_values() + .enumerate() + .map(|(i, bid)| bid * (i + 1)) + .sum()) + } + + fn part_2(input: &str) -> anyhow::Result { + let hand_bets = input + .lines() + .map(|s| { + s.trim() + .split_once(' ') + .with_context(|| format!("Invalid had format: {s}")) + .and_then(|(hand, bet)| anyhow::Ok((Reverse(hand.parse()?), bet.parse()?))) + }) + .try_collect::>, usize>>()?; + + Ok(hand_bets + .into_values() + .enumerate() + .map(|(i, bid)| bid * (i + 1)) + .sum()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum Hand { + FiveOfAKind([Card; 5]), + FourOfAKind([Card; 5]), + FullHouse([Card; 5]), + ThreeOfAKind([Card; 5]), + TwoPair([Card; 5]), + OnePair([Card; 5]), + HighCard([Card; 5]), +} + +impl Hand { + fn count_types(mut cards: [Card; 5]) -> (Vec, usize) { + cards.sort(); + + cards + .array_windows() + .rev() + .fold((Vec::from([1]), 0), |(mut acc, mut wilds), arr| { + match arr { + [Card::Joker, Card::Joker] if wilds == 0 => wilds += 1, + [_, Card::Joker] => wilds += 1, + [p, n] if p == n => *acc.last_mut().unwrap() += 1, + _ => acc.push(1), + } + + (acc, wilds) + }) + } +} + +impl From<[Card; 5]> for Hand { + fn from(cards: [Card; 5]) -> Self { + let (mut counts, wilds) = Hand::count_types(cards); + counts.sort(); + + let n = counts.pop().unwrap(); + let has_2 = counts.pop().is_some_and(|m| m == 2); + + match (n + wilds).min(5) { + 5 => Self::FiveOfAKind(cards), + 4 => Self::FourOfAKind(cards), + 3 if has_2 => Self::FullHouse(cards), + 3 => Self::ThreeOfAKind(cards), + 2 if has_2 => Self::TwoPair(cards), + 2 => Self::OnePair(cards), + _ => Self::HighCard(cards), + } + } +} +impl FromStr for Hand { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let cards: [Card; 5] = s + .chars() + .map(Card::try_from) + .try_collect::>()? + .try_into() + .map_err(|v| anyhow::format_err!("Incorrect number of cards: {v:?}"))?; + + Ok(Self::from(cards)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Card { + Ace, + King, + Queen, + Jack, + Ten, + Nine, + Eight, + Seven, + Six, + Five, + Four, + Three, + Two, + Joker, +} + +impl TryFrom for Card { + type Error = anyhow::Error; + + fn try_from(value: char) -> Result { + Ok(match value { + 'A' => Card::Ace, + 'K' => Card::King, + 'Q' => Card::Queen, + 'J' if WILD => Card::Joker, + 'J' => Card::Jack, + 'T' => Card::Ten, + '9' => Card::Nine, + '8' => Card::Eight, + '7' => Card::Seven, + '6' => Card::Six, + '5' => Card::Five, + '4' => Card::Four, + '3' => Card::Three, + '2' => Card::Two, + c => anyhow::bail!("Failed to parse card value: {c}"), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + const INPUT: &str = indoc::indoc! {" + 32T3K 765 + T55J5 684 + KK677 28 + KTJJT 220 + QQQJA 483 + "}; + + #[test] + fn test_part_1() -> anyhow::Result<()> { + Ok(assert_eq!(6440, Day07::part_1(INPUT)?)) + } + + #[test] + fn test_part_2() -> anyhow::Result<()> { + Ok(assert_eq!(5905, Day07::part_2(INPUT)?)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8875ecc..6564671 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(iterator_try_collect)] -#![feature(iter_array_chunks)] +#![feature(iterator_try_collect, iter_array_chunks, array_windows, let_chains)] pub mod day_01; pub mod day_02; @@ -7,6 +6,7 @@ pub mod day_03; pub mod day_04; pub mod day_05; pub mod day_06; +pub mod day_07; pub trait Problem { const DAY: u8; diff --git a/src/main.rs b/src/main.rs index 1c93d0a..e113a75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use aoc_2023::{ day_01::Day01, day_02::Day02, day_03::Day03, day_04::Day04, day_05::Day05, day_06::Day06, - Solution, + day_07::Day07, Solution, }; fn main() -> anyhow::Result<()> { @@ -10,6 +10,7 @@ fn main() -> anyhow::Result<()> { Day04::solve()?; Day05::solve()?; Day06::solve()?; + Day07::solve()?; Ok(()) } -- cgit v1.2.3-70-g09d2