Skip to content

Commit

Permalink
Day 07a
Browse files Browse the repository at this point in the history
  • Loading branch information
lpenz committed Dec 9, 2023
1 parent 1cb36a2 commit c037d9b
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"day04",
"day05",
"day06",
"day07",
"day09",
]

10 changes: 10 additions & 0 deletions day07/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
31 changes: 31 additions & 0 deletions day07/src/bin/day07a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2023 Leandro Lisboa Penz <lpenz@lpenz.org>
// 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<i64> {
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(())
}
152 changes: 152 additions & 0 deletions day07/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (C) 2023 Leandro Lisboa Penz <lpenz@lpenz.org>
// 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::<Card, usize>::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::<Vec<_>>();
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<Self, Self::Error> {
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<Self, Self::Err> {
let cards = s.chars().map(Card).collect::<Vec<_>>();
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<Vec<(Hand, i64)>> {
aoc::parse_with!(multi::many1(line), bufin)
}
}

#[test]
fn test() -> Result<()> {
assert_eq!(parser::parse(EXAMPLE.as_bytes())?.len(), 5);
Ok(())
}

0 comments on commit c037d9b

Please sign in to comment.