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"
|
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"
|
||||||
|
|
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue