day7: problem 1 & 2 solutions
forgot to commit problem 1 only solution
This commit is contained in:
parent
9718b8ab04
commit
9ab450d258
7
7/Cargo.lock
generated
Normal file
7
7/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "day7"
|
||||
version = "0.1.0"
|
8
7/Cargo.toml
Normal file
8
7/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "day7"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user