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 day04;
|
||||||
mod day05;
|
mod day05;
|
||||||
mod day06;
|
mod day06;
|
||||||
|
mod day07;
|
||||||
|
|
||||||
aoc_lib! { year = 2023 }
|
aoc_lib! { year = 2023 }
|
||||||
|
|
Loading…
Reference in New Issue