finished day 07

This commit is contained in:
Kristofers Solo
2023-12-07 17:14:40 +02:00
parent 2da227afa5
commit 2d4d1e91e6
15 changed files with 2501 additions and 93 deletions

10
2023/day-07/Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "day-07"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
color-eyre.workspace = true
thiserror.workspace = true

1000
2023/day-07/input1.txt Normal file

File diff suppressed because it is too large Load Diff

1000
2023/day-07/input2.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
use color_eyre::Result;
use day_07::part1::process;
fn main() -> Result<()> {
let file = include_str!("../../input1.txt");
let result = process(file)?;
println!("{}", result);
Ok(())
}

View File

@@ -0,0 +1,9 @@
use color_eyre::Result;
use day_07::part2::process;
fn main() -> Result<()> {
let file = include_str!("../../input2.txt");
let result = process(file)?;
println!("{}", result);
Ok(())
}

19
2023/day-07/src/error.rs Normal file
View File

@@ -0,0 +1,19 @@
use thiserror::Error;
#[derive(Debug, Error)]
pub enum HandParseError {
#[error("Invalid card: {0}")]
InvalidCard(char),
#[error("Invalid bid: {0}")]
InvalidBid(#[from] std::num::ParseIntError),
#[error("Invalid card count")]
InvalidCardCount,
}
#[derive(Debug, Error)]
pub enum CombinationError {
#[error("Invalid card count")]
InvalidCardCount,
}

3
2023/day-07/src/lib.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod error;
pub mod part1;
pub mod part2;

188
2023/day-07/src/part1.rs Normal file
View File

@@ -0,0 +1,188 @@
use std::{cmp::Ordering, collections::HashMap, convert::TryInto, str::FromStr};
use crate::error::{CombinationError, HandParseError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Card {
_2,
_3,
_4,
_5,
_6,
_7,
_8,
_9,
T,
J,
Q,
K,
A,
}
impl Card {
fn from_char(ch: char) -> Result<Card, HandParseError> {
match ch {
'A' => Ok(Card::A),
'K' => Ok(Card::K),
'Q' => Ok(Card::Q),
'J' => Ok(Card::J),
'T' => Ok(Card::T),
'9' => Ok(Card::_9),
'8' => Ok(Card::_8),
'7' => Ok(Card::_7),
'6' => Ok(Card::_6),
'5' => Ok(Card::_5),
'4' => Ok(Card::_4),
'3' => Ok(Card::_3),
'2' => Ok(Card::_2),
_ => Err(HandParseError::InvalidCard(ch)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Combination {
HighCard,
OnePair,
TwoPair,
ThreeOfAKind,
FullHouse,
FourOfAKind,
FiveOfAKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Hand {
cards: [Card; 5],
bid: usize,
}
impl Hand {
fn parse_bid(s: &str) -> Result<usize, HandParseError> {
s.parse().map_err(|e| HandParseError::InvalidBid(e))
}
fn combination(&self) -> Result<Combination, CombinationError> {
let mut card_count = HashMap::new();
for &card in self.cards.iter() {
*card_count.entry(card).or_insert(0) += 1;
}
if let Some((_, _)) = card_count.iter().find(|(_, &count)| count == 5) {
Ok(Combination::FiveOfAKind)
} else if let Some((_, _)) = card_count.iter().find(|(_, &count)| count == 4) {
Ok(Combination::FourOfAKind)
} else if let Some((&card, _)) = card_count.iter().find(|(_, &count)| count == 3) {
if card_count.contains_key(&card) && card_count.get(&card) == Some(&2) {
Ok(Combination::FullHouse)
} else {
Ok(Combination::ThreeOfAKind)
}
} else {
let pairs: Vec<Card> = card_count
.iter()
.filter(|&(_, &count)| count == 2)
.map(|(&card, _)| card)
.collect();
match pairs.len() {
2 => Ok(Combination::TwoPair),
1 => Ok(Combination::OnePair),
_ => Ok(Combination::HighCard),
}
}
}
}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self.combination(), other.combination()) {
(Ok(self_comb), Ok(other_comb)) => match self_comb.cmp(&other_comb) {
Ordering::Equal => {
for (self_card, other_card) in self.cards.iter().zip(other.cards.iter()) {
match self_card.cmp(other_card) {
Ordering::Equal => continue,
other => return other,
}
}
Ordering::Equal
}
other => other,
},
(Err(_), _) => Ordering::Less,
(_, Err(_)) => Ordering::Greater,
}
}
}
impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl FromStr for Hand {
type Err = HandParseError;
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
let hand: Vec<&str> = s.split_whitespace().collect();
let cards: Result<Vec<Card>, HandParseError> = hand
.first()
.ok_or_else(|| HandParseError::InvalidCardCount)?
.chars()
.map(Card::from_char)
.collect();
let bid = Self::parse_bid(hand.last().ok_or(HandParseError::InvalidCardCount)?)?;
Ok(Self {
cards: cards?
.try_into()
.map_err(|_| HandParseError::InvalidCardCount)?,
bid,
})
}
}
pub fn process(input: &str) -> color_eyre::Result<usize> {
let mut hands: Vec<Hand> = input
.lines()
.map(Hand::from_str)
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|_| Vec::new());
hands.sort();
hands.iter().for_each(|hand| {
println!(
"{:?} \t| {:?}\t| {}",
hand.cards,
hand.combination().unwrap(),
hand.bid
);
});
let total = hands
.iter()
.enumerate()
.map(|(index, hand)| hand.bid * (index + 1))
.sum();
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process() -> color_eyre::Result<()> {
let input = "32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483";
assert_eq!(6440, process(input)?);
Ok(())
}
}

196
2023/day-07/src/part2.rs Normal file
View File

@@ -0,0 +1,196 @@
use std::{cmp::Ordering, collections::HashMap, convert::TryInto, str::FromStr};
use crate::error::{CombinationError, HandParseError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Card {
J,
_2,
_3,
_4,
_5,
_6,
_7,
_8,
_9,
T,
Q,
K,
A,
}
impl Card {
fn from_char(ch: char) -> Result<Card, HandParseError> {
match ch {
'A' => Ok(Card::A),
'K' => Ok(Card::K),
'Q' => Ok(Card::Q),
'J' => Ok(Card::J),
'T' => Ok(Card::T),
'9' => Ok(Card::_9),
'8' => Ok(Card::_8),
'7' => Ok(Card::_7),
'6' => Ok(Card::_6),
'5' => Ok(Card::_5),
'4' => Ok(Card::_4),
'3' => Ok(Card::_3),
'2' => Ok(Card::_2),
_ => Err(HandParseError::InvalidCard(ch)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Combination {
HighCard,
OnePair,
TwoPair,
ThreeOfAKind,
FullHouse,
FourOfAKind,
FiveOfAKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Hand {
cards: [Card; 5],
bid: usize,
}
impl Hand {
fn parse_bid(s: &str) -> Result<usize, HandParseError> {
s.parse().map_err(|e| HandParseError::InvalidBid(e))
}
fn combination(&self) -> Result<Combination, CombinationError> {
let mut card_count = HashMap::new();
let mut cards = self.cards.clone();
cards.sort_by(|a, b| b.cmp(a));
for &card in cards.iter() {
*card_count.entry(card).or_insert(0) += 1;
if card == Card::J {
for (_, value) in card_count.iter_mut() {
*value += 1;
}
}
}
if let Some((_, _)) = card_count.iter().find(|(_, &count)| count == 5) {
Ok(Combination::FiveOfAKind)
} else if let Some((_, _)) = card_count.iter().find(|(_, &count)| count == 4) {
Ok(Combination::FourOfAKind)
} else if let Some((&card, _)) = card_count.iter().find(|(_, &count)| count == 3) {
if card_count.contains_key(&card) && card_count.get(&card) == Some(&2) {
Ok(Combination::FullHouse)
} else {
Ok(Combination::ThreeOfAKind)
}
} else {
let pairs: Vec<Card> = card_count
.iter()
.filter(|&(_, &count)| count == 2)
.map(|(&card, _)| card)
.collect();
match pairs.len() {
2 => Ok(Combination::TwoPair),
1 => Ok(Combination::OnePair),
_ => Ok(Combination::HighCard),
}
}
}
}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self.combination(), other.combination()) {
(Ok(self_comb), Ok(other_comb)) => match self_comb.cmp(&other_comb) {
Ordering::Equal => {
for (self_card, other_card) in self.cards.iter().zip(other.cards.iter()) {
match self_card.cmp(other_card) {
Ordering::Equal => continue,
other => return other,
}
}
Ordering::Equal
}
other => other,
},
(Err(_), _) => Ordering::Less,
(_, Err(_)) => Ordering::Greater,
}
}
}
impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl FromStr for Hand {
type Err = HandParseError;
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
let hand: Vec<&str> = s.split_whitespace().collect();
let cards: Result<Vec<Card>, HandParseError> = hand
.first()
.ok_or_else(|| HandParseError::InvalidCardCount)?
.chars()
.map(Card::from_char)
.collect();
let bid = Self::parse_bid(hand.last().ok_or(HandParseError::InvalidCardCount)?)?;
Ok(Self {
cards: cards?
.try_into()
.map_err(|_| HandParseError::InvalidCardCount)?,
bid,
})
}
}
pub fn process(input: &str) -> color_eyre::Result<usize> {
let mut hands: Vec<Hand> = input
.lines()
.map(Hand::from_str)
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|_| Vec::new());
hands.sort();
hands.iter().for_each(|hand| {
println!(
"{:?} \t| {:?}\t| {}",
hand.cards,
hand.combination().unwrap(),
hand.bid
);
});
let total = hands
.iter()
.enumerate()
.map(|(index, hand)| hand.bid * (index + 1))
.sum();
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process() -> color_eyre::Result<()> {
let input = "32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483";
assert_eq!(5905, process(input)?);
Ok(())
}
}