diff --git a/07/README.md b/07/README.md new file mode 100644 index 0000000..e995e72 --- /dev/null +++ b/07/README.md @@ -0,0 +1,12 @@ +# [Day 7](https://adventofcode.com/2023/day/7) +:gift::gift::gift::gift::gift::gift::gift: + +Today's language: **Rust** + +Not pretty, but pretty fast. + +```shell +rustc day07.rs +./day07 +``` + diff --git a/07/day07.rs b/07/day07.rs new file mode 100644 index 0000000..21c03ad --- /dev/null +++ b/07/day07.rs @@ -0,0 +1,227 @@ +use std::fmt; +use std::io::{self, BufRead}; +use std::cmp::{Ord, Ordering}; + +const HIGH_CARD: u32 = 0; +const ONE_PAIR: u32 = 1; +const TWO_PAIR: u32 = 2; +const THREE_OF_KIND: u32 = 3; +const FULL_HOUSE: u32 = 4; +const FOUR_OF_KIND: u32 = 5; +const FIVE_OF_KIND: u32 = 6; + + +#[derive(Eq)] +struct Hand { + cards: Vec, + hand_type: u32, + bid: u32, +} +impl Hand { + fn new1(cards: &str, bid: u32, card_counts: &mut [usize; 13]) -> Hand { + let mut hand = Hand{ cards: cards.chars().map(|c| Hand::char_to_num1(c)).collect(), hand_type: HIGH_CARD, bid: bid }; + assert!(hand.cards.len() == 5, "Found {} cards instead of five", hand.cards.len()); + hand.determine_type1(card_counts); + return hand; + } + fn new2(cards: &str, bid: u32, card_counts: &mut [usize; 13]) -> Hand { + let mut hand = Hand{ cards: cards.chars().map(|c| Hand::char_to_num2(c)).collect(), hand_type: HIGH_CARD, bid: bid }; + assert!(hand.cards.len() == 5, "Found {} cards instead of five", hand.cards.len()); + hand.determine_type2(card_counts); + return hand; + } + fn char_to_num1(c: char) -> usize { + match c { + '2' => return 0, + '3' => return 1, + '4' => return 2, + '5' => return 3, + '6' => return 4, + '7' => return 5, + '8' => return 6, + '9' => return 7, + 'T' => return 8, + 'J' => return 9, + 'Q' => return 10, + 'K' => return 11, + 'A' => return 12, + _ => panic!("Invalid char found: {}", c), + } + } + fn char_to_num2(c: char) -> usize { + match c { + 'J' => return 0, + '2' => return 1, + '3' => return 2, + '4' => return 3, + '5' => return 4, + '6' => return 5, + '7' => return 6, + '8' => return 7, + '9' => return 8, + 'T' => return 9, + 'Q' => return 10, + 'K' => return 11, + 'A' => return 12, + _ => panic!("Invalid char found: {}", c), + } + } + fn determine_type1(&mut self, card_counts: &mut [usize; 13]) { + // use given array to reduce memory allocations + for count in card_counts.iter_mut() { *count = 0 }; + for card in &self.cards { + card_counts[*card] += 1; + } + let mut found_three = false; + let mut found_two = false; + for count in card_counts { + match count { + 5 => { self.hand_type = FIVE_OF_KIND; return; }, + 4 => { self.hand_type = FOUR_OF_KIND; return; }, + 3 => { self.hand_type = THREE_OF_KIND; found_three = true; }, + 2 => { + if found_two { + self.hand_type = TWO_PAIR; + return; + } + else { + self.hand_type = ONE_PAIR; + found_two = true; + } + }, + _ => {}, + } + if found_two && found_three { + self.hand_type = FULL_HOUSE; + } + } + } + fn determine_type2(&mut self, card_counts: &mut [usize; 13]) { + // better solution would be to just add the number of jokers to the card with the highest + // number + // use given array to reduce memory allocations + for count in card_counts.iter_mut() { *count = 0 }; + for card in &self.cards { + card_counts[*card] += 1; + } + let mut found_three = false; + let mut found_two = false; + // determine without jokers + for count in card_counts.iter().skip(1) { + match count { + 5 => { self.hand_type = FIVE_OF_KIND; return; }, + 4 => { self.hand_type = FOUR_OF_KIND; break; }, + 3 => { self.hand_type = THREE_OF_KIND; found_three = true; }, + 2 => { + if found_two { + self.hand_type = TWO_PAIR; + break; + } + else { + self.hand_type = ONE_PAIR; + found_two = true; + } + }, + _ => {}, + } + if found_two && found_three { + self.hand_type = FULL_HOUSE; + return + } + } + match card_counts[0] { + 0 => return, // no jokers + 1 => match self.hand_type { + FOUR_OF_KIND => self.hand_type = FIVE_OF_KIND, + THREE_OF_KIND => self.hand_type = FOUR_OF_KIND, + TWO_PAIR => self.hand_type = FULL_HOUSE, + ONE_PAIR => self.hand_type = THREE_OF_KIND, + HIGH_CARD => self.hand_type = ONE_PAIR, + _ => panic!("Unhandled joker(1) case: hand_type={}", self.hand_type), + }, + 2 => match self.hand_type { + THREE_OF_KIND => self.hand_type = FIVE_OF_KIND, + ONE_PAIR => self.hand_type = FOUR_OF_KIND, + HIGH_CARD => self.hand_type = THREE_OF_KIND, + _ => panic!("Unhandled joker(2) case: hand_type={}", self.hand_type) + }, + 3 => match self.hand_type { + ONE_PAIR => self.hand_type = FIVE_OF_KIND, + HIGH_CARD => self.hand_type = FOUR_OF_KIND, + _ => panic!("Unhandled joker(3) case: hand_type={}", self.hand_type) + }, + _ => self.hand_type = FIVE_OF_KIND + } + } +} + + +impl Ord for Hand { + fn cmp(&self, other: &Self) -> Ordering { + let mut order = self.hand_type.cmp(&other.hand_type); + if order != Ordering::Equal { return order }; + for i in 0..self.cards.len() { + order = self.cards[i].cmp(&other.cards[i]); + if order != Ordering::Equal { return order }; + } + panic!("Equal elements: {} and {}", self, other); + } +} +impl PartialOrd for Hand { + fn partial_cmp(&self, other: &Self) -> Option { + return Some(self.cmp(other)); + } +} +impl PartialEq for Hand { + fn eq(&self, other: &Self) -> bool { + return self.cmp(other) == Ordering::Equal; + } +} +impl fmt::Display for Hand { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ =write!(f, "Hand="); + for c in &self.cards { + let _ = write!(f, "{}", c); + } + write!(f, ", type={}, bid={}", self.hand_type, self.bid) + } +} + + +fn main() { + let input = "input.txt"; + let lines = read_lines(&input); + let mut hands1: Vec = Vec::new(); + hands1.reserve(1000); + let mut hands2: Vec = Vec::new(); + hands2.reserve(1000); + // use given array to reduce memory allocations + let mut card_counts: [usize; 13] = [0; 13]; + for (_, line) in lines.enumerate() { + let Ok(line) = line else { panic!("Line not ok"); }; + hands1.push(Hand::new1(&line[..5], line[6..].parse::().unwrap(), &mut card_counts)); + hands2.push(Hand::new2(&line[..5], line[6..].parse::().unwrap(), &mut card_counts)); + // println!("Hand: {}", hands.last().expect("No hand")); + } + hands1.sort_unstable(); + hands2.sort_unstable(); + let mut total_winnings1: u32 = 0; + let mut total_winnings2: u32 = 0; + for (i, hand) in hands1.iter().enumerate() { + total_winnings1 += (i as u32 + 1) * hand.bid; + } + for (i, hand) in hands2.iter().enumerate() { + total_winnings2 += (i as u32 + 1) * hand.bid; + } + println!("Total winnings (1): {}", total_winnings1); + println!("Total winnings (2): {}", total_winnings2); +} + + +fn read_lines

(filename: P) -> io::Lines> +where P: AsRef, { + return match std::fs::File::open(filename) { + Err(why) => panic!("Could not open file. {}", why), + Ok(file) => std::io::BufReader::new(file).lines() + }; +} diff --git a/next_day.sh b/next_day.sh index 5955eec..9b6e013 100755 --- a/next_day.sh +++ b/next_day.sh @@ -1,9 +1,7 @@ #!/bin/bash maxday=$(ls -d */ | sed -r 's|0*(.*)/|\1|g' | tail -1) -echo $maxday nextday=$((maxday + 1)) nextday_dir=$(printf "%02d" $nextday) -echo $nextday mkdir $nextday_dir sed "s/X/$nextday/g" README.md.temp > $nextday_dir/README.md