Day 7 part 1

This commit is contained in:
Pascal Phelipot 2023-12-11 20:38:55 +01:00
parent 958a8f29e2
commit d7c0ce9190
3 changed files with 299 additions and 19 deletions

View File

@ -60,17 +60,10 @@ pub fn solve(lines: Vec<String>, part: Part) -> u64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use log::info; use super::*;
use crate::{day6::solve_race, utils::init_logger_test};
fn init(){
init_logger_test();
}
#[test] #[test]
fn test_solve_race() { fn test_solve_race() {
init();
assert_eq!(solve_race(9, 7), 4); assert_eq!(solve_race(9, 7), 4);
assert_eq!(solve_race(40, 15), 8); assert_eq!(solve_race(40, 15), 8);
assert_eq!(solve_race(200, 30), 9); assert_eq!(solve_race(200, 30), 9);

View File

@ -1,23 +1,298 @@
use std::{cmp::Ordering, collections::HashMap};
use super::Part; use super::Part;
use log::{debug, info, trace}; use log::{debug, info, trace};
use regex::Regex; use regex::Regex;
pub fn solve(lines: Vec<String>, part: Part) -> u64 { #[allow(dead_code)]
todo!() #[derive(PartialEq, Eq, Debug, Hash, Clone)]
enum Value {
A,
K,
Q,
J,
T,
Number(u8)
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Self::A, Self::A) => Some(Ordering::Equal),
(_, Self::A) => Some(Ordering::Less),
(Self::A, _) => Some(Ordering::Greater),
(Self::K, Self::K) => Some(Ordering::Equal),
(_, Self::K) => Some(Ordering::Less),
(Self::K, _) => Some(Ordering::Greater),
(Self::Q, Self::Q) => Some(Ordering::Equal),
(_, Self::Q) => Some(Ordering::Less),
(Self::Q, _) => Some(Ordering::Greater),
(Self::J, Self::J) => Some(Ordering::Equal),
(_, Self::J) => Some(Ordering::Less),
(Self::J, _) => Some(Ordering::Greater),
(Self::T, Self::T) => Some(Ordering::Equal),
(_, Self::T) => Some(Ordering::Less),
(Self::T, _) => Some(Ordering::Greater),
(Self::Number(n_self), Self::Number(n_other)) => n_self.partial_cmp(n_other)
}
}
}
impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
#[allow(dead_code)]
#[derive(Debug)]
enum Type {
FiveOfAKind(Value),
FourOfAKind(Value),
FullHouse(Value, Value),
ThreeOfAKind(Value),
TwoPair(Value, Value),
Pair(Value),
HighCard(Value)
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
matches!((self, other), (Self::FiveOfAKind(_), Self::FiveOfAKind(_)) | (Self::FourOfAKind(_), Self::FourOfAKind(_)) | (Self::ThreeOfAKind(_), Self::ThreeOfAKind(_)) | (Self::Pair(_), Self::Pair(_)) | (Self::HighCard(_), Self::HighCard(_)) | (Self::FullHouse(_, _), Self::FullHouse(_, _)) | (Self::TwoPair(_, _), Self::TwoPair(_, _)))
}
}
impl Eq for Type {
}
impl PartialOrd for Type {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::FiveOfAKind(v), Self::FiveOfAKind(o)) => v.partial_cmp(o),
(Self::FiveOfAKind(_), _) => Some(Ordering::Greater),
(_, Self::FiveOfAKind(_)) => Some(Ordering::Less),
(Self::FourOfAKind(v), Self::FourOfAKind(o)) => v.partial_cmp(o),
(Self::FourOfAKind(_), _) => Some(Ordering::Greater),
(_, Self::FourOfAKind(_)) => Some(Ordering::Less),
(Self::FullHouse(v, v2), Self::FullHouse(o, o2)) => {
let cmp = v.partial_cmp(o);
if let Some(Ordering::Equal) = cmp {
v2.partial_cmp(o2)
}else{
cmp
}
},
(Self::FullHouse(_, _), _) => Some(Ordering::Greater),
(_, Self::FullHouse(_, _)) => Some(Ordering::Less),
(Self::ThreeOfAKind(v), Self::ThreeOfAKind(o)) => v.partial_cmp(o),
(Self::ThreeOfAKind(_), _) => Some(Ordering::Greater),
(_, Self::ThreeOfAKind(_)) => Some(Ordering::Less),
(Self::TwoPair(v, v2), Self::TwoPair(o, o2)) => {
let cmp = v.partial_cmp(o);
if let Some(Ordering::Equal) = cmp {
v2.partial_cmp(o2)
}else{
cmp
}
},
(Self::TwoPair(_, _), _) => Some(Ordering::Greater),
(_, Self::TwoPair(_, _)) => Some(Ordering::Less),
(Self::Pair(v), Self::Pair(o)) => v.partial_cmp(o),
(Self::Pair(_), _) => Some(Ordering::Greater),
(_, Self::Pair(_)) => Some(Ordering::Less),
(Self::HighCard(v), Self::HighCard(o)) => v.partial_cmp(o),
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct Hand {
cards: [Value; 5],
kind: Type,
bid: u64,
}
impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.kind == other.kind {
for (v, o) in self.cards.iter().zip(other.cards.iter()) {
match v.cmp(o) {
Ordering::Greater => return Some(Ordering::Greater),
Ordering::Less => return Some(Ordering::Less),
_ => {},
}
};
Some(Ordering::Equal)
}else{
self.kind.partial_cmp(&other.kind)
}
}
}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl Value {
fn parse_char(c: char) -> Option<Value> {
match c {
'A' => Some(Value::A),
'K' => Some(Value::K),
'Q' => Some(Value::Q),
'J' => Some(Value::J),
'T' => Some(Value::T),
'2'..='9' => Some(Value::Number(c.to_digit(10).unwrap() as u8)),
_ => None
}
}
}
fn get_type_hand(cards: &[Value; 5]) -> Type {
// Get the count of each type of card
let counts = cards.iter().fold(HashMap::new(), |mut acc, e| {
let counter = acc.entry(e).or_insert(0);
*counter += 1;
acc
});
let mut counts : Vec<_> = counts.iter().collect();
counts.sort_by(|a,b| a.1.cmp(b.1));
counts.reverse();
debug!("# Cards {:?}", counts);
let (v, c) = counts.first().unwrap();
// TODO: make it an accumulator ?
if **c == 5 {
Type::FiveOfAKind((***v).clone())
}else if **c == 4 {
Type::FourOfAKind((***v).clone())
}else if let Some(second_part) = counts.get(1) && **c == 3 && *second_part.1 == 2{
Type::FullHouse((***v).clone(), (**second_part.0).clone())
}else if **c == 3 {
Type::ThreeOfAKind((***v).clone())
}else if let Some(second_part) = counts.get(1) && **c == 2 && *second_part.1 == 2 {
Type::TwoPair((***v).clone(), (**second_part.0).clone())
}else if **c == 2 {
Type::Pair((***v).clone())
}else{
Type::HighCard((***v).clone())
}
}
fn parse(line: &str) -> Hand {
let (hand, bid) = line.split_once(' ').unwrap();
let cards = hand.chars().map(|c| Value::parse_char(c).unwrap()).collect::<Vec<Value>>().try_into().unwrap();
let bid = bid.parse().unwrap();
let kind = get_type_hand(&cards);
Hand {
cards,
kind,
bid
}
}
pub fn solve(lines: Vec<String>, _: Part) -> u64 {
let mut list : Vec<Hand> = lines.iter().map(|v| parse(v)).collect();
list.sort();
debug!("{:?}", list);
let mut sum: u64 = 0;
for (rank, card) in list.iter().enumerate() {
sum += (rank as u64 + 1 ) * card.bid;
}
sum
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use log::info; use log::info;
use crate::{day6::solve_race, utils::init_logger_test}; use crate::utils::init_logger_test;
use super::*;
use super::Value::*;
fn init(){ fn init(){
init_logger_test(); init_logger_test();
} }
#[test] #[test]
fn test_solve() { fn test_parse_hands() {
init(); init();
let h1 = "32T3K 765";
let o1 = Hand{
cards: [
Number(3),
Number(2),
T,
Number(3),
K
],
kind: Type::Pair(Number(3)),
bid: 765,
};
let h2 = "T55J5 684";
let o2 = Hand {
cards: [
T,
Number(5),
Number(5),
J,
Number(5),
],
bid: 684,
kind: Type::ThreeOfAKind(Number(5))
};
let h3 = "QQQJA 483";
let o3 = Hand {
cards: [
Q, Q, Q, J, A
],
kind: Type::ThreeOfAKind(Q),
bid: 483
};
let h4 = "KK677 28";
let o4 = Hand {
cards: [
K, K, Number(6), Number(7), Number(7)
],
kind: Type::TwoPair(K, Number(7)),
bid: 28
};
info!("Hand 1 parsed {:?}", parse(h1));
info!("Hand 1 reference {:?}", o1);
assert_eq!(parse(h1), o1);
assert_eq!(parse(h2), o2);
assert_eq!(parse(h3), o3);
assert_eq!(parse(h4), o4);
}
#[test]
fn test_parse_lines_and_all() {
let lines = "32T3K 765\nT55J5 684\nKK677 28\nKTJJT 220\nQQQJA 483".split('\n').map(|v| v.to_owned()).collect();
let result = solve(lines, Part::One);
assert_eq!(result, 6440);
} }
} }

View File

@ -1,4 +1,5 @@
#![feature(let_chains)] #![feature(let_chains)]
#![feature(array_zip)]
#![feature(string_remove_matches)] #![feature(string_remove_matches)]
mod day1; mod day1;
@ -9,6 +10,7 @@ mod day5;
mod day6; mod day6;
mod day7; mod day7;
mod utils; mod utils;
use log::info;
use utils::Part; use utils::Part;
@ -21,7 +23,9 @@ fn main() {
.init(); .init();
let data = utils::lines_from_file("./datasets/adventofcode.com_2023_day_7_input.txt").expect("Can't load the data"); let data = utils::lines_from_file("./datasets/adventofcode.com_2023_day_7_input.txt").expect("Can't load the data");
//let data = utils::lines_from_file("./datasets/test.txt").expect("Can't load the data"); let result = day7::solve(data, Part::One);
info!("Result : {}", result);
} }
@ -53,8 +57,8 @@ mod tests {
#[test] #[test]
fn day3() { fn day3() {
let data_path = "./datasets/adventofcode.com_2023_day_3_input.txt"; let data_path = "./datasets/adventofcode.com_2023_day_3_input.txt";
let result = day3::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::One); let result = day3::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::One);
let result2 = day3::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); let result2 = day3::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::Two);
assert_eq!(result, 522726); assert_eq!(result, 522726);
assert_eq!(result2, 81721933); assert_eq!(result2, 81721933);
@ -63,8 +67,8 @@ mod tests {
#[test] #[test]
fn day4() { fn day4() {
let data_path = "./datasets/adventofcode.com_2023_day_4_input.txt"; let data_path = "./datasets/adventofcode.com_2023_day_4_input.txt";
let result = day4::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::One); let result = day4::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::One);
let result2 = day4::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); let result2 = day4::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::Two);
assert_eq!(result, 20407); assert_eq!(result, 20407);
assert_eq!(result2, 23806951); assert_eq!(result2, 23806951);
} }
@ -72,9 +76,17 @@ mod tests {
#[test] #[test]
fn day6() { fn day6() {
let data_path = "./datasets/adventofcode.com_2023_day_6_input.txt"; let data_path = "./datasets/adventofcode.com_2023_day_6_input.txt";
let result = day6::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::One); let result = day6::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::One);
let result2 = day6::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); let result2 = day6::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::Two);
assert_eq!(result, 2612736); assert_eq!(result, 2612736);
assert_eq!(result2, 29891250); assert_eq!(result2, 29891250);
} }
#[test]
fn day7() {
let data_path = "./datasets/adventofcode.com_2023_day_7_input.txt";
let result = day7::solve(utils::lines_from_file(data_path).expect("Could not load the dataset"), Part::One);
assert_eq!(result, 253866470);
}
} }