From c037d9b3d340373355e3a6dd1c4785ae3ef17143 Mon Sep 17 00:00:00 2001 From: Leandro Lisboa Penz Date: Sat, 9 Dec 2023 14:32:33 +0000 Subject: [PATCH] Day 07a --- Cargo.lock | 19 +++++ Cargo.toml | 1 + day07/Cargo.toml | 10 +++ day07/src/bin/day07a.rs | 31 ++++++++ day07/src/lib.rs | 152 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+) create mode 100644 day07/Cargo.toml create mode 100644 day07/src/bin/day07a.rs create mode 100644 day07/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 54bb767..fce5313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,6 +197,16 @@ dependencies = [ "nom", ] +[[package]] +name = "day07" +version = "0.1.0" +dependencies = [ + "aoc", + "color-eyre", + "itertools", + "nom", +] + [[package]] name = "day09" version = "0.1.0" @@ -235,6 +245,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index 4f812a4..3c2d32b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "day04", "day05", "day06", + "day07", "day09", ] diff --git a/day07/Cargo.toml b/day07/Cargo.toml new file mode 100644 index 0000000..fbf3f27 --- /dev/null +++ b/day07/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day07" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../aoc" } +color-eyre = "0.6.2" +itertools = "0.12.0" +nom = "7.1.3" diff --git a/day07/src/bin/day07a.rs b/day07/src/bin/day07a.rs new file mode 100644 index 0000000..7d2ed09 --- /dev/null +++ b/day07/src/bin/day07a.rs @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +use itertools::Itertools; +use std::io::{stdin, BufRead}; + +use day07::*; + +fn process(bufin: impl BufRead) -> Result { + let input = parser::parse(bufin)?; + Ok(input + .into_iter() + .map(|(hand, bid)| (hand.value(), bid)) + .sorted() + .enumerate() + .map(|(i, (_hand, bid))| (i as i64 + 1) * bid) + .sum()) +} + +#[test] +fn test() -> Result<()> { + assert_eq!(process(EXAMPLE.as_bytes())?, 6440); + Ok(()) +} + +fn main() -> Result<()> { + color_eyre::install()?; + println!("{}", process(stdin().lock())?); + Ok(()) +} diff --git a/day07/src/lib.rs b/day07/src/lib.rs new file mode 100644 index 0000000..1bd1227 --- /dev/null +++ b/day07/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +pub use color_eyre::{eyre::eyre, Report, Result}; +use itertools::Itertools; +use std::collections::BTreeMap; +use std::str::FromStr; + +pub const EXAMPLE: &str = "32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 +"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Card(pub char); + +impl From<&Card> for i64 { + fn from(value: &Card) -> i64 { + match value { + Card('A') => 12, + Card('K') => 11, + Card('Q') => 10, + Card('J') => 9, + Card('T') => 8, + Card(n) => n.to_digit(10).expect("invalid card") as i64 - 2, + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Type { + Highest = 0, + OnePair, + TwoPair, + ThreeOfKind, + FullHouse, + FourOfKind, + FiveOfKind, +} + +pub struct Hand(pub [Card; 5]); + +impl Hand { + pub fn htype(&self) -> Type { + let card_count = self + .0 + .iter() + .fold(BTreeMap::::new(), |mut counts, card| { + let e = counts.entry(*card).or_default(); + *e += 1; + counts + }); + let count_card = card_count + .into_iter() + .map(|(card, count)| (count, card)) + .sorted() + .rev() + .collect::>(); + if count_card[0].0 == 5 { + Type::FiveOfKind + } else if count_card[0].0 == 4 { + Type::FourOfKind + } else if count_card[0].0 == 3 && count_card[1].0 == 2 { + Type::FullHouse + } else if count_card[0].0 == 3 { + Type::ThreeOfKind + } else if count_card[0].0 == 2 && count_card[1].0 == 2 { + Type::TwoPair + } else if count_card[0].0 == 2 { + Type::OnePair + } else { + Type::Highest + } + } + + pub fn value(&self) -> i64 { + self.htype() as i64 * 13_i64.pow(6) + i64::from(self) + } +} + +impl From<&Hand> for i64 { + fn from(hand: &Hand) -> i64 { + hand.0 + .iter() + .rev() + .enumerate() + .map(|(i, c)| i64::from(c) * 13_i64.pow(i as u32)) + .sum() + } +} + +impl TryFrom<&[Card]> for Hand { + type Error = Report; + fn try_from(cards: &[Card]) -> Result { + if cards.len() != 5 { + return Err(eyre!("invalid length for hand")); + } + Ok(Hand([cards[0], cards[1], cards[2], cards[3], cards[4]])) + } +} + +impl FromStr for Hand { + type Err = Report; + fn from_str(s: &str) -> Result { + let cards = s.chars().map(Card).collect::>(); + Hand::try_from(cards.as_ref()) + } +} + +#[test] +fn test_hand_type() -> Result<()> { + assert_eq!(Hand::from_str("AAAAA")?.htype(), Type::FiveOfKind); + assert_eq!(Hand::from_str("AA1AA")?.htype(), Type::FourOfKind); + assert_eq!(Hand::from_str("A2A2A")?.htype(), Type::FullHouse); + assert_eq!(Hand::from_str("A2A1A")?.htype(), Type::ThreeOfKind); + assert_eq!(Hand::from_str("A221A")?.htype(), Type::TwoPair); + assert_eq!(Hand::from_str("A2219")?.htype(), Type::OnePair); + assert_eq!(Hand::from_str("A2319")?.htype(), Type::Highest); + Ok(()) +} + +pub mod parser { + use aoc::parser::*; + + use super::*; + + fn card(input: &str) -> IResult<&str, Card> { + let (input, c) = character::one_of("AKQJT98765432")(input)?; + Ok((input, Card(c))) + } + + fn line(input: &str) -> IResult<&str, (Hand, i64)> { + let (input, cards) = multi::count(card, 5)(input)?; + let (input, _) = character::space1(input)?; + let (input, bid) = character::i64(input)?; + let (input, _) = character::newline(input)?; + Ok((input, (Hand::try_from(cards.as_ref()).unwrap(), bid))) + } + + pub fn parse(mut bufin: impl BufRead) -> Result> { + aoc::parse_with!(multi::many1(line), bufin) + } +} + +#[test] +fn test() -> Result<()> { + assert_eq!(parser::parse(EXAMPLE.as_bytes())?.len(), 5); + Ok(()) +}