Skip to content

Commit

Permalink
Day 7
Browse files Browse the repository at this point in the history
  • Loading branch information
kcaffrey committed Dec 7, 2023
1 parent eae7ec6 commit b4e20f0
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 8 deletions.
21 changes: 21 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 @@ -17,6 +17,7 @@ dhat-heap = ["dhat"]
[dependencies]
pico-args = "0.5.0"
dhat = { version = "0.3.2", optional = true }
enum-ordinalize = "4.2.1"

[profile.dhat]
inherits = "release"
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `37.5µs` | `80.3µs` |
| [Day 2](./src/bin/02.rs) | `43.8µs` | `42.8µs` |
| [Day 3](./src/bin/03.rs) | `115.0µs` | `113.1µs` |
| [Day 4](./src/bin/04.rs) | `52.4µs` | `55.1µs` |
| [Day 5](./src/bin/05.rs) | `23.4µs` | `27.1µs` |
| [Day 6](./src/bin/06.rs) | `193.0ns` | `105.0ns` |

**Total: 0.59ms**
| [Day 1](./src/bin/01.rs) | `39.8µs` | `80.4µs` |
| [Day 2](./src/bin/02.rs) | `44.6µs` | `44.2µs` |
| [Day 3](./src/bin/03.rs) | `117.7µs` | `116.0µs` |
| [Day 4](./src/bin/04.rs) | `53.3µs` | `57.1µs` |
| [Day 5](./src/bin/05.rs) | `23.8µs` | `27.3µs` |
| [Day 6](./src/bin/06.rs) | `202.0ns` | `102.0ns` |
| [Day 7](./src/bin/07.rs) | `137.7µs` | `151.9µs` |

**Total: 0.89ms**
<!--- benchmarking table --->

---
Expand Down
5 changes: 5 additions & 0 deletions data/examples/07.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
209 changes: 209 additions & 0 deletions src/bin/07.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
use enum_ordinalize::Ordinalize;

advent_of_code::solution!(7);

pub fn part_one(input: &str) -> Option<u32> {
score(input, None)
}

pub fn part_two(input: &str) -> Option<u32> {
score(input, Some(Card::Jack))
}

fn score(input: &str, joker: Option<Card>) -> Option<u32> {
let mut hands = input
.lines()
.map(|line| {
let (hand_str, bid_str) = line.split_once(' ').ok_or(ParseHandErr)?;
Ok::<_, ParseHandErr>((
Hand::from_str(hand_str, joker)?,
bid_str.parse::<u32>().map_err(|_| ParseHandErr)?,
))
})
.collect::<Result<Vec<_>, _>>()
.expect("should parse hands correctly");
hands.sort_by_key(|hand| hand.0);
Some(
hands
.into_iter()
.enumerate()
.map(|(index, (_, bid))| (index as u32 + 1) * bid)
.sum(),
)
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Ordinalize)]
enum Card {
#[default]
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
enum HandType {
#[default]
HighCard,
OnePair,
TwoPair,
ThreeOfAKind,
FullHouse,
FourOfAKind,
FiveOfAKind,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
struct Hand {
hand_type: HandType,
cards: [Card; 5],
joker: Option<Card>,
}

impl Hand {
pub fn from_str(s: &str, joker: Option<Card>) -> Result<Self, ParseHandErr> {
if s.len() != 5 {
return Err(ParseHandErr);
}
let mut hand = Hand {
joker,
..Default::default()
};
for (i, card) in s.chars().map(Card::try_from).enumerate() {
hand.cards[i] = card?;
}
hand.hand_type = hand_type(hand.cards, joker);
Ok(hand)
}

fn card_ordering_index(&self, card: Card) -> usize {
let Some(joker) = self.joker else {
return card.ordinal() as usize;
};
if joker == card {
return 0;
}
card.ordinal() as usize + 1
}

fn cards_ordering(&self) -> [usize; 5] {
let mut ordering_values = [0; 5];
for (index, card) in self.cards.iter().copied().enumerate() {
ordering_values[index] = self.card_ordering_index(card);
}
ordering_values
}
}

impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.hand_type.cmp(&other.hand_type) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
self.cards_ordering().cmp(&other.cards_ordering())
}
}

fn hand_type(cards: [Card; 5], joker: Option<Card>) -> HandType {
let mut counts = [0; 13];
let mut joker_count = 0;
for card in cards {
if joker.filter(|&c| c == card).is_some() {
joker_count += 1;
} else {
counts[card.ordinal() as usize] += 1;
}
}
let mut five_count = 0;
let mut four_count = 0;
let mut three_count = 0;
let mut two_count = 0;
for count in counts {
match count {
5 => five_count += 1,
4 => four_count += 1,
3 => three_count += 1,
2 => two_count += 1,
_ => {}
}
}
match (five_count, four_count, three_count, two_count, joker_count) {
(1, _, _, _, _) => HandType::FiveOfAKind,
(_, 1, _, _, 1) => HandType::FiveOfAKind,
(_, _, 1, _, 2) => HandType::FiveOfAKind,
(_, _, _, 1, 3) => HandType::FiveOfAKind,
(_, _, _, _, 4) => HandType::FiveOfAKind,
(_, _, _, _, 5) => HandType::FiveOfAKind,
(_, 1, _, _, _) => HandType::FourOfAKind,
(_, _, 1, _, 1) => HandType::FourOfAKind,
(_, _, 0, 1, 2) => HandType::FourOfAKind,
(_, _, 0, 0, 3) => HandType::FourOfAKind,
(_, _, 1, 1, _) => HandType::FullHouse,
(_, _, 0, 2, 1) => HandType::FullHouse,
(_, _, 1, 0, _) => HandType::ThreeOfAKind,
(_, _, 0, 1, 1) => HandType::ThreeOfAKind,
(_, _, 0, 0, 2) => HandType::ThreeOfAKind,
(_, _, _, 2, _) => HandType::TwoPair,
(_, _, _, 1, _) => HandType::OnePair,
(0, 0, 0, 0, 1) => HandType::OnePair,
_ => HandType::HighCard,
}
}

#[derive(Debug)]
struct ParseHandErr;

impl TryFrom<char> for Card {
type Error = ParseHandErr;

fn try_from(value: char) -> Result<Self, Self::Error> {
Ok(match value {
'2' => Self::Two,
'3' => Self::Three,
'4' => Self::Four,
'5' => Self::Five,
'6' => Self::Six,
'7' => Self::Seven,
'8' => Self::Eight,
'9' => Self::Nine,
'T' => Self::Ten,
'J' => Self::Jack,
'Q' => Self::Queen,
'K' => Self::King,
'A' => Self::Ace,
_ => return Err(ParseHandErr),
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(6440));
}

#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(5905));
}
}

0 comments on commit b4e20f0

Please sign in to comment.