mirror of
https://github.com/kristoferssolo/Advent-of-Code.git
synced 2025-10-21 18:00:35 +00:00
finished day 06
This commit is contained in:
parent
2da227afa5
commit
fffb94783b
90
2023/;
90
2023/;
@ -1,90 +0,0 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct Record {
|
|
||||||
time: usize,
|
|
||||||
distance: usize,
|
|
||||||
my_distance: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(usize, usize)> for Record {
|
|
||||||
fn from(value: (usize, usize)) -> Self {
|
|
||||||
let (time, distance) = value;
|
|
||||||
Self {
|
|
||||||
time,
|
|
||||||
distance,
|
|
||||||
my_distance: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct Records(Vec<Record>);
|
|
||||||
impl FromStr for Records {
|
|
||||||
type Err = &'static str;
|
|
||||||
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
|
|
||||||
let mut lines = s.lines();
|
|
||||||
let time_values: Vec<_> = lines
|
|
||||||
.next()
|
|
||||||
.and_then(|line| {
|
|
||||||
line.split_whitespace()
|
|
||||||
.skip(1)
|
|
||||||
.map(|s| s.parse().ok())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.ok_or("Invalid input format")?;
|
|
||||||
let distabce_values: Vec<_> = lines
|
|
||||||
.next()
|
|
||||||
.and_then(|line| {
|
|
||||||
line.split_whitespace()
|
|
||||||
.skip(1)
|
|
||||||
.map(|s| s.parse().ok())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.ok_or("Invalid input format")?;
|
|
||||||
let records: Vec<_> = time_values
|
|
||||||
.into_iter()
|
|
||||||
.zip(distabce_values.into_iter())
|
|
||||||
.map(Record::from)
|
|
||||||
.collect();
|
|
||||||
Ok(Records(records))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process(input: &str) -> Result<usize> {
|
|
||||||
let mut records: Records = input.parse().expect("Error parsing input");
|
|
||||||
records.0.iter_mut().for_each(|record| {
|
|
||||||
(0..=record.time).for_each(|time| {
|
|
||||||
let distance = time * (record.time - time);
|
|
||||||
record.my_distance.push(distance);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let total: usize = records
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|record| {
|
|
||||||
record
|
|
||||||
.my_distance
|
|
||||||
.iter()
|
|
||||||
.filter(|&&num| num > record.distance)
|
|
||||||
.count()
|
|
||||||
})
|
|
||||||
.product();
|
|
||||||
dbg!(&total);
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process() -> Result<()> {
|
|
||||||
let input = "Time: 7 15 30
|
|
||||||
Distance: 9 40 200";
|
|
||||||
assert_eq!(288, process(input)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
2023/Cargo.lock
generated
63
2023/Cargo.lock
generated
@ -175,6 +175,14 @@ dependencies = [
|
|||||||
"color-eyre",
|
"color-eyre",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day-07"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"color-eyre",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@ -316,6 +324,24 @@ version = "0.2.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -357,6 +383,37 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.7"
|
version = "1.1.7"
|
||||||
@ -408,6 +465,12 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@ -9,3 +9,4 @@ itertools = "0.12.0"
|
|||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
nom-supreme = "0.8.0"
|
nom-supreme = "0.8.0"
|
||||||
rayon = "1.8.0"
|
rayon = "1.8.0"
|
||||||
|
thiserror = "1.0.50"
|
||||||
|
|||||||
@ -6,4 +6,4 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
color-eyre = { workspace = true }
|
color-eyre.workspace = true
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
|
||||||
pub fn process(input: &str) -> Result<u32> {
|
pub fn process(input: &str) -> Result<usize> {
|
||||||
todo!("day xx - part 1");
|
todo!("day xx - part 1");
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
|
||||||
pub fn process(input: &str) -> Result<u32> {
|
pub fn process(input: &str) -> Result<usize> {
|
||||||
todo!("day xx - part 2");
|
todo!("day xx - part 2");
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|||||||
10
2023/day-07/Cargo.toml
Normal file
10
2023/day-07/Cargo.toml
Normal 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
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
1000
2023/day-07/input2.txt
Normal file
File diff suppressed because it is too large
Load Diff
9
2023/day-07/src/bin/part1.rs
Normal file
9
2023/day-07/src/bin/part1.rs
Normal 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(())
|
||||||
|
}
|
||||||
9
2023/day-07/src/bin/part2.rs
Normal file
9
2023/day-07/src/bin/part2.rs
Normal 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
19
2023/day-07/src/error.rs
Normal 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
3
2023/day-07/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod part1;
|
||||||
|
pub mod part2;
|
||||||
188
2023/day-07/src/part1.rs
Normal file
188
2023/day-07/src/part1.rs
Normal 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
196
2023/day-07/src/part2.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user