diff --git a/2023/Cargo.lock b/2023/Cargo.lock index 8028695..666ee58 100644 --- a/2023/Cargo.lock +++ b/2023/Cargo.lock @@ -89,6 +89,8 @@ version = "2023.0.0" dependencies = [ "itertools", "nom", + "num", + "num-traits", ] [[package]] @@ -273,6 +275,82 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "pin-project-lite" version = "0.2.13" diff --git a/2023/day-7/Cargo.toml b/2023/day-7/Cargo.toml index 5ea5f49..d38efff 100644 --- a/2023/day-7/Cargo.toml +++ b/2023/day-7/Cargo.toml @@ -10,3 +10,5 @@ repository.workspace = true [dependencies] nom.workspace = true itertools.workspace = true +num = "0.4.1" +num-traits = "0.2.17" diff --git a/2023/day-7/src/part1.rs b/2023/day-7/src/part1.rs index a7db908..d56d99d 100644 --- a/2023/day-7/src/part1.rs +++ b/2023/day-7/src/part1.rs @@ -1,20 +1,181 @@ #![warn(clippy::all, clippy::pedantic)] +use itertools::Itertools; +use nom::{character::complete, multi::separated_list1, sequence::separated_pair, IResult}; +use std::{ + cmp::{Ord, Ordering, PartialOrd}, + collections::BTreeMap, + str::FromStr, +}; + +#[derive(Debug)] +struct Day1Part1Error; + +#[derive(Debug, Ord, Eq, PartialEq, PartialOrd)] +enum Card { + Two, + Three, + Four, + Five, + Six, + Seven, + Eight, + Nine, + Ten, + Jack, + Queen, + King, + Ace, +} +impl FromStr for Card { + type Err = Day1Part1Error; + fn from_str(input: &str) -> Result { + match input { + "2" => Ok(Self::Two), + "3" => Ok(Self::Three), + "4" => Ok(Self::Four), + "5" => Ok(Self::Five), + "6" => Ok(Self::Six), + "7" => Ok(Self::Seven), + "8" => Ok(Self::Eight), + "9" => Ok(Self::Nine), + "T" => Ok(Self::Ten), + "J" => Ok(Self::Jack), + "Q" => Ok(Self::Queen), + "K" => Ok(Self::King), + "A" => Ok(Self::Ace), + _ => Err(Day1Part1Error), + } + } +} + +impl From<&Card> for &u32 { + fn from(value: &Card) -> Self { + match value { + Card::Two => &2, + Card::Three => &3, + Card::Four => &4, + Card::Five => &5, + Card::Six => &6, + Card::Seven => &7, + Card::Eight => &8, + Card::Nine => &9, + Card::Ten => &10, + Card::Jack => &11, + Card::Queen => &12, + Card::King => &13, + Card::Ace => &14, + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] +enum HandType { + FiveOfAKind, + FourOfAKind, + FullHouse, + ThreeOfAKind, + TwoPair, + OnePair, + HighCard, +} + +impl From<&Hand> for HandType { + fn from(value: &Hand) -> Self { + let map = value.cards.iter().fold(BTreeMap::new(), |mut acc, card| { + if let Some(c) = acc.get_mut(card) { + *c += 1; + } else { + acc.insert(card, 1); + } + acc + }); + match map + .iter() + .sorted_by(|a, b| b.1.cmp(a.1)) + .collect::>()[..] + { + [(_, 5), ..] => Self::FiveOfAKind, + [(_, 4), ..] => Self::FourOfAKind, + [(_, 3), (_, 2), ..] => Self::FullHouse, + [(_, 3), ..] => Self::ThreeOfAKind, + [(_, 2), (_, 2), ..] => Self::TwoPair, + [(_, 2), ..] => Self::OnePair, + _ => Self::HighCard, + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord)] +struct Hand { + pub cards: [Card; 5], + pub bet: u32, +} +impl PartialOrd for Hand { + fn partial_cmp(&self, other: &Self) -> Option { + let a = HandType::from(self); + let b = HandType::from(other); + let c = b.cmp(&a); + match c { + Ordering::Equal => self + .cards + .iter() + .interleave(other.cards.iter()) + .tuples::<(_, _)>() + .find_map(|(a, b)| { + if b.cmp(a).is_ne() { + Some(a.cmp(b)) + } else { + None + } + }), + x => Some(x), + } + } +} #[must_use] -pub fn part1 (_input: &str) -> String { - "Not Finished".to_string() +pub fn part1(_input: &str) -> String { + let (_, mut hands) = parse_input(_input).expect("always valid input"); + hands.sort(); + + dbg!(hands) + .iter() + .enumerate() + .map(|(i, hand)| (i + 1) * hand.bet as usize) + .sum::() + .to_string() +} + +fn parse_hand(input: &str) -> IResult<&str, Hand> { + let (input, (cards, bet)) = + separated_pair(complete::alphanumeric1, complete::space1, complete::u32)(input)?; + let cards = cards + .chars() + .filter_map(|c| c.to_string().parse().ok()) + .collect::>() + .try_into() + .expect("should work"); + Ok((input, Hand { cards, bet })) +} + +fn parse_input(input: &str) -> IResult<&str, Vec> { + separated_list1(complete::line_ending, parse_hand)(input) } #[cfg(test)] mod test { use super::*; - const INPUT: &str = ""; + const INPUT: &str = "32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483"; #[test] fn part1_works() { let result = part1(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "6440".to_string()); } }