[TASK] Day 7

This commit is contained in:
2023-12-11 00:08:32 +01:00
parent fcc6bb39d0
commit 4b79613e19
6 changed files with 1225 additions and 1 deletions

199
src/day7.rs Normal file
View File

@@ -0,0 +1,199 @@
use std::{fmt::Display, cmp::Ordering};
use itertools::Itertools;
use crate::day_solver::DaySolver;
#[cfg(test)]
use crate::util::read_file;
const CARD_ORDER: [char; 13] = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'];
pub struct Day7 {
hands: Vec<Hand>
}
pub struct Hand {
cards: [char; 5],
bid: usize
}
impl Display for Hand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.cards.iter().collect::<String>(), self.bid)
}
}
impl Hand {
fn parse(input: &str) -> Self {
let (cards_str, bid_str) = input.split_once(" ").unwrap();
return Hand {
cards: cards_str.chars().collect::<Vec<char>>().try_into().unwrap(),
bid: bid_str.parse().unwrap()
}
}
fn calc_type(&self) -> u8 {
let mut found_three_of_kind = false;
let mut found_pair = false;
for unique_card in self.cards.iter().unique() {
let card_appearances = self.cards.iter().filter(|c| c == &unique_card).count();
if card_appearances == 5 {
return FIVE_OF_A_KIND;
} else if card_appearances == 4 {
return FOUR_OF_A_KIND;
} else if card_appearances == 3 {
found_three_of_kind = true;
} else if card_appearances == 2 {
if found_pair {
return TWO_PAIR
} else {
found_pair = true
}
}
}
if found_three_of_kind && found_pair {
return FULL_HOUSE;
} else if found_three_of_kind {
return THREE_OF_A_KIND;
} else if found_pair {
return ONE_PAIR;
}
return HIGH_CARD;
}
// Calculates the maximum type, given that "J" is a joker
fn calc_max_type(&self) -> u8 {
if !self.cards.contains(&'J') {
return self.calc_type();
}
let mut max_type = self.calc_type();
if max_type == FIVE_OF_A_KIND { return max_type };
// It only makes sense to try to map jokers to any of the other cards we have
let owned_cards: Vec<&char> = self.cards.iter().filter(|c| c != &&'J').unique().collect();
for i in 0..5 {
if self.cards[i] == 'J' {
for c in &owned_cards {
let mut new_cards = self.cards.clone();
new_cards[i] = *c.to_owned();
let new_hand = Hand { cards: new_cards, bid: 0};
let j_type = new_hand.calc_max_type();
// println!("Trying hand {} as {}, got type {}", self, new_hand, j_type);
if j_type > max_type {
max_type = j_type;
if max_type == FIVE_OF_A_KIND { return max_type }
}
}
}
}
return max_type;
}
}
const FIVE_OF_A_KIND: u8 = 64;
const FOUR_OF_A_KIND: u8 = 32;
const FULL_HOUSE: u8 = 16;
const THREE_OF_A_KIND: u8 = 8;
const TWO_PAIR: u8 = 4;
const ONE_PAIR: u8 = 2;
const HIGH_CARD: u8 = 0;
impl Day7 {
pub fn create(input: String) -> Self {
// let lines = input.lines();
// Put the input into the day struct
return Day7 {
hands: input.lines().map(Hand::parse).collect()
}
}
}
fn card_rank(card: &char) -> usize {
for i in 0..CARD_ORDER.len() {
if &CARD_ORDER[i] == card {
return i;
}
}
CARD_ORDER.len()
}
fn compare_card(a: &char, b: &char) -> Ordering {
return card_rank(b).partial_cmp(&card_rank(a)).unwrap();
}
fn compare_card_pt2(a: &char, b: &char) -> Ordering {
if a == &'J' && b == &'J' { return Ordering::Equal }
else if a == &'J' { return Ordering::Less }
else if b == &'J' { return Ordering::Greater }
else {
return card_rank(b).partial_cmp(&card_rank(a)).unwrap();
}
}
impl DaySolver for Day7 {
fn solve_part1(&mut self) -> String {
let mut hands_and_types: Vec<(&Hand, u8)> = self.hands.iter()
.map(|h| (h, h.calc_type()))
.collect();
hands_and_types.sort_by(|a, b| {
if a.1 != b.1 {
return a.1.partial_cmp(&b.1).unwrap();
} else {
for i in 0usize..5 {
let card_comp = compare_card(&a.0.cards[i], &b.0.cards[i]);
if card_comp.is_ne() {
return card_comp;
}
}
return Ordering::Equal;
}
});
return hands_and_types.iter().map(|(h,_)|h).enumerate()
.map(|(r, h)| (r + 1) * h.bid)
.sum::<usize>()
.to_string();
}
fn solve_part2(&mut self) -> String {
let mut hands_and_max_types: Vec<(&Hand, u8)> = self.hands.iter()
.map(|h| (h, h.calc_max_type()))
.collect();
hands_and_max_types.sort_by(|a, b| {
if a.1 != b.1 {
return a.1.partial_cmp(&b.1).unwrap();
} else {
for i in 0..5 {
let card_comp = compare_card_pt2(&a.0.cards[i], &b.0.cards[i]);
if card_comp.is_ne() {
return card_comp;
}
}
return Ordering::Equal;
}
});
// for (hand, max_type) in &hands_and_max_types {
// println!("Hand {} with type {}", hand, max_type);
// }
return hands_and_max_types.iter().map(|(h,_)|h).enumerate()
.map(|(r, h)| (r + 1) * h.bid)
.sum::<usize>()
.to_string();
}
}
#[test]
fn test_part1() {
let mut day = Day7::create(read_file("input/day07_example.txt"));
assert_eq!("6440", day.solve_part1());
}
#[test]
fn test_part2() {
let mut day = Day7::create(read_file("input/day07_example.txt"));
assert_eq!("5905", day.solve_part2());
}