solution: day 7 clean

This commit is contained in:
Matej Janezic 2023-12-07 06:34:43 +01:00
parent 0000015082
commit 00000160a1
Signed by: janezicmatej
GPG Key ID: 4298E230ED37B2C0
2 changed files with 196 additions and 0 deletions

5
data/examples/07.txt Normal file
View File

@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483

191
src/bin/07.rs Normal file
View File

@ -0,0 +1,191 @@
use std::{cmp::Ordering, collections::HashMap, str::FromStr};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
enum Label {
Ace = 14,
King = 13,
Queen = 12,
Jack = 11,
Ten = 10,
Nine = 9,
Eight = 8,
Seven = 7,
Six = 6,
Five = 5,
Four = 4,
Three = 3,
Two = 2,
Joker = 1,
}
impl From<char> for Label {
fn from(value: char) -> Self {
use Label::*;
match value {
'A' => Ace,
'K' => King,
'Q' => Queen,
'J' => Jack,
'T' => Ten,
'9' => Nine,
'8' => Eight,
'7' => Seven,
'6' => Six,
'5' => Five,
'4' => Four,
'3' => Three,
'2' => Two,
'X' => Joker,
_ => unreachable!(),
}
}
}
#[derive(Debug)]
struct ParseHandError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Hand {
labels: [Label; 5],
}
impl FromStr for Hand {
type Err = ParseHandError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let labels = s
.chars()
.map(Label::from)
.collect::<Vec<_>>()
.try_into()
.map_err(|_| ParseHandError)?;
Ok(Hand { labels })
}
}
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 {
let sf = HandType::from(*self);
let so = HandType::from(*other);
sf.cmp(&so).then({
let mut c = Ordering::Equal;
for i in 0..5 {
c = self.labels[i].cmp(&other.labels[i]);
if c != Ordering::Equal {
break;
}
}
c
})
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum HandType {
Five = 6,
Four = 5,
FullHouse = 4,
Three = 3,
TwoPair = 2,
Pair = 1,
HighCard = 0,
}
impl From<Hand> for HandType {
fn from(value: Hand) -> Self {
let mut map = HashMap::new();
for c in value.labels {
*map.entry(c).or_insert(0) += 1;
}
let joker = map.remove(&Label::Joker).unwrap_or(0);
let is_n_k = |n| joker == n || map.values().filter(|&x| *x == n - joker).count() > 0;
if is_n_k(5) {
return Self::Five;
}
if is_n_k(4) {
return Self::Four;
}
// full house
if map.values().count() <= 2 {
return Self::FullHouse;
}
if is_n_k(3) {
return Self::Three;
}
// two pair
if map.values().count() <= 3 {
return Self::TwoPair;
}
if is_n_k(2) {
return Self::Pair;
}
Self::HighCard
}
}
pub fn part_one(input: &str) -> Option<u32> {
let mut v = input
.lines()
.filter_map(|l| l.split_once(' '))
.map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap()))
.collect::<Vec<_>>();
v.sort_by(|f, o| f.0.cmp(&o.0));
Some(
v.into_iter()
.enumerate()
.map(|(i, x)| (i as u32 + 1) * x.1)
.sum::<u32>(),
)
}
pub fn part_two(input: &str) -> Option<u32> {
let mut v = input
.replace('J', "X")
.lines()
.filter_map(|l| l.split_once(' '))
.map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap()))
.collect::<Vec<_>>();
v.sort_by(|f, o| f.0.cmp(&o.0));
Some(
v.into_iter()
.enumerate()
.map(|(i, x)| (i as u32 + 1) * x.1)
.sum::<u32>(),
)
}
aoc::solution!(7);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(
part_one(&aoc::template::read_file("examples", 7)),
Some(6440)
);
}
#[test]
fn test_part_two() {
assert_eq!(
part_two(&aoc::template::read_file("examples", 7)),
Some(5905)
);
}
}