1
0
Fork 0

day 7 solution

main
Andrew Coleman 2023-12-07 13:47:27 -05:00
parent da50cbd57b
commit df1e26462d
2 changed files with 361 additions and 0 deletions

360
2023/src/day07.rs Normal file
View File

@ -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);
}
}

View File

@ -6,5 +6,6 @@ mod day03;
mod day04;
mod day05;
mod day06;
mod day07;
aoc_lib! { year = 2023 }