264 lines
6.1 KiB
Rust
264 lines
6.1 KiB
Rust
use std::{cmp::Ordering, collections::HashMap, iter::zip};
|
|
|
|
use nom::{
|
|
character::complete::{alphanumeric1, multispace1, u64},
|
|
multi::separated_list1,
|
|
sequence::separated_pair,
|
|
IResult, Parser,
|
|
};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum HandType {
|
|
HighCard,
|
|
OnePair,
|
|
TwoPair,
|
|
ThreeKind,
|
|
FullHouse,
|
|
FourKind,
|
|
FiveKind,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum CardType {
|
|
Two,
|
|
Three,
|
|
Four,
|
|
Five,
|
|
Six,
|
|
Seven,
|
|
Eight,
|
|
Nine,
|
|
T,
|
|
Jack,
|
|
Queen,
|
|
King,
|
|
Ace,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum CardType2 {
|
|
Jack,
|
|
Two,
|
|
Three,
|
|
Four,
|
|
Five,
|
|
Six,
|
|
Seven,
|
|
Eight,
|
|
Nine,
|
|
T,
|
|
Queen,
|
|
King,
|
|
Ace,
|
|
}
|
|
|
|
fn parse_input(input: &str) -> IResult<&str, Vec<(String, u64)>> {
|
|
let (input, result) = separated_list1(
|
|
multispace1,
|
|
separated_pair(alphanumeric1::<&str, _>, multispace1, u64)
|
|
.map(|(hand, bid)| (hand.to_string(), bid)),
|
|
)(input)?;
|
|
|
|
Ok((input, result))
|
|
}
|
|
|
|
fn classify_hand(hand: &String) -> HandType {
|
|
use HandType::*;
|
|
|
|
let mut card_numbers: HashMap<char, i32> = HashMap::new();
|
|
for c in hand.to_owned().chars() {
|
|
card_numbers.insert(
|
|
c,
|
|
match card_numbers.get(&c) {
|
|
Some(n) => n + 1,
|
|
None => 1,
|
|
},
|
|
);
|
|
}
|
|
|
|
let mut card_numbers = card_numbers.values().collect::<Vec<_>>();
|
|
card_numbers.sort_unstable();
|
|
match card_numbers[..] {
|
|
[1, 1, 1, 1, 1] => HighCard,
|
|
[1, 1, 1, 2] => OnePair,
|
|
[1, 2, 2] => TwoPair,
|
|
[1, 1, 3] => ThreeKind,
|
|
[2, 3] => FullHouse,
|
|
[1, 4] => FourKind,
|
|
[5] => FiveKind,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn classify_hand2(hand: &String) -> HandType {
|
|
use HandType::*;
|
|
|
|
let mut card_numbers: HashMap<char, i32> = HashMap::new();
|
|
for c in hand.to_owned().chars() {
|
|
card_numbers.insert(
|
|
c,
|
|
match card_numbers.get(&c) {
|
|
Some(n) => n + 1,
|
|
None => 1,
|
|
},
|
|
);
|
|
}
|
|
|
|
if let Some(jokers) = card_numbers.remove(&'J') {
|
|
let mut card_numbers = card_numbers.values().collect::<Vec<_>>();
|
|
card_numbers.sort_unstable();
|
|
let highest = card_numbers.pop().unwrap_or(&0) + jokers;
|
|
card_numbers.push(&highest);
|
|
|
|
match card_numbers[..] {
|
|
[1, 1, 1, 2] => OnePair,
|
|
[1, 1, 3] => ThreeKind,
|
|
[2, 3] => FullHouse,
|
|
[1, 4] => FourKind,
|
|
[5] => FiveKind,
|
|
_ => unreachable!(),
|
|
}
|
|
} else {
|
|
let mut card_numbers = card_numbers.values().collect::<Vec<_>>();
|
|
card_numbers.sort_unstable();
|
|
match card_numbers[..] {
|
|
[1, 1, 1, 1, 1] => HighCard,
|
|
[1, 1, 1, 2] => OnePair,
|
|
[1, 2, 2] => TwoPair,
|
|
[1, 1, 3] => ThreeKind,
|
|
[2, 3] => FullHouse,
|
|
[1, 4] => FourKind,
|
|
[5] => FiveKind,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn classify_card(card: char) -> CardType {
|
|
use CardType::*;
|
|
|
|
match card {
|
|
'A' => Ace,
|
|
'K' => King,
|
|
'Q' => Queen,
|
|
'J' => Jack,
|
|
'T' => T,
|
|
'9' => Nine,
|
|
'8' => Eight,
|
|
'7' => Seven,
|
|
'6' => Six,
|
|
'5' => Five,
|
|
'4' => Four,
|
|
'3' => Three,
|
|
'2' => Two,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn classify_card2(card: char) -> CardType2 {
|
|
use CardType2::*;
|
|
|
|
match card {
|
|
'A' => Ace,
|
|
'K' => King,
|
|
'Q' => Queen,
|
|
'T' => T,
|
|
'9' => Nine,
|
|
'8' => Eight,
|
|
'7' => Seven,
|
|
'6' => Six,
|
|
'5' => Five,
|
|
'4' => Four,
|
|
'3' => Three,
|
|
'2' => Two,
|
|
'J' => Jack,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn compare_hands(hand_1: &String, hand_2: &String) -> Ordering {
|
|
let (hand_type_1, hand_type_2) = (classify_hand(hand_1), classify_hand(hand_2));
|
|
if hand_type_1 == hand_type_2 {
|
|
for (card_1, card_2) in zip(hand_1.to_owned().chars(), hand_2.to_owned().chars()) {
|
|
if card_1 != card_2 {
|
|
return classify_card(card_1).cmp(&classify_card(card_2));
|
|
}
|
|
}
|
|
Ordering::Equal
|
|
} else {
|
|
hand_type_1.cmp(&hand_type_2)
|
|
}
|
|
}
|
|
|
|
fn compare_hands2(hand_1: &String, hand_2: &String) -> Ordering {
|
|
let (hand_type_1, hand_type_2) = (classify_hand2(hand_1), classify_hand2(hand_2));
|
|
if hand_type_1 == hand_type_2 {
|
|
for (card_1, card_2) in zip(hand_1.to_owned().chars(), hand_2.to_owned().chars()) {
|
|
if card_1 != card_2 {
|
|
return classify_card2(card_1).cmp(&classify_card2(card_2));
|
|
}
|
|
}
|
|
Ordering::Equal
|
|
} else {
|
|
hand_type_1.cmp(&hand_type_2)
|
|
}
|
|
}
|
|
|
|
fn solve_1(plays: &Vec<(String, u64)>) -> u64 {
|
|
let mut plays = plays.clone();
|
|
|
|
plays.sort_unstable_by(|(hand_1, _bid1), (hand_2, _bid2)| compare_hands(hand_1, hand_2));
|
|
|
|
plays
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(rank, (_hand, bid))| (rank as u64 + 1) * bid)
|
|
.sum()
|
|
}
|
|
|
|
fn solve_2(plays: &Vec<(String, u64)>) -> u64 {
|
|
let mut plays = plays.clone();
|
|
|
|
plays.sort_unstable_by(|(hand_1, _bid1), (hand_2, _bid2)| compare_hands2(hand_1, hand_2));
|
|
|
|
plays
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(rank, (_hand, bid))| (rank as u64 + 1) * bid)
|
|
.sum()
|
|
}
|
|
|
|
fn main() {
|
|
println!("Hello, this is Patrick!");
|
|
|
|
let input_text = include_str!("../input.txt");
|
|
|
|
let (_, plays) = parse_input(input_text).unwrap();
|
|
|
|
println!("The total winnings are {}", solve_1(&plays));
|
|
println!("The new total winnings are {}", solve_2(&plays));
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_1() {
|
|
let input_text = include_str!("../test_input.txt");
|
|
|
|
let (_, plays) = parse_input(input_text).unwrap();
|
|
|
|
assert_eq!(solve_1(&plays), 6440);
|
|
}
|
|
|
|
#[test]
|
|
fn test_2() {
|
|
let input_text = include_str!("../test_input.txt");
|
|
|
|
let (_, plays) = parse_input(input_text).unwrap();
|
|
|
|
assert_eq!(solve_2(&plays), 5905);
|
|
}
|
|
}
|