summaryrefslogtreecommitdiffstats
path: root/src
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
parent49c163b6002d7694eec3288e3f4de93067bbe116 (diff)
feat: impl day 7
Diffstat (limited to 'src')
-rw-r--r--src/day_07.rs186
-rw-r--r--src/lib.rs4
-rw-r--r--src/main.rs3
3 files changed, 190 insertions, 3 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)?))
+ }
+}
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(())
}