Implemented faulty first part of day 7
This commit is contained in:
parent
bb63ca7f74
commit
d117c34786
7 changed files with 341 additions and 2 deletions
|
@ -10,7 +10,7 @@ pub fn create_solutions() -> Vec<Vec<fn(&str) -> String>> {
|
|||
vec![not_implemented_yet, not_implemented_yet],
|
||||
vec![day5::solve_task_1, day5::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],
|
||||
|
|
|
@ -1 +1,49 @@
|
|||
use categorized_hand::CategorizedHand;
|
||||
|
||||
mod categorized_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);
|
||||
}
|
||||
}
|
||||
|
|
194
src/solutions/day7/categorized_hand.rs
Normal file
194
src/solutions/day7/categorized_hand.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -3,12 +3,28 @@ use std::{str::FromStr, usize};
|
|||
use derive_more::derive::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct DealtHand {
|
||||
hand: String,
|
||||
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)]
|
||||
pub enum InvalidStrDealtHand {
|
||||
#[error("Dealt hand must consist of 2 elements")]
|
||||
|
|
17
src/solutions/day7/hand_kind.rs
Normal file
17
src/solutions/day7/hand_kind.rs
Normal 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,
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
]
|
Loading…
Add table
Reference in a new issue