2023 day7/rust: add solution

This commit is contained in:
Xiretza 2023-12-07 17:37:24 +00:00
parent fbb486c683
commit 2d93f5f748
4 changed files with 286 additions and 7 deletions

11
2023/day7/rust/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "rust_2023_07"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aoc = { path = "../../../common/rust" }
itertools = "0.12.0"
strum = { version = "0.25.0", features = ["derive"] }

227
2023/day7/rust/src/main.rs Normal file
View file

@ -0,0 +1,227 @@
#![warn(clippy::pedantic)]
use std::{cmp::Ordering, io::stdin};
use itertools::Itertools;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumIter)]
enum Card {
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
T,
J,
Q,
K,
A,
}
impl Card {
fn parse(c: char) -> Self {
match c {
'2' => Card::N2,
'3' => Card::N3,
'4' => Card::N4,
'5' => Card::N5,
'6' => Card::N6,
'7' => Card::N7,
'8' => Card::N8,
'9' => Card::N9,
'T' => Card::T,
'J' => Card::J,
'Q' => Card::Q,
'K' => Card::K,
'A' => Card::A,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Type {
HighCard,
OnePair,
TwoPairs,
ThreeSame,
FullHouse,
FourSame,
FiveSame,
}
impl Type {
fn classify_inner(mut value: [Card; 5]) -> Option<Self> {
value.sort();
let groups: Vec<_> = value
.into_iter()
.group_by(|c| *c)
.into_iter()
.map(|(_card, group)| group.count())
.collect();
if groups.contains(&5) {
Some(Type::FiveSame)
} else if groups.contains(&4) {
Some(Type::FourSame)
} else if groups.contains(&3) {
if groups.contains(&2) {
Some(Type::FullHouse)
} else {
Some(Type::ThreeSame)
}
} else if groups.contains(&2) {
if groups.iter().filter(|&&n| n == 2).count() == 2 {
Some(Type::TwoPairs)
} else {
Some(Type::OnePair)
}
} else if groups.iter().all(|&n| n == 1) {
Some(Type::HighCard)
} else {
None
}
}
fn classify(value: [Card; 5], mode: Mode) -> Option<Self> {
match mode {
Mode::Jokers => {
if !value.contains(&Card::J) {
return Self::classify_inner(value);
}
let jokers: Vec<_> = value.iter().positions(|&c| c == Card::J).collect();
let all_other_cards = Card::iter().filter(|&c| c != Card::J);
std::iter::repeat(all_other_cards)
.take(jokers.len())
.multi_cartesian_product()
.map(|replacements| {
let mut value = value;
for (&pos, replacement) in jokers.iter().zip(replacements) {
value[pos] = replacement;
}
value
})
.map(Type::classify_inner)
.max()
.unwrap()
}
Mode::NoJokers => Self::classify_inner(value),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Mode {
Jokers,
NoJokers,
}
#[derive(Debug, Clone, Copy)]
struct Hand {
typ: Option<Type>,
cards: [Card; 5],
mode: Mode,
bid: usize,
}
impl Hand {
pub fn parse(s: &str, mode: Mode) -> Self {
let (cards, bid) = s.split_once(' ').unwrap();
let bid = bid.parse().unwrap();
let cards: Vec<_> = cards.chars().map(Card::parse).collect();
let cards = *<&[Card; 5]>::try_from(cards.as_slice()).unwrap();
let typ = Type::classify(cards, mode);
Self {
typ,
cards,
mode,
bid,
}
}
pub fn set_mode(&mut self, mode: Mode) {
if mode == self.mode {
return;
}
self.typ = Type::classify(self.cards, mode);
self.mode = mode;
}
}
impl PartialEq for Hand {
fn eq(&self, other: &Self) -> bool {
assert!(
self.mode == other.mode,
"can't compare hands with different modes"
);
self.cards == other.cards
}
}
impl Eq for Hand {}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> Ordering {
assert!(
self.mode == other.mode,
"can't compare hands with different modes"
);
self.typ.cmp(&other.typ).then_with(|| {
self.cards
.iter()
.zip(&other.cards)
.map(|(s, o)| {
if let Mode::Jokers = self.mode {
match (s, o) {
(Card::J, Card::J) => {}
(Card::J, _) => return Ordering::Less,
(_, Card::J) => return Ordering::Greater,
_ => {}
}
}
s.cmp(o)
})
.reduce(Ordering::then)
.unwrap()
})
}
}
impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn get_winnings(hands: &mut [Hand], mode: Mode) -> usize {
for hand in hands.iter_mut() {
hand.set_mode(mode);
}
hands.sort();
hands
.iter()
.enumerate()
.map(|(rank, hand)| hand.bid * (rank + 1))
.sum()
}
fn main() {
let mut hands: Vec<_> = stdin()
.lines()
.map(Result::unwrap)
.map(|s| Hand::parse(&s, Mode::NoJokers))
.collect();
println!("{}", get_winnings(&mut hands, Mode::NoJokers));
println!("{}", get_winnings(&mut hands, Mode::Jokers));
}

54
Cargo.lock generated
View file

@ -16,7 +16,7 @@ name = "aoc"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"strum", "strum 0.24.1",
"thiserror", "thiserror",
] ]
@ -89,7 +89,7 @@ dependencies = [
"clap", "clap",
"criterion-plot", "criterion-plot",
"csv", "csv",
"itertools", "itertools 0.10.5",
"lazy_static", "lazy_static",
"num-traits", "num-traits",
"oorandom", "oorandom",
@ -111,7 +111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
dependencies = [ dependencies = [
"cast", "cast",
"itertools", "itertools 0.10.5",
] ]
[[package]] [[package]]
@ -252,6 +252,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.9" version = "1.0.9"
@ -452,7 +461,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
name = "rust_2021_1" name = "rust_2021_1"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"itertools", "itertools 0.10.5",
] ]
[[package]] [[package]]
@ -596,7 +605,7 @@ name = "rust_2022_14"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"aoc", "aoc",
"itertools", "itertools 0.10.5",
] ]
[[package]] [[package]]
@ -612,7 +621,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"aoc", "aoc",
"petgraph", "petgraph",
"strum", "strum 0.24.1",
] ]
[[package]] [[package]]
@ -651,6 +660,15 @@ dependencies = [
"aoc", "aoc",
] ]
[[package]]
name = "rust_2023_07"
version = "0.1.0"
dependencies = [
"aoc",
"itertools 0.12.0",
"strum 0.25.0",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.14"
@ -725,7 +743,16 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [ dependencies = [
"strum_macros", "strum_macros 0.24.3",
]
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros 0.25.3",
] ]
[[package]] [[package]]
@ -741,6 +768,19 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.39",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"

View file

@ -32,4 +32,5 @@ members = [
"2023/day4/rust", "2023/day4/rust",
"2023/day5/rust", "2023/day5/rust",
"2023/day6/rust", "2023/day6/rust",
"2023/day7/rust",
] ]