day 7 solution
parent
da50cbd57b
commit
df1e26462d
|
@ -0,0 +1,360 @@
|
|||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
|
||||
enum Card {
|
||||
A,
|
||||
K,
|
||||
Q,
|
||||
J,
|
||||
T,
|
||||
C9,
|
||||
C8,
|
||||
C7,
|
||||
C6,
|
||||
C5,
|
||||
C4,
|
||||
C3,
|
||||
C2,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
|
||||
enum Part2Card {
|
||||
A,
|
||||
K,
|
||||
Q,
|
||||
T,
|
||||
C9,
|
||||
C8,
|
||||
C7,
|
||||
C6,
|
||||
C5,
|
||||
C4,
|
||||
C3,
|
||||
C2,
|
||||
J,
|
||||
}
|
||||
|
||||
fn card_to_part2(input: &Card) -> Part2Card {
|
||||
match input {
|
||||
Card::A => Part2Card::A,
|
||||
Card::K => Part2Card::K,
|
||||
Card::Q => Part2Card::Q,
|
||||
Card::T => Part2Card::T,
|
||||
Card::C9 => Part2Card::C9,
|
||||
Card::C8 => Part2Card::C8,
|
||||
Card::C7 => Part2Card::C7,
|
||||
Card::C6 => Part2Card::C6,
|
||||
Card::C5 => Part2Card::C5,
|
||||
Card::C4 => Part2Card::C4,
|
||||
Card::C3 => Part2Card::C3,
|
||||
Card::C2 => Part2Card::C2,
|
||||
Card::J => Part2Card::J,
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Card {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Card, Self::Err> {
|
||||
match input {
|
||||
"A" => Ok(Card::A),
|
||||
"K" => Ok(Card::K),
|
||||
"Q" => Ok(Card::Q),
|
||||
"J" => Ok(Card::J),
|
||||
"T" => Ok(Card::T),
|
||||
"9" => Ok(Card::C9),
|
||||
"8" => Ok(Card::C8),
|
||||
"7" => Ok(Card::C7),
|
||||
"6" => Ok(Card::C6),
|
||||
"5" => Ok(Card::C5),
|
||||
"4" => Ok(Card::C4),
|
||||
"3" => Ok(Card::C3),
|
||||
"2" => Ok(Card::C2),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||
enum HandKinds {
|
||||
FiveAlike,
|
||||
FourAlike,
|
||||
FullHouse,
|
||||
ThreeAlike,
|
||||
TwoPair,
|
||||
OnePair,
|
||||
HighCard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Hand {
|
||||
bid: u64,
|
||||
cards: [Card; 5],
|
||||
hand_kind: HandKinds,
|
||||
}
|
||||
|
||||
impl PartialEq for Hand {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hand_kind == other.hand_kind && self.cards == other.cards
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Hand {}
|
||||
|
||||
impl PartialOrd for Hand {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Hand {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.hand_kind == other.hand_kind {
|
||||
if self.cards[0] != other.cards[0] {
|
||||
self.cards[0].cmp(&other.cards[0])
|
||||
} else if self.cards[1] != other.cards[1] {
|
||||
self.cards[1].cmp(&other.cards[1])
|
||||
} else if self.cards[2] != other.cards[2] {
|
||||
self.cards[2].cmp(&other.cards[2])
|
||||
} else if self.cards[3] != other.cards[3] {
|
||||
self.cards[3].cmp(&other.cards[3])
|
||||
} else if self.cards[4] != other.cards[4] {
|
||||
self.cards[4].cmp(&other.cards[4])
|
||||
} else {
|
||||
panic!(
|
||||
"could not sort hand {:?} with {:?}",
|
||||
self.cards, other.cards
|
||||
)
|
||||
}
|
||||
} else {
|
||||
self.hand_kind.cmp(&other.hand_kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Part2Hand {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hand_kind == other.hand_kind && self.cards == other.cards
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Part2Hand {}
|
||||
|
||||
impl PartialOrd for Part2Hand {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Part2Hand {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.hand_kind == other.hand_kind {
|
||||
if self.cards[0] != other.cards[0] {
|
||||
self.cards[0].cmp(&other.cards[0])
|
||||
} else if self.cards[1] != other.cards[1] {
|
||||
self.cards[1].cmp(&other.cards[1])
|
||||
} else if self.cards[2] != other.cards[2] {
|
||||
self.cards[2].cmp(&other.cards[2])
|
||||
} else if self.cards[3] != other.cards[3] {
|
||||
self.cards[3].cmp(&other.cards[3])
|
||||
} else if self.cards[4] != other.cards[4] {
|
||||
self.cards[4].cmp(&other.cards[4])
|
||||
} else {
|
||||
panic!(
|
||||
"could not sort hand {:?} with {:?}",
|
||||
self.cards, other.cards
|
||||
)
|
||||
}
|
||||
} else {
|
||||
self.hand_kind.cmp(&other.hand_kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hand_type<T>(cards: &[T; 5]) -> HandKinds
|
||||
where
|
||||
T: std::fmt::Debug + Eq + PartialEq + PartialOrd + std::hash::Hash,
|
||||
{
|
||||
let mut distinct_cards: HashSet<&T> = HashSet::new();
|
||||
for c in cards.iter() {
|
||||
distinct_cards.insert(c);
|
||||
}
|
||||
match distinct_cards.len() {
|
||||
1 => HandKinds::FiveAlike,
|
||||
2 => {
|
||||
let tcount = cards
|
||||
.iter()
|
||||
.fold(0, |acc, c| if *c == cards[0] { acc + 1 } else { acc });
|
||||
if tcount == 4 || tcount == 1 {
|
||||
HandKinds::FourAlike
|
||||
} else {
|
||||
HandKinds::FullHouse
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
let mut counts = distinct_cards
|
||||
.iter()
|
||||
.map(|card| {
|
||||
cards
|
||||
.iter()
|
||||
.fold(0, |acc, c| if c == *card { acc + 1 } else { acc })
|
||||
})
|
||||
.collect::<Vec<u8>>();
|
||||
counts.sort();
|
||||
if counts == [1, 1, 3] {
|
||||
HandKinds::ThreeAlike
|
||||
} else {
|
||||
HandKinds::TwoPair
|
||||
}
|
||||
}
|
||||
4 => HandKinds::OnePair,
|
||||
5 => HandKinds::HighCard,
|
||||
_ => panic!(
|
||||
"expected to find size 1-5, but instead got {} from line {:?}",
|
||||
distinct_cards.len(),
|
||||
cards
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Part2Hand {
|
||||
bid: u64,
|
||||
cards: [Part2Card; 5],
|
||||
hand_kind: HandKinds,
|
||||
}
|
||||
|
||||
#[aoc_generator(day7)]
|
||||
fn parse(input: &str) -> Vec<Hand> {
|
||||
input
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
if !line.is_empty() {
|
||||
let parts = line.split(" ").collect::<Vec<&str>>();
|
||||
let cards = TryInto::<[Card; 5]>::try_into(
|
||||
parts[0]
|
||||
.split("")
|
||||
.filter_map(|c| {
|
||||
if c.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Card::from_str(c).unwrap())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Card>>(),
|
||||
)
|
||||
.unwrap_or_else(|v: Vec<Card>| {
|
||||
panic!("Expected a Vec of length 5 but it was {}", v.len())
|
||||
});
|
||||
let bid = parts[1].parse::<u64>().unwrap();
|
||||
let hand_kind = get_hand_type(&cards);
|
||||
Some(Hand {
|
||||
bid,
|
||||
cards,
|
||||
hand_kind,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[aoc(day7, part1)]
|
||||
fn part1(input: &[Hand]) -> u64 {
|
||||
let mut sum: u64 = 0;
|
||||
let mut hands = input.iter().map(|h| h.clone()).collect::<Vec<Hand>>();
|
||||
hands.sort();
|
||||
for (index, hand) in hands.iter().rev().enumerate() {
|
||||
sum += hand.bid * (index as u64 + 1);
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[aoc(day7, part2)]
|
||||
fn part2(input: &[Hand]) -> u64 {
|
||||
let mut sum: u64 = 0;
|
||||
let mut hands = input
|
||||
.iter()
|
||||
.map(|h| {
|
||||
let bid = h.bid;
|
||||
let p2cards = h
|
||||
.cards
|
||||
.iter()
|
||||
.map(|c| card_to_part2(c))
|
||||
.collect::<Vec<Part2Card>>();
|
||||
let mut counts: BTreeMap<&Part2Card, u32> = BTreeMap::new();
|
||||
p2cards.iter().for_each(|c| {
|
||||
if !counts.contains_key(c) {
|
||||
counts.insert(c, 0);
|
||||
}
|
||||
let ccount = counts.get_mut(c).unwrap();
|
||||
*ccount = *ccount + 1;
|
||||
});
|
||||
|
||||
let mut highest_count = 0;
|
||||
let mut highest_card = &Part2Card::J;
|
||||
for (card, count) in counts.iter() {
|
||||
if **card != Part2Card::J {
|
||||
if *count > highest_count {
|
||||
highest_count = *count;
|
||||
highest_card = card;
|
||||
} else if *count == highest_count && *card > highest_card {
|
||||
highest_card = card;
|
||||
}
|
||||
}
|
||||
}
|
||||
let newhand = TryInto::<[Part2Card; 5]>::try_into(
|
||||
p2cards
|
||||
.iter()
|
||||
.map(|c| {
|
||||
if *c == Part2Card::J {
|
||||
*highest_card
|
||||
} else {
|
||||
*c
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Part2Card>>(),
|
||||
)
|
||||
.unwrap_or_else(|v: Vec<Part2Card>| {
|
||||
panic!("Expected a Vec of length 5 but it was {}", v.len())
|
||||
});
|
||||
let hand_kind = get_hand_type(&newhand);
|
||||
let cards =
|
||||
TryInto::<[Part2Card; 5]>::try_into(p2cards).unwrap_or_else(|v: Vec<Part2Card>| {
|
||||
panic!("Expected a Vec of length 5 but it was {}", v.len())
|
||||
});
|
||||
Part2Hand {
|
||||
bid,
|
||||
cards,
|
||||
hand_kind,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Part2Hand>>();
|
||||
hands.sort();
|
||||
for (index, hand) in hands.iter().rev().enumerate() {
|
||||
sum += hand.bid * (index as u64 + 1);
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE_DATA: &'static str = r#"32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn sample_data() {
|
||||
let cards = parse(&SAMPLE_DATA);
|
||||
assert_eq!(part1(&cards), 6440);
|
||||
assert_eq!(part2(&cards), 5905);
|
||||
}
|
||||
}
|
|
@ -6,5 +6,6 @@ mod day03;
|
|||
mod day04;
|
||||
mod day05;
|
||||
mod day06;
|
||||
mod day07;
|
||||
|
||||
aoc_lib! { year = 2023 }
|
||||
|
|
Loading…
Reference in New Issue