Files
contests/advent_of_code/2023/7/src/main.rs

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