2023 day7/rust: add solution
This commit is contained in:
parent
fbb486c683
commit
2d93f5f748
4 changed files with 286 additions and 7 deletions
11
2023/day7/rust/Cargo.toml
Normal file
11
2023/day7/rust/Cargo.toml
Normal 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
227
2023/day7/rust/src/main.rs
Normal 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
54
Cargo.lock
generated
|
@ -16,7 +16,7 @@ name = "aoc"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"strum",
|
||||
"strum 0.24.1",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -89,7 +89,7 @@ dependencies = [
|
|||
"clap",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
|
@ -111,7 +111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -252,6 +252,15 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
|
@ -452,7 +461,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|||
name = "rust_2021_1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -596,7 +605,7 @@ name = "rust_2022_14"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aoc",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -612,7 +621,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"aoc",
|
||||
"petgraph",
|
||||
"strum",
|
||||
"strum 0.24.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -651,6 +660,15 @@ dependencies = [
|
|||
"aoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_2023_07"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aoc",
|
||||
"itertools 0.12.0",
|
||||
"strum 0.25.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
|
@ -725,7 +743,16 @@ version = "0.24.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
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]]
|
||||
|
@ -741,6 +768,19 @@ dependencies = [
|
|||
"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]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
|
|
@ -32,4 +32,5 @@ members = [
|
|||
"2023/day4/rust",
|
||||
"2023/day5/rust",
|
||||
"2023/day6/rust",
|
||||
"2023/day7/rust",
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue