diff --git a/src/day6/mod.rs b/src/day6/mod.rs index 43f1c0b..063d114 100644 --- a/src/day6/mod.rs +++ b/src/day6/mod.rs @@ -60,17 +60,10 @@ pub fn solve(lines: Vec, part: Part) -> u64 { #[cfg(test)] mod tests { - use log::info; - - use crate::{day6::solve_race, utils::init_logger_test}; - fn init(){ - init_logger_test(); - } + use super::*; #[test] fn test_solve_race() { - init(); - assert_eq!(solve_race(9, 7), 4); assert_eq!(solve_race(40, 15), 8); assert_eq!(solve_race(200, 30), 9); diff --git a/src/day7/mod.rs b/src/day7/mod.rs index 169e7e6..e1d9357 100644 --- a/src/day7/mod.rs +++ b/src/day7/mod.rs @@ -1,23 +1,298 @@ +use std::{cmp::Ordering, collections::HashMap}; + use super::Part; use log::{debug, info, trace}; use regex::Regex; -pub fn solve(lines: Vec, part: Part) -> u64 { - todo!() +#[allow(dead_code)] +#[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 { + 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 { + 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 { + 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 { + 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::>().try_into().unwrap(); + let bid = bid.parse().unwrap(); + + let kind = get_type_hand(&cards); + + Hand { + cards, + kind, + bid + } +} + +pub fn solve(lines: Vec, _: Part) -> u64 { + let mut list : Vec = 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)] mod tests { 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(){ init_logger_test(); } #[test] - fn test_solve() { + fn test_parse_hands() { 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); } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1373c0f..4f8db0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(let_chains)] +#![feature(array_zip)] #![feature(string_remove_matches)] mod day1; @@ -9,6 +10,7 @@ mod day5; mod day6; mod day7; mod utils; +use log::info; use utils::Part; @@ -21,7 +23,9 @@ fn main() { .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/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] fn day3() { 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 result2 = day3::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); + 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"), Part::Two); assert_eq!(result, 522726); assert_eq!(result2, 81721933); @@ -63,8 +67,8 @@ mod tests { #[test] fn day4() { 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 result2 = day4::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); + 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"), Part::Two); assert_eq!(result, 20407); assert_eq!(result2, 23806951); } @@ -72,9 +76,17 @@ mod tests { #[test] fn day6() { 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 result2 = day6::solve(utils::lines_from_file(data_path).expect("Could not load the dataset for day 3"), Part::Two); + 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"), Part::Two); assert_eq!(result, 2612736); 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); + } } \ No newline at end of file