133 lines
3.0 KiB
Rust
133 lines
3.0 KiB
Rust
use std::{fmt::Display, io::stdin, str::FromStr};
|
|
use anyhow::{anyhow, Result, Error};
|
|
|
|
#[derive(Debug,Clone,Copy,Eq,PartialEq)]
|
|
struct Card(char);
|
|
|
|
#[derive(Debug,Clone,Copy,Eq,PartialEq)]
|
|
struct Hand([Card; 5]);
|
|
|
|
#[derive(Debug,Clone,Copy,Eq,PartialEq,Ord,PartialOrd)]
|
|
enum Suit {
|
|
HighCard,
|
|
OnePair,
|
|
TwoPairs,
|
|
Three,
|
|
Full,
|
|
Four,
|
|
Five,
|
|
}
|
|
|
|
impl Card {
|
|
|
|
fn new(c: char) -> Result<Self> {
|
|
match c {
|
|
'A'|'K'|'Q'|'J'|'T'|'2'..='9' => Ok(Self(c)),
|
|
_ => Err(anyhow!("invalid card {c:?}"))
|
|
}
|
|
}
|
|
|
|
fn value(self) -> usize {
|
|
match self.0 {
|
|
'A' => 14,
|
|
'K' => 13,
|
|
'Q' => 12,
|
|
'J' => 11,
|
|
'T' => 10,
|
|
'2'..='9' => char::to_digit(self.0, 10).unwrap() as usize,
|
|
_ => panic!("invalid card"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Ord for Card {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.value().cmp(&other.value())
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Card {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.0.cmp(&other.0))
|
|
}
|
|
}
|
|
|
|
impl Display for Card {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Hand {
|
|
fn suit(&self) -> Suit {
|
|
let mut cache = [0; 16];
|
|
let mut counts = [0; 5];
|
|
for v in self.0 {
|
|
cache[v.value()] += 1;
|
|
}
|
|
for c in cache {
|
|
if c > 0 { counts[c-1] += 1 }
|
|
}
|
|
|
|
match counts {
|
|
[_,_,_,_,1] => Suit::Five,
|
|
[_,_,_,1,_] => Suit::Four,
|
|
[_,1,1,_,_] => Suit::Full,
|
|
[_,_,1,_,_] => Suit::Three,
|
|
[_,2,_,_,_] => Suit::TwoPairs,
|
|
[_,1,_,_,_] => Suit::OnePair,
|
|
_ => Suit::HighCard,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Ord for Hand {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.suit().cmp(&other.suit())
|
|
.then_with(|| self.0.cmp(&other.0))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Hand {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.cmp(&other))
|
|
}
|
|
}
|
|
|
|
impl FromStr for Hand {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
if s.len() != 5 { return Err(anyhow!("Invalid hand {s}")) }
|
|
let c = s.chars().map(Card::new)
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
Ok(Self([c[0], c[1], c[2], c[3], c[4]]))
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
let mut data: Vec<(Hand, usize)> = stdin()
|
|
.lines()
|
|
.map(|l| -> Result<(Hand,usize)> {
|
|
let l = l?;
|
|
let (hand, bet) = l.split_once(' ')
|
|
.ok_or_else(|| anyhow!("bad line {l:?}"))?;
|
|
Ok((hand.parse()?, bet.parse()?))
|
|
})
|
|
.collect::<Result<_>>()?;
|
|
|
|
data.sort_by(|a,b| a.0.cmp(&b.0));
|
|
|
|
let total: usize = data.iter()
|
|
.enumerate()
|
|
.map(|(rank, (_,bid))| bid*(rank+1) )
|
|
.sum();
|
|
|
|
println!("Part 1: total winnings {total}");
|
|
|
|
Ok(())
|
|
|
|
}
|