day7: problem 1 & 2 solutions
forgot to commit problem 1 only solution
This commit is contained in:
		
							
								
								
									
										267
									
								
								7/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								7/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
 | 
			
		||||
// BOILERPLATE
 | 
			
		||||
type InputIter = Lines<BufReader<File>>;
 | 
			
		||||
 | 
			
		||||
fn get_input() -> InputIter {
 | 
			
		||||
    let f = File::open("input").unwrap();
 | 
			
		||||
    let br = BufReader::new(f);
 | 
			
		||||
    br.lines()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    println!("Problem 1 solution: {}", problem1(get_input()));
 | 
			
		||||
    println!("Problem 2 solution: {}", problem2(get_input()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PARSING
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq)]
 | 
			
		||||
enum Card {
 | 
			
		||||
    TWO,
 | 
			
		||||
    THREE,
 | 
			
		||||
    FOUR,
 | 
			
		||||
    FIVE,
 | 
			
		||||
    SIX,
 | 
			
		||||
    SEVEN,
 | 
			
		||||
    EIGHT,
 | 
			
		||||
    NINE,
 | 
			
		||||
    T,
 | 
			
		||||
    J,
 | 
			
		||||
    Q,
 | 
			
		||||
    K,
 | 
			
		||||
    A,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Card {
 | 
			
		||||
    const fn count() -> usize {
 | 
			
		||||
        Card::A as usize + 1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
 | 
			
		||||
enum HandType {
 | 
			
		||||
    HighCard,
 | 
			
		||||
    OnePair,
 | 
			
		||||
    TwoPair,
 | 
			
		||||
    ThreeOfAKind,
 | 
			
		||||
    FullHouse,
 | 
			
		||||
    FourOfAKind,
 | 
			
		||||
    FiveOfAKind,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<char> for Card {
 | 
			
		||||
    fn from(c: char) -> Self {
 | 
			
		||||
        match c {
 | 
			
		||||
            'A' => Self::A,
 | 
			
		||||
            'K' => Self::K,
 | 
			
		||||
            'Q' => Self::Q,
 | 
			
		||||
            'J' => Self::J,
 | 
			
		||||
            'T' => Self::T,
 | 
			
		||||
            '9' => Self::NINE,
 | 
			
		||||
            '8' => Self::EIGHT,
 | 
			
		||||
            '7' => Self::SEVEN,
 | 
			
		||||
            '6' => Self::SIX,
 | 
			
		||||
            '5' => Self::FIVE,
 | 
			
		||||
            '4' => Self::FOUR,
 | 
			
		||||
            '3' => Self::THREE,
 | 
			
		||||
            '2' => Self::TWO,
 | 
			
		||||
            _ => panic!("Invalid card {}", c),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
struct Hand<const WILD: bool> {
 | 
			
		||||
    cards: Vec<Card>,
 | 
			
		||||
    bid: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const WILD: bool> Hand<WILD> {
 | 
			
		||||
    fn card_counts(&self) -> [u64; Card::count()] {
 | 
			
		||||
        let mut counts = [0u64; Card::count()];
 | 
			
		||||
        for card in &self.cards {
 | 
			
		||||
            counts[*card as usize] += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        counts
 | 
			
		||||
    }
 | 
			
		||||
    fn hand_type(&self) -> HandType {
 | 
			
		||||
        if !WILD {
 | 
			
		||||
            let card_counts = self.card_counts();
 | 
			
		||||
            if card_counts.contains(&5) {
 | 
			
		||||
                HandType::FiveOfAKind
 | 
			
		||||
            } else if card_counts.contains(&4) {
 | 
			
		||||
                HandType::FourOfAKind
 | 
			
		||||
            } else if card_counts.contains(&3) && card_counts.contains(&2) {
 | 
			
		||||
                HandType::FullHouse
 | 
			
		||||
            } else if card_counts.contains(&3) {
 | 
			
		||||
                HandType::ThreeOfAKind
 | 
			
		||||
            } else if card_counts.iter().filter(|count| **count == 2).count() == 2 {
 | 
			
		||||
                HandType::TwoPair
 | 
			
		||||
            } else if card_counts.contains(&2) {
 | 
			
		||||
                HandType::OnePair
 | 
			
		||||
            } else {
 | 
			
		||||
                HandType::HighCard
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let mut card_counts = self.card_counts();
 | 
			
		||||
            let wild_count = card_counts[Card::J as usize];
 | 
			
		||||
            card_counts[Card::J as usize] = 0;
 | 
			
		||||
 | 
			
		||||
            let same_count = card_counts.iter().max().unwrap() + wild_count;
 | 
			
		||||
 | 
			
		||||
            if same_count == 5 {
 | 
			
		||||
                HandType::FiveOfAKind
 | 
			
		||||
            } else if same_count == 4 {
 | 
			
		||||
                HandType::FourOfAKind
 | 
			
		||||
            } else if card_counts.contains(&3) && card_counts.contains(&2) {
 | 
			
		||||
                HandType::FullHouse
 | 
			
		||||
            } else
 | 
			
		||||
            // Full House with WILDs
 | 
			
		||||
            if wild_count == 1
 | 
			
		||||
                && (card_counts.contains(&3)
 | 
			
		||||
                    || (card_counts.iter().filter(|count| **count == 2).count() == 2))
 | 
			
		||||
            {
 | 
			
		||||
                HandType::FullHouse
 | 
			
		||||
            } else if wild_count == 2 && card_counts.contains(&2) {
 | 
			
		||||
                HandType::FullHouse
 | 
			
		||||
            } else if wild_count == 3 {
 | 
			
		||||
                HandType::FullHouse
 | 
			
		||||
            } else if same_count == 3 {
 | 
			
		||||
                HandType::ThreeOfAKind
 | 
			
		||||
            } else if card_counts.iter().filter(|count| **count == 2).count() == 2
 | 
			
		||||
                || (card_counts.contains(&2) && wild_count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                HandType::TwoPair
 | 
			
		||||
            } else if same_count == 2 {
 | 
			
		||||
                HandType::OnePair
 | 
			
		||||
            } else {
 | 
			
		||||
                HandType::HighCard
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const WILD: bool> From<&str> for Hand<WILD> {
 | 
			
		||||
    fn from(s: &str) -> Self {
 | 
			
		||||
        let (cards_s, bid_s) = s.split_once(' ').unwrap();
 | 
			
		||||
        let bid = bid_s.parse().unwrap();
 | 
			
		||||
        let cards = cards_s.chars().map(|c| Card::from(c)).collect();
 | 
			
		||||
 | 
			
		||||
        Hand { cards, bid }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const WILD: bool> PartialOrd for Hand<WILD> {
 | 
			
		||||
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 | 
			
		||||
        Some(self.cmp(other))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const WILD: bool> Ord for Hand<WILD> {
 | 
			
		||||
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 | 
			
		||||
        let our_type = self.hand_type();
 | 
			
		||||
        let other_type = other.hand_type();
 | 
			
		||||
        if our_type != other_type {
 | 
			
		||||
            our_type.cmp(&other_type)
 | 
			
		||||
        } else {
 | 
			
		||||
            let (ours, theirs) = self
 | 
			
		||||
                .cards
 | 
			
		||||
                .iter()
 | 
			
		||||
                .zip(other.cards.iter())
 | 
			
		||||
                .find(|(ours, theirs)| *ours != *theirs)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            if !WILD {
 | 
			
		||||
                ours.cmp(theirs)
 | 
			
		||||
            } else {
 | 
			
		||||
                if *ours == Card::J {
 | 
			
		||||
                    std::cmp::Ordering::Less
 | 
			
		||||
                } else if *theirs == Card::J {
 | 
			
		||||
                    std::cmp::Ordering::Greater
 | 
			
		||||
                } else {
 | 
			
		||||
                    ours.cmp(theirs)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 1 solution
 | 
			
		||||
 | 
			
		||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let mut hands: Vec<_> = input
 | 
			
		||||
        .map(|line| Hand::<false>::from(line.unwrap().as_str()))
 | 
			
		||||
        .collect();
 | 
			
		||||
    hands.sort();
 | 
			
		||||
    hands
 | 
			
		||||
        .iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        .map(|(idx, hand)| (idx + 1) as u64 * hand.bid)
 | 
			
		||||
        .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 2 solution
 | 
			
		||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let mut hands: Vec<_> = input
 | 
			
		||||
    .map(|line| Hand::<true>::from(line.unwrap().as_str()))
 | 
			
		||||
    .collect();
 | 
			
		||||
hands.sort();
 | 
			
		||||
hands
 | 
			
		||||
    .iter()
 | 
			
		||||
    .enumerate()
 | 
			
		||||
    .map(|(idx, hand)| (idx + 1) as u64 * hand.bid)
 | 
			
		||||
    .sum()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::io::Cursor;
 | 
			
		||||
 | 
			
		||||
    use crate::*;
 | 
			
		||||
 | 
			
		||||
    const EXAMPLE: &str = &"32T3K 765
 | 
			
		||||
T55J5 684
 | 
			
		||||
KK677 28
 | 
			
		||||
KTJJT 220
 | 
			
		||||
QQQJA 483";
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_hand_parser() {
 | 
			
		||||
        let hand = Hand::<false>::from(EXAMPLE.lines().next().unwrap());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            hand,
 | 
			
		||||
            Hand {
 | 
			
		||||
                bid: 765,
 | 
			
		||||
                cards: vec![Card::THREE, Card::TWO, Card::T, Card::THREE, Card::K]
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem1_test_hand_compare() {
 | 
			
		||||
        let hand1 = Hand::<false>::from("T55J5 123");
 | 
			
		||||
        let hand2 = Hand::<false>::from("QQQJA 123");
 | 
			
		||||
        let hand3 = Hand::<false>::from("KK677 123");
 | 
			
		||||
 | 
			
		||||
        assert!(hand2 > hand1);
 | 
			
		||||
        assert!(hand3 < hand1);
 | 
			
		||||
        assert!(hand3 < hand2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem2_test_hand_compare() {}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem1_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem1(c.lines()), 6440);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem2_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem2(c.lines()), 5905);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user