Implemented faulty first part of day 7

This commit is contained in:
BoolPurist 2024-08-23 08:27:22 +02:00
parent bb63ca7f74
commit d117c34786
7 changed files with 341 additions and 2 deletions

View file

@ -10,7 +10,7 @@ pub fn create_solutions() -> Vec<Vec<fn(&str) -> String>> {
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![day5::solve_task_1, day5::solve_task_2], vec![day5::solve_task_1, day5::solve_task_2],
vec![day6::solve_task_1, day6::solve_task_2], vec![day6::solve_task_1, day6::solve_task_2],
vec![not_implemented_yet, not_implemented_yet], vec![day7::solve_task_1, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],

View file

@ -1 +1,49 @@
use categorized_hand::CategorizedHand;
mod categorized_hand;
mod dealt_hand; mod dealt_hand;
mod hand_kind;
mod parsing;
/// TODO: wrong answer for 253639210 is too low for my puzzle input
pub fn solve_task_1(input: &str) -> String {
let parsed = parsing::parse_input(input);
let sorted_and_categorized = {
let mut categorized: Vec<_> = parsed.into_iter().map(CategorizedHand::from).collect();
categorized.sort();
categorized
};
let total_score = calc_bid_and_ranks(&sorted_and_categorized);
total_score.to_string()
}
fn calc_bid_and_ranks(sorted: &[CategorizedHand]) -> usize {
sorted
.into_iter()
.enumerate()
.map(|(index, hand)| {
let rank = index + 1;
hand.score() * rank
})
.sum()
}
#[cfg(test)]
const TEST_INPUT: &str = "32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
";
#[cfg(test)]
mod testing {
use super::TEST_INPUT;
#[test]
fn solve_example_according_to_task_1() {
let actual = super::solve_task_1(TEST_INPUT);
let expected = "6440";
assert_eq!(expected, actual);
}
}

View file

@ -0,0 +1,194 @@
use std::{
cmp::{Ordering, Reverse},
collections::HashMap,
sync::LazyLock,
usize,
};
use super::{dealt_hand::DealtHand, hand_kind::HandKind};
#[derive(Debug, PartialEq, Eq)]
pub struct CategorizedHand {
kind: HandKind,
rest: DealtHand,
}
impl CategorizedHand {
pub fn rest(&self) -> &DealtHand {
&self.rest
}
pub fn score(&self) -> usize {
self.rest().score()
}
}
impl Ord for CategorizedHand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.kind.cmp(&other.kind) {
std::cmp::Ordering::Equal => compare_along_str(self.rest().hand(), other.rest().hand()),
not_equal_ordering => not_equal_ordering,
}
}
}
impl PartialOrd for CategorizedHand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
static LABEL_ORDERING: LazyLock<HashMap<char, usize>> = LazyLock::new(|| {
let numbers = (0..9)
.into_iter()
.map(|number| std::char::from_digit(number, 10).unwrap());
let letters = ['T', 'J', 'Q', 'K', 'A'];
numbers
.chain(letters.into_iter())
.into_iter()
.enumerate()
.map(|(index, character)| (character, index))
.collect()
});
//A hand consists of five cards labeled one of A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2.
//The relative strength of each card follows this order, where A is the highest and 2 is the lowest.
fn compare_along_str(left: &str, right: &str) -> Ordering {
for (left_char, right_char) in left.chars().zip(right.chars()) {
let left_rank_value = LABEL_ORDERING.get(&left_char);
let right_rank_value = LABEL_ORDERING.get(&right_char);
let current_ordering = left_rank_value.cmp(&right_rank_value);
if current_ordering != Ordering::Equal {
return current_ordering;
}
}
left.len().cmp(&right.len())
}
impl From<DealtHand> for CategorizedHand {
fn from(value: DealtHand) -> Self {
let mut count_of_same: HashMap<char, usize> = HashMap::with_capacity(10);
for next_char in value.hand().chars() {
count_of_same
.entry(next_char)
.and_modify(|count| *count = *count + 1)
.or_insert(1);
}
let values = {
let mut to_sort: Vec<usize> =
count_of_same.into_iter().map(|(_, count)| count).collect();
to_sort.sort_by_key(|&count| Reverse(count));
to_sort
};
let max = values.first().unwrap();
let kind = match max {
5 => HandKind::Five,
4 => HandKind::Four,
_ => {
let second = values.get(1).unwrap();
match (max, second) {
(3, 2) => HandKind::FullHouse,
(3, _) => HandKind::Three,
(2, 2) => HandKind::TwoPair,
(2, 1) => HandKind::OnePair,
_ => HandKind::HighCard,
}
}
};
CategorizedHand { kind, rest: value }
}
}
#[cfg(test)]
mod testing {
use std::cmp::Ordering;
use crate::solutions::day7::{
categorized_hand::CategorizedHand, dealt_hand::DealtHand, hand_kind::HandKind,
};
#[test]
fn from_dealt_hand_to_categorized_hand() {
fn assert_case(given: DealtHand, expected: HandKind) {
let expected_hand = CategorizedHand {
kind: expected,
rest: given.clone(),
};
let actual = given.into();
assert_eq!(expected_hand, actual);
}
assert_case(DealtHand::new("AAAAA", 20), HandKind::Five);
assert_case(DealtHand::new("AA8AA", 20), HandKind::Four);
assert_case(DealtHand::new("23332", 20), HandKind::FullHouse);
assert_case(DealtHand::new("TTT98", 20), HandKind::Three);
assert_case(DealtHand::new("23456", 20), HandKind::HighCard);
assert_case(DealtHand::new("23432", 20), HandKind::TwoPair);
assert_case(DealtHand::new("A23A4", 20), HandKind::OnePair);
}
#[test]
fn correct_cmp() {
fn assert_case(left: DealtHand, right: DealtHand, expected: Ordering) {
let (left, right): (CategorizedHand, CategorizedHand) = (left.into(), right.into());
let actual = left.cmp(&right);
assert_eq!(expected, actual, "Left: {:?}\nRight: {:?}", left, right);
}
assert_case(
DealtHand::new("AAAAA", 20),
DealtHand::new("BBBBA", 30),
Ordering::Greater,
);
assert_case(
DealtHand::new("12345", 20),
DealtHand::new("BBBBA", 30),
Ordering::Less,
);
assert_case(
DealtHand::new("12345", 20),
DealtHand::new("28495", 30),
Ordering::Less,
);
assert_case(
DealtHand::new("QQQJA", 20),
DealtHand::new("T55J5", 30),
Ordering::Greater,
);
//KK677 and KTJJT are both two pair.
//Their first cards both have the same label,
//but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2 and KK677 gets rank 3.
assert_case(
DealtHand::new("KK677", 20),
DealtHand::new("KTJJT", 30),
Ordering::Greater,
);
}
#[test]
fn sort_example_correctly() {
//32T3K is the only one pair and the other hands are all a stronger type, so it gets rank 1.
//
//KK677 and KTJJT are both two pair.
//Their first cards both have the same label,
//but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2 and KK677 gets rank 3.
//
//T55J5 and QQQJA are both three of a kind.
//QQQJA has a stronger first card, so it gets rank 5 and T55J5 gets rank 4.
let mut input: Vec<_> = vec![
DealtHand::new("32T3K", 765),
DealtHand::new("T55J5", 684),
DealtHand::new("KK677", 28),
DealtHand::new("KTJJT", 220),
DealtHand::new("QQQJA", 483),
]
.into_iter()
.map(CategorizedHand::from)
.collect();
input.sort();
insta::assert_debug_snapshot!(input);
}
}

View file

@ -3,12 +3,28 @@ use std::{str::FromStr, usize};
use derive_more::derive::Debug; use derive_more::derive::Debug;
use thiserror::Error; use thiserror::Error;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct DealtHand { pub struct DealtHand {
hand: String, hand: String,
score: usize, score: usize,
} }
impl DealtHand {
#[cfg(test)]
pub fn new(hand: impl Into<String>, score: usize) -> Self {
let hand = hand.into();
Self { hand, score }
}
pub fn hand(&self) -> &str {
&self.hand
}
pub fn score(&self) -> usize {
self.score
}
}
#[derive(Debug, Error, PartialEq, Eq)] #[derive(Debug, Error, PartialEq, Eq)]
pub enum InvalidStrDealtHand { pub enum InvalidStrDealtHand {
#[error("Dealt hand must consist of 2 elements")] #[error("Dealt hand must consist of 2 elements")]

View file

@ -0,0 +1,17 @@
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum HandKind {
/// High card, where all cards' labels are distinct: 23456
HighCard,
/// One pair, where two cards share one label, and the other three cards have a different label from the pair and each other: A23A4
OnePair,
/// Two pair, where two cards share one label, two other cards share a second label, and the remaining card has a third label: 23432
TwoPair,
/// Three of a kind, where three cards have the same label, and the remaining two cards are each different from any other card in the hand: TTT98
Three,
/// Full house, where three cards have the same label, and the remaining two cards share a different label: 23332
FullHouse,
/// Four of a kind, where four cards have the same label and one card has a different label: AA8AA
Four,
/// Five of a kind, where all five cards have the same label: AAAAA
Five,
}

View file

@ -0,0 +1,23 @@
use super::dealt_hand::DealtHand;
pub fn parse_input(input: &str) -> Vec<DealtHand> {
input.lines().map(|line| line.parse().unwrap()).collect()
}
#[cfg(test)]
mod testing {
use crate::solutions::day7::{dealt_hand::DealtHand, TEST_INPUT};
#[test]
fn parse() {
let actual = super::parse_input(TEST_INPUT);
let expected = vec![
DealtHand::new("32T3K", 765),
DealtHand::new("T55J5", 684),
DealtHand::new("KK677", 28),
DealtHand::new("KTJJT", 220),
DealtHand::new("QQQJA", 483),
];
assert_eq!(expected, actual);
}
}

View file

@ -0,0 +1,41 @@
---
source: src/solutions/day7/categorized_hand.rs
expression: input
---
[
CategorizedHand {
kind: OnePair,
rest: DealtHand {
hand: "32T3K",
score: 765,
},
},
CategorizedHand {
kind: TwoPair,
rest: DealtHand {
hand: "KTJJT",
score: 220,
},
},
CategorizedHand {
kind: TwoPair,
rest: DealtHand {
hand: "KK677",
score: 28,
},
},
CategorizedHand {
kind: Three,
rest: DealtHand {
hand: "T55J5",
score: 684,
},
},
CategorizedHand {
kind: Three,
rest: DealtHand {
hand: "QQQJA",
score: 483,
},
},
]