This commit is contained in:
pasterp 2023-12-12 10:03:10 +01:00
commit 1074c8d5ee
5 changed files with 1405 additions and 10 deletions

File diff suppressed because it is too large Load Diff

71
src/day6/mod.rs Normal file
View File

@ -0,0 +1,71 @@
use super::Part;
use log::{debug, info, trace};
use regex::Regex;
fn solve_race(d: u64, t: u64) -> u64 {
info!("Resolving race d={}, t={}", d, t);
let mut count = 0;
for x in 0..t { //TODO: we could definetly do maths (erk) here but i'm in a rush
trace!("If I am holding {}ms the boat will travel {}mm/{}mm ", x, (t-x)*x, d);
if (t-x)*x > d {
trace!("Found a solution: x={}: {} * {} = {}", x, x, t-x, d);
count+=1;
}
}
count
}
pub fn solve(lines: Vec<String>, part: Part) -> u64 {
let mut time_line = lines.get(0).unwrap().to_owned();
let mut distance_line = lines.get(1).unwrap().to_owned();
if let Part::Two = part {
time_line.remove_matches(' ');
distance_line.remove_matches(' ');
}
let re = Regex::new(r"\d+").unwrap();
let times : Vec<u64> = re.captures_iter(&time_line)
.map(|capture| capture.get(0).unwrap().as_str().parse().unwrap())
.collect();
debug!("Current times: {:?}", times);
let distances : Vec<u64> = re.captures_iter(&distance_line)
.map(|capture| capture.get(0).unwrap().as_str().parse().unwrap())
.collect();
debug!("Current distances: {:?}", distances);
assert_eq!(times.len(), distances.len());
// Holding 1 ms => +1 mm/ms
// T: total time
// d: distance
// x: number of seconds holding the button
// x * (T - x) = d
// Tx - x² = d
let mut result = 1;
for n in 0..(times.len()) {
let t = times[n];
let d = distances[n];
result *= solve_race(d, t);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_solve_race() {
assert_eq!(solve_race(9, 7), 4);
assert_eq!(solve_race(40, 15), 8);
assert_eq!(solve_race(200, 30), 9);
}
}

298
src/day7/mod.rs Normal file
View File

@ -0,0 +1,298 @@
use std::{cmp::Ordering, collections::HashMap};
use super::Part;
use log::{debug, info, trace};
use regex::Regex;
#[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<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)]
mod tests {
use log::info;
use crate::utils::init_logger_test;
use super::*;
use super::Value::*;
fn init(){
init_logger_test();
}
#[test]
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);
}
}

View File

@ -1,10 +1,14 @@
#![feature(let_chains)] #![feature(let_chains)]
#![feature(array_zip)]
#![feature(string_remove_matches)]
mod day1; mod day1;
mod day2; mod day2;
mod day3; mod day3;
mod day4; mod day4;
mod day5; mod day5;
mod day6;
mod day7;
mod utils; mod utils;
use log::info; use log::info;
use utils::Part; use utils::Part;
@ -18,13 +22,10 @@ fn main() {
.format_timestamp(None) .format_timestamp(None)
.init(); .init();
let data = utils::lines_from_file("./datasets/adventofcode.com_2023_day_5_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);
let result = day5::solve(data.clone(), Part::One);
let result2 = day5::solve(data, Part::Two);
info!("Result part 1: {}", result); info!("Result : {}", result);
info!("Result part 2: {}", result2);
} }
@ -56,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);
@ -66,9 +67,26 @@ 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);
} }
#[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"), 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);
}
} }

View File

@ -15,3 +15,11 @@ pub enum Part {
One, One,
Two Two
} }
pub fn init_logger_test() {
env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.format_timestamp(None)
.is_test(true)
.init();
}