summaryrefslogtreecommitdiffstats
path: root/src/day_07.rs
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2023-12-07 19:38:00 -0600
committerToby Vincent <tobyv@tobyvin.dev>2023-12-07 19:38:00 -0600
commit3523330c422d8d722e188b4ebd8da53b838b9046 (patch)
tree5d12ea30ca8d6e85346f99c2b277292fb942f40f /src/day_07.rs
parent49c163b6002d7694eec3288e3f4de93067bbe116 (diff)
feat: impl day 7
Diffstat (limited to 'src/day_07.rs')
-rw-r--r--src/day_07.rs186
1 files changed, 186 insertions, 0 deletions
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<Self::Answer1> {
+ 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::<BTreeMap<Reverse<Hand<false>>, usize>>()?;
+
+ Ok(hand_bets
+ .into_values()
+ .enumerate()
+ .map(|(i, bid)| bid * (i + 1))
+ .sum())
+ }
+
+ fn part_2(input: &str) -> anyhow::Result<Self::Answer2> {
+ 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::<BTreeMap<Reverse<Hand<true>>, usize>>()?;
+
+ Ok(hand_bets
+ .into_values()
+ .enumerate()
+ .map(|(i, bid)| bid * (i + 1))
+ .sum())
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum Hand<const WILD: bool> {
+ FiveOfAKind([Card<WILD>; 5]),
+ FourOfAKind([Card<WILD>; 5]),
+ FullHouse([Card<WILD>; 5]),
+ ThreeOfAKind([Card<WILD>; 5]),
+ TwoPair([Card<WILD>; 5]),
+ OnePair([Card<WILD>; 5]),
+ HighCard([Card<WILD>; 5]),
+}
+
+impl<const WILD: bool> Hand<WILD> {
+ fn count_types(mut cards: [Card<WILD>; 5]) -> (Vec<usize>, 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<const WILD: bool> From<[Card<WILD>; 5]> for Hand<WILD> {
+ fn from(cards: [Card<WILD>; 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<const WILD: bool> FromStr for Hand<WILD> {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let cards: [Card<WILD>; 5] = s
+ .chars()
+ .map(Card::try_from)
+ .try_collect::<Vec<_>>()?
+ .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<const WILD: bool> {
+ Ace,
+ King,
+ Queen,
+ Jack,
+ Ten,
+ Nine,
+ Eight,
+ Seven,
+ Six,
+ Five,
+ Four,
+ Three,
+ Two,
+ Joker,
+}
+
+impl<const WILD: bool> TryFrom<char> for Card<WILD> {
+ type Error = anyhow::Error;
+
+ fn try_from(value: char) -> Result<Self, Self::Error> {
+ 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)?))
+ }
+}