day7: problem 1 & 2 solutions

forgot to commit problem 1 only solution
This commit is contained in:
Keenan Tims 2023-12-06 22:37:02 -08:00
parent 9718b8ab04
commit 9ab450d258
Signed by: ktims
GPG Key ID: 11230674D69038D4
4 changed files with 1282 additions and 0 deletions

7
7/Cargo.lock generated Normal file
View 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
View 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]

1000
7/input Normal file

File diff suppressed because it is too large Load Diff

267
7/src/main.rs Normal file
View 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);
}
}