summaryrefslogtreecommitdiff
path: root/2023/src/bin/day_7.rs
diff options
context:
space:
mode:
Diffstat (limited to '2023/src/bin/day_7.rs')
-rw-r--r--2023/src/bin/day_7.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/2023/src/bin/day_7.rs b/2023/src/bin/day_7.rs
new file mode 100644
index 0000000..3b629bd
--- /dev/null
+++ b/2023/src/bin/day_7.rs
@@ -0,0 +1,169 @@
+use nom::{
+ branch::alt,
+ character::complete::{char as nom_char, line_ending, space1, u32 as nom_u32},
+ combinator::{map, value},
+ multi::separated_list1,
+ sequence::tuple,
+ IResult,
+};
+use std::{collections::BTreeMap, fs};
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let input = fs::read_to_string("inputs/day_7.txt")?;
+
+ {
+ let mut games_without_jokers = CardGame::parser(false)(&input).unwrap().1;
+ games_without_jokers.sort();
+ dbg!(games_without_jokers.calculate_winnings());
+ }
+
+ {
+ let mut games_with_jokers = CardGame::parser(true)(&input).unwrap().1;
+ games_with_jokers.sort();
+ dbg!(games_with_jokers.calculate_winnings());
+ }
+
+ Ok(())
+}
+
+#[derive(Debug)]
+struct CardGame(Vec<CardHand>);
+
+#[derive(Debug, PartialEq, Eq)]
+struct CardHand {
+ cards: [Card; 5],
+ bid: u32,
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+struct Card(u8);
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+enum CardRank {
+ HighCard,
+ OnePair,
+ TwoPair,
+ ThreeOfAKind,
+ FullHouse,
+ FourOfAKind,
+ FiveOfAKind,
+}
+
+impl CardGame {
+ fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> {
+ move |input: &str| {
+ map(
+ separated_list1(line_ending, CardHand::parser(with_jokers)),
+ CardGame,
+ )(input)
+ }
+ }
+
+ fn sort(&mut self) {
+ self.0.sort()
+ }
+
+ fn calculate_winnings(&self) -> u32 {
+ self.0
+ .iter()
+ .enumerate()
+ .map(|(i, hand)| hand.bid * (i as u32 + 1))
+ .sum()
+ }
+}
+
+impl CardHand {
+ fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> {
+ move |input: &str| {
+ map(
+ tuple((
+ Card::parser(with_jokers),
+ Card::parser(with_jokers),
+ Card::parser(with_jokers),
+ Card::parser(with_jokers),
+ Card::parser(with_jokers),
+ space1,
+ nom_u32,
+ )),
+ |(c1, c2, c3, c4, c5, _, bid)| CardHand {
+ cards: [c1, c2, c3, c4, c5],
+ bid,
+ },
+ )(input)
+ }
+ }
+
+ fn card_rank(&self) -> CardRank {
+ let mut cards_set: BTreeMap<Card, u8> = BTreeMap::new();
+ for card in &self.cards {
+ *cards_set.entry(*card).or_insert(0) += 1;
+ }
+
+ let jokers = cards_set.get(&Card(1)).cloned().unwrap_or(0);
+ cards_set.remove(&Card(1));
+
+ let mut card_counts: Vec<u8> = cards_set.into_values().collect();
+ card_counts.sort_by(|a, b| b.cmp(a));
+
+ if card_counts.len() == 0 {
+ // all 5 were jokers!
+ CardRank::FiveOfAKind
+ } else {
+ card_counts[0] += jokers;
+ if card_counts[0] == 5 {
+ CardRank::FiveOfAKind
+ } else if card_counts[0] == 4 {
+ CardRank::FourOfAKind
+ } else if card_counts[0] == 3 && card_counts[1] == 2 {
+ CardRank::FullHouse
+ } else if card_counts[0] == 3 {
+ CardRank::ThreeOfAKind
+ } else if card_counts[0] == 2 && card_counts[1] == 2 {
+ CardRank::TwoPair
+ } else if card_counts[0] == 2 {
+ CardRank::OnePair
+ } else {
+ CardRank::HighCard
+ }
+ }
+ }
+}
+
+impl Card {
+ fn parser(with_jokers: bool) -> impl FnMut(&str) -> IResult<&str, Self> {
+ move |input: &str| {
+ map(
+ alt((
+ value(2, nom_char('2')),
+ value(3, nom_char('3')),
+ value(4, nom_char('4')),
+ value(5, nom_char('5')),
+ value(6, nom_char('6')),
+ value(7, nom_char('7')),
+ value(8, nom_char('8')),
+ value(9, nom_char('9')),
+ value(10, nom_char('T')),
+ value(if with_jokers { 1 } else { 11 }, nom_char('J')),
+ value(12, nom_char('Q')),
+ value(13, nom_char('K')),
+ value(14, nom_char('A')),
+ )),
+ Card,
+ )(input)
+ }
+ }
+}
+
+impl std::cmp::Ord for CardHand {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.card_rank()
+ .cmp(&other.card_rank())
+ .then(self.cards.cmp(&other.cards))
+ }
+}
+
+impl std::cmp::PartialOrd for CardHand {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}