Merge branch 'master' of https://git.phelipot.me/pascal/advent_of_code_2023
This commit is contained in:
commit
1074c8d5ee
File diff suppressed because it is too large
Load Diff
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/main.rs
38
src/main.rs
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue