Advent of Code 2023 - Day 07
I'ts that time of year again, the Advent of Code! I'm completing the challenges this year using Rust 🦀 I wish I could have more time to cleanup day 7's condition hell 😅!
I'll be posting them here, just to document how I'm solving each challenge, and I highly encourage you to check them out before reading my solutions!
Here's the Day 7 Challenge
BTW: Complete Part 1 to have access to Part 2 😉
Solution for Part 1
use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Result};
use std::path::Path;
use std::str::FromStr;
use crate::HandType::{FiveOfAKind, FourOfAKind, FullHouse, ThreeOfAKind, TwoPair};
use itertools::{Itertools, rev};
#[derive(Eq, Hash, PartialEq, Debug)]
enum HandType {
FiveOfAKind,
FourOfAKind,
FullHouse,
ThreeOfAKind,
TwoPair,
OnePair,
HighCard,
}
const HAND_STRENGTH: [HandType; 7] = [
FiveOfAKind,
FourOfAKind,
FullHouse,
ThreeOfAKind,
TwoPair,
HandType::OnePair,
HandType::HighCard,
];
#[derive(Eq, Hash, PartialEq, Debug)]
struct Hand {
original_hand: String,
hand_type: HandType,
bid: i32,
}
const LABELS: [char; 13] = [
'A',
'K',
'Q',
'J',
'T',
'9',
'8',
'7',
'6',
'5',
'4',
'3',
'2',
];
fn main() {
let input_file_path = "path_to_input";
let mut curr_sum = 0;
let iterator = for_each_line(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let mut hands: Vec<Hand> = vec![];
for line in iterator.flatten() {
let (hand, bid) = line.split_once(' ').unwrap();
let hand: Hand = build_hand(hand, bid);
hands.push(hand);
}
hands.sort_by(hand_order);
for (idx, hand) in rev(hands).enumerate() {
curr_sum += (i32::try_from(idx).unwrap() + 1) * hand.bid;
}
println!("Sum of all winning cards: {}", curr_sum);
}
fn hand_order(a: &Hand, b: &Hand) -> Ordering {
let a_hand_pos = HAND_STRENGTH.iter().position(|i| i == &a.hand_type).unwrap();
let b_hand_pos = HAND_STRENGTH.iter().position(|i| i == &b.hand_type).unwrap();
match a_hand_pos.cmp(&b_hand_pos) {
Ordering::Less => { Ordering::Less }
Ordering::Greater => { Ordering::Greater}
Ordering::Equal => {
for idx in 0..5 {
let idx = idx as usize;
let a_char = a.original_hand.chars().nth(idx).unwrap();
let b_char = b.original_hand.chars().nth(idx).unwrap();
let a_position = LABELS.iter().position(|i| i == &a_char).unwrap();
let b_position = LABELS.iter().position(|i| i == &b_char).unwrap();
match a_position.cmp(&b_position) {
Ordering::Less => { return Ordering::Less }
Ordering::Greater => { return Ordering::Greater}
Ordering::Equal => { continue }
}
}
Ordering::Equal
}
}
}
fn build_hand(original_hand: &str, bid: &str) -> Hand {
let bid = i32::from_str(bid).unwrap();
let mut ordered_hand = original_hand.chars()
.collect::<Vec<char>>();
ordered_hand.sort_by(|a, b| {
let a_position = LABELS.iter().position(|i| i == a).unwrap();
let b_position = LABELS.iter().position(|i| i == b).unwrap();
a_position.cmp(&b_position)
});
if ordered_hand.clone().iter().all(|i| i == &ordered_hand[0]) {
return Hand {
original_hand: original_hand.into(),
hand_type: FiveOfAKind,
bid,
};
}
if ordered_hand.clone().windows(4).any(|chunk| {
chunk.iter().all(|i| {
i == &chunk[0]
})
}) {
return Hand {
original_hand: original_hand.into(),
hand_type: FourOfAKind,
bid,
};
}
if is_full_house(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: FullHouse,
bid,
};
}
if ordered_hand.clone().windows(3)
.any(|chunk| chunk.iter().all(|i| i == &chunk[0])) {
return Hand {
original_hand: original_hand.into(),
hand_type: ThreeOfAKind,
bid,
};
}
if is_two_pair(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: TwoPair,
bid,
};
}
if ordered_hand.clone().windows(2)
.any(|chunk| chunk.iter().all(|i| i == &chunk[0])) {
return Hand {
original_hand: original_hand.into(),
hand_type: HandType::OnePair,
bid,
};
}
Hand {
original_hand: original_hand.into(),
hand_type: HandType::HighCard,
bid,
}
}
fn is_two_pair(hand: &[char]) -> bool {
let deduplicated_hand = hand.iter().unique().collect_vec();
deduplicated_hand.len() == 3
}
fn is_full_house(hand: &[char]) -> bool {
return (hand[0..=1].iter().all(|i| i == &hand[0]) && hand[2..=4].iter().all(|i| i == &hand[2])) ||
(hand[0..=2].iter().all(|i| i == &hand[0]) && hand[3..=4].iter().all(|i| i == &hand[3]));
}
fn for_each_line<P: AsRef<Path>>(filename: P) -> Result<Lines<BufReader<File>>> {
let file = File::open(filename)?;
Ok(BufReader::new(file).lines())
}
Solution for Part 2
use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Result};
use std::path::Path;
use std::str::FromStr;
use crate::HandType::{FiveOfAKind, FourOfAKind, FullHouse, ThreeOfAKind, TwoPair};
use itertools::{Itertools, rev};
#[derive(Eq, Hash, PartialEq, Debug)]
enum HandType {
FiveOfAKind,
FourOfAKind,
FullHouse,
ThreeOfAKind,
TwoPair,
OnePair,
HighCard,
}
const HAND_STRENGTH: [HandType; 7] = [
FiveOfAKind,
FourOfAKind,
FullHouse,
ThreeOfAKind,
TwoPair,
HandType::OnePair,
HandType::HighCard,
];
#[derive(Eq, Hash, PartialEq, Debug)]
struct Hand {
original_hand: String,
hand_type: HandType,
bid: i32,
}
const LABELS: [char; 13] = [
'A',
'K',
'Q',
'T',
'9',
'8',
'7',
'6',
'5',
'4',
'3',
'2',
'J',
];
fn main() {
let input_file_path = "path_to_input_file";
let mut curr_sum = 0;
let iterator = for_each_line(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let mut hands: Vec<Hand> = vec![];
for line in iterator.flatten() {
let (hand, bid) = line.split_once(' ').unwrap();
let hand: Hand = build_hand(hand, bid);
hands.push(hand);
}
hands.sort_by(hand_order);
for (idx, hand) in rev(hands).enumerate() {
curr_sum += (i32::try_from(idx).unwrap() + 1) * hand.bid;
}
println!("Sum of all winning cards: {}", curr_sum);
}
fn hand_order(a: &Hand, b: &Hand) -> Ordering {
let a_hand_pos = HAND_STRENGTH.iter().position(|i| i == &a.hand_type).unwrap();
let b_hand_pos = HAND_STRENGTH.iter().position(|i| i == &b.hand_type).unwrap();
match a_hand_pos.cmp(&b_hand_pos) {
Ordering::Less => { Ordering::Less }
Ordering::Greater => { Ordering::Greater}
Ordering::Equal => {
for idx in 0..5 {
let idx = idx as usize;
let a_char = a.original_hand.chars().nth(idx).unwrap();
let b_char = b.original_hand.chars().nth(idx).unwrap();
let a_position = LABELS.iter().position(|i| i == &a_char).unwrap();
let b_position = LABELS.iter().position(|i| i == &b_char).unwrap();
match a_position.cmp(&b_position) {
Ordering::Less => { return Ordering::Less }
Ordering::Greater => { return Ordering::Greater}
Ordering::Equal => { continue }
}
}
Ordering::Equal
}
}
}
fn build_hand(original_hand: &str, bid: &str) -> Hand {
let bid = i32::from_str(bid).unwrap();
let mut ordered_hand = original_hand.chars()
.collect::<Vec<char>>();
ordered_hand.sort_by(|a, b| {
let a_position = LABELS.iter().position(|i| i == a).unwrap();
let b_position = LABELS.iter().position(|i| i == b).unwrap();
a_position.cmp(&b_position)
});
if is_five_of_a_kind(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: FiveOfAKind,
bid,
};
}
if ordered_hand.as_slice().iter().any(|c| c == &'J') {
return build_joker_based_hand(ordered_hand.as_slice(), original_hand, bid)
}
if is_four_of_a_kind(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: FourOfAKind,
bid,
};
}
if is_full_house(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: FullHouse,
bid,
};
}
if is_three_of_a_kind(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: ThreeOfAKind,
bid,
};
}
if is_two_pair(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: TwoPair,
bid,
};
}
if is_one_pair(&ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: HandType::OnePair,
bid,
};
}
Hand {
original_hand: original_hand.into(),
hand_type: HandType::HighCard,
bid,
}
}
fn build_joker_based_hand(ordered_hand: &[char], original_hand: &str, bid: i32) -> Hand {
let joker_count : Vec<&char> = ordered_hand.iter().filter(|c| c == &&'J').collect();
let joker_count = joker_count.len();
if is_four_of_a_kind(ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: FiveOfAKind,
bid,
};
}
if is_full_house(ordered_hand) && joker_count >= 2 {
return Hand {
original_hand: original_hand.into(),
hand_type: FiveOfAKind,
bid,
};
}
if is_three_of_a_kind(ordered_hand) && [1, 3].contains(&joker_count) {
return Hand {
original_hand: original_hand.into(),
hand_type: FourOfAKind,
bid,
};
}
if is_two_pair(ordered_hand) && joker_count == 2 {
return Hand {
original_hand: original_hand.into(),
hand_type: FourOfAKind,
bid,
};
}
if is_two_pair(ordered_hand) && joker_count == 1 {
return Hand {
original_hand: original_hand.into(),
hand_type: FullHouse,
bid,
};
}
if is_one_pair(ordered_hand) {
return Hand {
original_hand: original_hand.into(),
hand_type: ThreeOfAKind,
bid,
};
}
Hand {
original_hand: original_hand.into(),
hand_type: HandType::OnePair,
bid,
}
}
fn is_five_of_a_kind(ordered_hand: &[char]) -> bool {
ordered_hand.clone().iter().all(|i| i == &ordered_hand[0])
}
fn is_one_pair(ordered_hand: &[char]) -> bool {
ordered_hand.clone().windows(2)
.any(|chunk| chunk.iter().all(|i| i == &chunk[0]))
}
fn is_three_of_a_kind(ordered_hand: &[char]) -> bool {
ordered_hand.clone().windows(3)
.any(|chunk| chunk.iter().all(|i| i == &chunk[0]))
}
fn is_four_of_a_kind(ordered_hand: &[char]) -> bool {
ordered_hand.clone().windows(4).any(|chunk| {
chunk.iter().all(|i| {
i == &chunk[0]
})
})
}
fn is_two_pair(hand: &[char]) -> bool {
let deduplicated_hand = hand.iter().unique().collect_vec();
deduplicated_hand.len() == 3
}
fn is_full_house(hand: &[char]) -> bool {
return (hand[0..=1].iter().all(|i| i == &hand[0]) && hand[2..=4].iter().all(|i| i == &hand[2])) ||
(hand[0..=2].iter().all(|i| i == &hand[0]) && hand[3..=4].iter().all(|i| i == &hand[3]));
}
fn for_each_line<P: AsRef<Path>>(filename: P) -> Result<Lines<BufReader<File>>> {
let file = File::open(filename)?;
Ok(BufReader::new(file).lines())
}