[TASK] Day 7
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
1000
input/day07.txt
Normal file
File diff suppressed because it is too large
Load Diff
5
input/day07_example.txt
Normal file
5
input/day07_example.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
199
src/day7.rs
Normal file
199
src/day7.rs
Normal 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());
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user