mirror of
https://github.com/Noratrieb/advent-of-code.git
synced 2026-01-16 18:45:02 +01:00
things
This commit is contained in:
parent
f88364a895
commit
3120797873
3 changed files with 286 additions and 45 deletions
|
|
@ -1,3 +1,6 @@
|
||||||
|
mod sort_key;
|
||||||
|
mod unstable_sort;
|
||||||
|
|
||||||
use std::cmp::{self, Ordering};
|
use std::cmp::{self, Ordering};
|
||||||
|
|
||||||
use helper::{Day, IteratorExt, Variants};
|
use helper::{Day, IteratorExt, Variants};
|
||||||
|
|
@ -11,10 +14,14 @@ struct Day07;
|
||||||
helper::define_variants! {
|
helper::define_variants! {
|
||||||
day => crate::Day07;
|
day => crate::Day07;
|
||||||
part1 {
|
part1 {
|
||||||
basic => crate::part1;
|
basic => crate::part1, sample_count=10000;
|
||||||
|
unstable_sort => crate::unstable_sort::part1, sample_count=10000;
|
||||||
|
card_soa => crate::sort_key::part1, sample_count=10000;
|
||||||
}
|
}
|
||||||
part2 {
|
part2 {
|
||||||
basic => crate::part2;
|
basic => crate::part2, sample_count=10000;
|
||||||
|
unstable_sort => crate::unstable_sort::part2, sample_count=10000;
|
||||||
|
card_soa => crate::sort_key::part2, sample_count=10000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +35,7 @@ impl Day for Day07 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum HandType {
|
enum HandType {
|
||||||
FiveSame = 0,
|
FiveSame = 0,
|
||||||
FourSame,
|
FourSame,
|
||||||
|
|
@ -46,48 +53,47 @@ struct Hand {
|
||||||
bid: u64,
|
bid: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HandType {
|
fn hand_type_of(hand: [u8; 5], has_jokers: bool) -> HandType {
|
||||||
fn of(hand: [u8; 5], has_jokers: bool) -> Self {
|
let mut card_type = [0; 5];
|
||||||
let mut map: [Option<(u8, u8)>; 5] = [None; 5];
|
let mut counts = [0; 5];
|
||||||
let mut jokers = 0;
|
let mut jokers = 0;
|
||||||
|
|
||||||
for card in hand {
|
for card in hand {
|
||||||
if card == b'J' && has_jokers {
|
if card == b'J' && has_jokers {
|
||||||
jokers += 1;
|
jokers += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if let Some(existing) = map.iter_mut().find(|c| c.is_some_and(|c| c.0 == card)) {
|
|
||||||
existing.as_mut().unwrap().1 += 1;
|
|
||||||
} else {
|
|
||||||
let idx = map.iter().position(|c| c.is_none()).unwrap();
|
|
||||||
map[idx] = Some((card, 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
map.sort_by_key(|c| cmp::Reverse(c.map(|c| c.1)));
|
if let Some(existing) = card_type.into_iter().position(|c| c == card) {
|
||||||
|
counts[existing] += 1;
|
||||||
let map = map.map(|c| c.map_or(0, |c| c.1));
|
|
||||||
if map[0] + jokers == 5 {
|
|
||||||
Self::FiveSame
|
|
||||||
} else if map[0] + jokers == 4 {
|
|
||||||
Self::FourSame
|
|
||||||
// If there are only cards of two types + jokers, then we can form a full house
|
|
||||||
// no matter how they're set up.
|
|
||||||
} else if map[0] + map[1] + jokers == 5 {
|
|
||||||
Self::FullHouse
|
|
||||||
} else if map[0] + jokers == 3 {
|
|
||||||
Self::ThreeSame
|
|
||||||
// We need four cards for a two pair. Given that the previous constellations
|
|
||||||
// are not possible, we are able to build a two pair.
|
|
||||||
} else if map[0] + map[1] + jokers == 4 {
|
|
||||||
Self::TwoPair
|
|
||||||
// Fun fact: This == is fine and needn't be >=, because if map[0]+jokers==3,
|
|
||||||
// then we can build two pairs.
|
|
||||||
} else if map[0] + jokers == 2 {
|
|
||||||
Self::OnePair
|
|
||||||
} else {
|
} else {
|
||||||
Self::HighCard
|
let idx = card_type.into_iter().position(|c| c == 0).unwrap();
|
||||||
|
card_type[idx] = card;
|
||||||
|
counts[idx] = 1_u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
counts.sort_by_key(|&c| cmp::Reverse(c));
|
||||||
|
|
||||||
|
if counts[0] + jokers == 5 {
|
||||||
|
HandType::FiveSame
|
||||||
|
} else if counts[0] + jokers == 4 {
|
||||||
|
HandType::FourSame
|
||||||
|
// If there are only cards of two types + jokers, then we can form a full house
|
||||||
|
// no matter how they're set up.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 5 {
|
||||||
|
HandType::FullHouse
|
||||||
|
} else if counts[0] + jokers == 3 {
|
||||||
|
HandType::ThreeSame
|
||||||
|
// We need four cards for a two pair. Given that the previous constellations
|
||||||
|
// are not possible, we are able to build a two pair.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 4 {
|
||||||
|
HandType::TwoPair
|
||||||
|
// Fun fact: This == is fine and needn't be >=, because if map[0]+jokers==3,
|
||||||
|
// then we can build two pairs.
|
||||||
|
} else if counts[0] + jokers == 2 {
|
||||||
|
HandType::OnePair
|
||||||
|
} else {
|
||||||
|
HandType::HighCard
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -96,9 +102,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hand_type() {
|
fn hand_type() {
|
||||||
assert_eq!(HandType::of(*b"32T3K", false), HandType::OnePair);
|
assert_eq!(super::hand_type_of(*b"32T3K", false), HandType::OnePair);
|
||||||
assert_eq!(HandType::of(*b"K6KK6", false), HandType::FullHouse);
|
assert_eq!(super::hand_type_of(*b"K6KK6", false), HandType::FullHouse);
|
||||||
assert_eq!(HandType::of(*b"KTJJT", true), HandType::FourSame);
|
assert_eq!(super::hand_type_of(*b"KTJJT", true), HandType::FourSame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +117,7 @@ fn parse(input: &str, has_jokers: bool) -> Vec<Hand> {
|
||||||
let bid = parts.next().unwrap().parse().unwrap();
|
let bid = parts.next().unwrap().parse().unwrap();
|
||||||
|
|
||||||
let values = cards.bytes().collect_array::<5>().unwrap();
|
let values = cards.bytes().collect_array::<5>().unwrap();
|
||||||
let hand_type = HandType::of(values, has_jokers);
|
let hand_type = hand_type_of(values, has_jokers);
|
||||||
|
|
||||||
Hand {
|
Hand {
|
||||||
values,
|
values,
|
||||||
|
|
@ -174,7 +180,7 @@ helper::tests! {
|
||||||
}
|
}
|
||||||
part2 {
|
part2 {
|
||||||
small => 5905;
|
small => 5905;
|
||||||
default => 0;
|
default => 248781813;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
helper::benchmarks! {}
|
helper::benchmarks! {}
|
||||||
|
|
|
||||||
111
2023/day07/src/sort_key.rs
Normal file
111
2023/day07/src/sort_key.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
use std::cmp::{self};
|
||||||
|
|
||||||
|
use helper::IteratorExt;
|
||||||
|
|
||||||
|
use crate::{Hand, HandType};
|
||||||
|
|
||||||
|
fn hand_type_of(hand: [u8; 5], has_jokers: bool) -> HandType {
|
||||||
|
let mut card_type = [0; 5];
|
||||||
|
let mut counts = [0; 5];
|
||||||
|
let mut jokers = 0;
|
||||||
|
|
||||||
|
for card in hand {
|
||||||
|
if card == b'J' && has_jokers {
|
||||||
|
jokers += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(existing) = card_type.into_iter().position(|c| c == card) {
|
||||||
|
counts[existing] += 1;
|
||||||
|
} else {
|
||||||
|
let idx = card_type.into_iter().position(|c| c == 0).unwrap();
|
||||||
|
card_type[idx] = card;
|
||||||
|
counts[idx] = 1_u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counts.sort_unstable_by_key(|&c| cmp::Reverse(c));
|
||||||
|
|
||||||
|
if counts[0] + jokers == 5 {
|
||||||
|
HandType::FiveSame
|
||||||
|
} else if counts[0] + jokers == 4 {
|
||||||
|
HandType::FourSame
|
||||||
|
// If there are only cards of two types + jokers, then we can form a full house
|
||||||
|
// no matter how they're set up.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 5 {
|
||||||
|
HandType::FullHouse
|
||||||
|
} else if counts[0] + jokers == 3 {
|
||||||
|
HandType::ThreeSame
|
||||||
|
// We need four cards for a two pair. Given that the previous constellations
|
||||||
|
// are not possible, we are able to build a two pair.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 4 {
|
||||||
|
HandType::TwoPair
|
||||||
|
// Fun fact: This == is fine and needn't be >=, because if map[0]+jokers==3,
|
||||||
|
// then we can build two pairs.
|
||||||
|
} else if counts[0] + jokers == 2 {
|
||||||
|
HandType::OnePair
|
||||||
|
} else {
|
||||||
|
HandType::HighCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::HandType;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hand_type() {
|
||||||
|
assert_eq!(super::hand_type_of(*b"32T3K", false), HandType::OnePair);
|
||||||
|
assert_eq!(super::hand_type_of(*b"K6KK6", false), HandType::FullHouse);
|
||||||
|
assert_eq!(super::hand_type_of(*b"KTJJT", true), HandType::FourSame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str, has_jokers: bool) -> Vec<Hand> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|line| {
|
||||||
|
let mut parts = line.split_ascii_whitespace();
|
||||||
|
let cards = parts.next().unwrap();
|
||||||
|
let bid = parts.next().unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
let values = cards.bytes().collect_array::<5>().unwrap();
|
||||||
|
let hand_type = hand_type_of(values, has_jokers);
|
||||||
|
|
||||||
|
Hand {
|
||||||
|
values,
|
||||||
|
hand_type,
|
||||||
|
bid,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_hands(hands: &mut [Hand], has_jokers: bool) -> u64 {
|
||||||
|
// Worst hand first, best hand last.
|
||||||
|
hands.sort_unstable_by_key(|hand| {
|
||||||
|
let mk_compare = |v| match v {
|
||||||
|
b'A' => b'Z',
|
||||||
|
b'K' => b'Y',
|
||||||
|
b'T' => b'I',
|
||||||
|
b'J' if has_jokers => b'0',
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
|
||||||
|
cmp::Reverse((hand.hand_type, cmp::Reverse(hand.values.map(mk_compare))))
|
||||||
|
});
|
||||||
|
|
||||||
|
hands
|
||||||
|
.iter()
|
||||||
|
.zip(1_u64..)
|
||||||
|
.map(|(hand, i)| hand.bid * i)
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: &str) -> u64 {
|
||||||
|
let mut hands = parse(input, false);
|
||||||
|
evaluate_hands(&mut hands, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: &str) -> u64 {
|
||||||
|
let mut hands = parse(input, true);
|
||||||
|
evaluate_hands(&mut hands, true)
|
||||||
|
}
|
||||||
124
2023/day07/src/unstable_sort.rs
Normal file
124
2023/day07/src/unstable_sort.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
use std::cmp::{self, Ordering};
|
||||||
|
|
||||||
|
use helper::IteratorExt;
|
||||||
|
|
||||||
|
use crate::{Hand, HandType};
|
||||||
|
|
||||||
|
fn hand_type_of(hand: [u8; 5], has_jokers: bool) -> HandType {
|
||||||
|
let mut card_type = [0; 5];
|
||||||
|
let mut counts = [0; 5];
|
||||||
|
let mut jokers = 0;
|
||||||
|
|
||||||
|
for card in hand {
|
||||||
|
if card == b'J' && has_jokers {
|
||||||
|
jokers += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(existing) = card_type.into_iter().position(|c| c == card) {
|
||||||
|
counts[existing] += 1;
|
||||||
|
} else {
|
||||||
|
let idx = card_type.into_iter().position(|c| c == 0).unwrap();
|
||||||
|
card_type[idx] = card;
|
||||||
|
counts[idx] = 1_u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counts.sort_unstable_by_key(|&c| cmp::Reverse(c));
|
||||||
|
|
||||||
|
if counts[0] + jokers == 5 {
|
||||||
|
HandType::FiveSame
|
||||||
|
} else if counts[0] + jokers == 4 {
|
||||||
|
HandType::FourSame
|
||||||
|
// If there are only cards of two types + jokers, then we can form a full house
|
||||||
|
// no matter how they're set up.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 5 {
|
||||||
|
HandType::FullHouse
|
||||||
|
} else if counts[0] + jokers == 3 {
|
||||||
|
HandType::ThreeSame
|
||||||
|
// We need four cards for a two pair. Given that the previous constellations
|
||||||
|
// are not possible, we are able to build a two pair.
|
||||||
|
} else if counts[0] + counts[1] + jokers == 4 {
|
||||||
|
HandType::TwoPair
|
||||||
|
// Fun fact: This == is fine and needn't be >=, because if map[0]+jokers==3,
|
||||||
|
// then we can build two pairs.
|
||||||
|
} else if counts[0] + jokers == 2 {
|
||||||
|
HandType::OnePair
|
||||||
|
} else {
|
||||||
|
HandType::HighCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::HandType;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hand_type() {
|
||||||
|
assert_eq!(super::hand_type_of(*b"32T3K", false), HandType::OnePair);
|
||||||
|
assert_eq!(super::hand_type_of(*b"K6KK6", false), HandType::FullHouse);
|
||||||
|
assert_eq!(super::hand_type_of(*b"KTJJT", true), HandType::FourSame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str, has_jokers: bool) -> Vec<Hand> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|line| {
|
||||||
|
let mut parts = line.split_ascii_whitespace();
|
||||||
|
let cards = parts.next().unwrap();
|
||||||
|
let bid = parts.next().unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
let values = cards.bytes().collect_array::<5>().unwrap();
|
||||||
|
let hand_type = hand_type_of(values, has_jokers);
|
||||||
|
|
||||||
|
Hand {
|
||||||
|
values,
|
||||||
|
hand_type,
|
||||||
|
bid,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_hands(hands: &mut [Hand], has_jokers: bool) -> u64 {
|
||||||
|
// Worst hand first, best hand last.
|
||||||
|
hands.sort_unstable_by(|a, b| {
|
||||||
|
let mk_compare = |v| match v {
|
||||||
|
b'A' => b'Z',
|
||||||
|
b'K' => b'Y',
|
||||||
|
b'T' => b'I',
|
||||||
|
b'J' if has_jokers => b'0',
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
|
||||||
|
let types = a.hand_type.cmp(&b.hand_type);
|
||||||
|
if types != Ordering::Equal {
|
||||||
|
return types.reverse();
|
||||||
|
}
|
||||||
|
for (a, b) in std::iter::zip(a.values, b.values) {
|
||||||
|
if a == b {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let a = mk_compare(a);
|
||||||
|
let b = mk_compare(b);
|
||||||
|
|
||||||
|
return a.cmp(&b);
|
||||||
|
}
|
||||||
|
Ordering::Equal
|
||||||
|
});
|
||||||
|
|
||||||
|
hands
|
||||||
|
.iter()
|
||||||
|
.zip(1_u64..)
|
||||||
|
.map(|(hand, i)| hand.bid * i)
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(input: &str) -> u64 {
|
||||||
|
let mut hands = parse(input, false);
|
||||||
|
evaluate_hands(&mut hands, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: &str) -> u64 {
|
||||||
|
let mut hands = parse(input, true);
|
||||||
|
evaluate_hands(&mut hands, true)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue