aoc2023/day7/src/main.rs
2023-12-12 17:44:44 +01:00

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