Skip to content
Snippets Groups Projects

AoC 2023 day 7

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by s91149
    main.rs 3.78 KiB
    use std::{cmp::Ordering, collections::HashMap};
    
    fn main() {
        let mut hands: Vec<Hand> = std::fs::read_to_string("../inputs/input.txt")
            .unwrap()
            .lines()
            .map(|line| {
                let split: Vec<&str> = line.split_whitespace().collect();
                let cards: Vec<char> = split[0].chars().collect();
                let bid: u32 = split[1].parse().unwrap();
                Hand {
                    cards,
                    bid,
                    is_part_1: true,
                }
            })
            .collect();
    
        hands.sort();
        println!("part 1: {}", get_total_winnings(&hands));
    
        hands.iter_mut().for_each(|hand| hand.is_part_1 = false);
        hands.sort();
        println!("part 2: {}", get_total_winnings(&hands));
    }
    
    fn get_total_winnings(sorted_hands: &[Hand]) -> u32 {
        sorted_hands
            .iter()
            .enumerate()
            .map(|(i, hand)| hand.bid * (i as u32 + 1))
            .sum()
    }
    
    #[derive(Debug, Eq, PartialEq, Clone)]
    struct Hand {
        cards: Vec<char>,
        bid: u32,
        is_part_1: bool,
    }
    
    fn get_occurrences(cards: &[char]) -> HashMap<char, u32> {
        let mut occurrences: HashMap<char, u32> = HashMap::new();
        for card in cards.iter() {
            *occurrences.entry(*card).or_default() += 1;
        }
        occurrences
    }
    
    impl Hand {
        fn type_score(&self) -> u8 {
            let occurrences = get_occurrences(&self.cards);
            match occurrences.values().max() {
                Some(5) => 6,
                Some(4) => 5,
                Some(3) => {
                    if occurrences.values().any(|x| *x == 2) {
                        4
                    } else {
                        3
                    }
                }
                Some(2) => {
                    if occurrences.values().filter(|&x| *x == 2).count() == 2 {
                        2
                    } else {
                        1
                    }
                }
                Some(1) => 0,
                _ => unreachable!(),
            }
        }
    
        fn replace_joker(&self) -> Hand {
            let mut without_jokers: Vec<char> =
                self.cards.iter().filter(|&&c| c != 'J').copied().collect();
    
            let occurrences = get_occurrences(&without_jokers);
            without_jokers.sort_by(|a, b| occurrences[a].cmp(&occurrences[b]));
            let most_common = without_jokers.last().unwrap_or(&'A');
    
            let mut cards = without_jokers.clone();
            for _i in 0..(5 - without_jokers.len()) {
                cards.push(*most_common);
            }
            Hand { cards, ..*self }
        }
    }
    
    impl Ord for Hand {
        fn cmp(&self, other: &Self) -> Ordering {
            let (self_score, other_score) = if self.is_part_1 {
                (self.type_score(), other.type_score())
            } else {
                (
                    self.replace_joker().type_score(),
                    other.replace_joker().type_score(),
                )
            };
    
            if self_score == other_score {
                let order = if self.is_part_1 {
                    [
                        'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2',
                    ]
                } else {
                    [
                        'A', 'K', 'Q', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'J',
                    ]
                };
    
                for (&self_card, &other_card) in self.cards.iter().zip(other.cards.iter()) {
                    let self_index = order.iter().position(|&x| x == self_card).unwrap();
                    let other_index = order.iter().position(|&x| x == other_card).unwrap();
    
                    match self_index.cmp(&other_index) {
                        Ordering::Equal => continue,
                        Ordering::Greater => return Ordering::Less,
                        Ordering::Less => return Ordering::Greater,
                    }
                }
            }
            self_score.cmp(&other_score)
        }
    }
    
    impl PartialOrd for Hand {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            Some(self.cmp(other))
        }
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment