[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

16
Cargo.lock generated
View File

@@ -6,6 +6,7 @@ version = 3
name = "advent-of-code-2023-rust"
version = "0.1.0"
dependencies = [
"itertools",
"num",
]
@@ -15,6 +16,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "num"
version = "0.4.1"

View File

@@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
num = "0.4"
itertools = "0.12.0"
# For flamegraph, enable this:
[profile.release]

1000
input/day07.txt Normal file

File diff suppressed because it is too large Load Diff

5
input/day07_example.txt Normal file
View File

@@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483

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());
}

View File

@@ -7,6 +7,7 @@ use crate::day3::Day3;
use crate::day4::Day4;
use crate::day5::Day5;
use crate::day6::Day6;
use crate::day7::Day7;
use crate::day_solver::DaySolver;
use crate::util::read_file;
@@ -17,9 +18,10 @@ mod day3;
mod day4;
mod day5;
mod day6;
mod day7;
mod day_solver;
const MAX_DAY: u8 = 6;
const MAX_DAY: u8 = 7;
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
fn main() {
@@ -96,6 +98,7 @@ fn build_day_solver(day: u8, input: String) -> Option<Box<dyn DaySolver>> {
4 => Some(Box::new(Day4::create(input))),
5 => Some(Box::new(Day5::create(input))),
6 => Some(Box::new(Day6::create(input))),
7 => Some(Box::new(Day7::create(input))),
_ => None
}
}