feat: day 13

This commit is contained in:
Lucy 2024-12-16 08:47:10 +01:00
parent 0dc10ff73c
commit 75d1327446
5 changed files with 229 additions and 0 deletions

21
Cargo.lock generated
View file

@ -33,6 +33,7 @@ dependencies = [
"color-eyre",
"rayon",
"regex",
"thiserror",
"tracing",
"tracing-subscriber",
"tracing-test",
@ -353,6 +354,26 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"

View file

@ -17,6 +17,7 @@ full = [
"day10",
"day11",
"day12",
"day13",
]
default = ["full"]
day1 = []
@ -31,6 +32,7 @@ day9 = []
day10 = []
day11 = []
day12 = []
day13 = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -39,6 +41,7 @@ day12 = []
color-eyre = "0.6.3"
rayon = "1.10.0"
regex = "1.11.1"
thiserror = "2.0.6"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
tracing-test = "0.2.5"

15
inputs/day13/example.txt Normal file
View file

@ -0,0 +1,15 @@
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279

186
src/day13.rs Normal file
View file

@ -0,0 +1,186 @@
#![allow(
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::too_many_lines,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::float_cmp
)]
use std::str::FromStr;
use color_eyre::eyre::{self, OptionExt};
struct Elem {
x: usize,
y: usize,
}
struct Machine {
button_a: Elem,
button_b: Elem,
prize: Elem,
}
fn parse_button(s: &str) -> Result<Elem, eyre::Error> {
let positions = s.split_once(", ").ok_or_eyre("Button A line malformed")?;
Ok(Elem {
x: positions
.0
.split_once('+')
.ok_or_eyre("seems to be malformed")?
.1
.parse::<usize>()?,
y: positions
.1
.split_once('+')
.ok_or_eyre("seems to be malformed")?
.1
.parse::<usize>()?,
})
}
impl FromStr for Machine {
type Err = eyre::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lines = s.lines().collect::<Vec<_>>();
let button_a = parse_button(lines.first().ok_or_eyre("Could not find button a line")?)?;
let button_b = parse_button(lines.get(1).ok_or_eyre("could not find button b line")?)?;
let prize_line = lines.get(2).ok_or_eyre("Could not find prize line")?;
let prizes = prize_line
.split_once(", ")
.ok_or_eyre("Could not parse prize line")?;
let prize = Elem {
x: prizes
.0
.split_once('=')
.ok_or_eyre("Could not parse prize")?
.1
.parse::<usize>()?,
y: prizes
.1
.split_once('=')
.ok_or_eyre("Could not parse prize")?
.1
.parse::<usize>()?,
};
Ok(Machine {
button_a,
button_b,
prize,
})
}
}
impl Machine {
pub fn play(&self) -> Option<usize> {
let machine = self;
let determinant = machine.button_a.x as f64 * machine.button_b.y as f64
- machine.button_a.y as f64 * machine.button_b.x as f64;
let a = ((-(machine.button_b.x as f64)) * (machine.prize.y as f64)
+ machine.button_b.y as f64 * machine.prize.x as f64)
/ determinant;
let b = (machine.button_a.x as f64 * machine.prize.y as f64
- machine.button_a.y as f64 * machine.prize.x as f64)
/ determinant;
if a.floor() == a && b.floor() == b && a >= 0.0 && b >= 0.0 {
if a > 100.0 || b > 100.0 {
None
} else {
Some(a.round() as usize * 3 + b as usize)
}
} else {
None
}
}
pub fn play_without_limits(&self) -> Option<usize> {
let machine = self;
let determinant = machine.button_a.x as f64 * machine.button_b.y as f64
- machine.button_a.y as f64 * machine.button_b.x as f64;
let a = ((-(machine.button_b.x as f64)) * (machine.prize.y as f64)
+ machine.button_b.y as f64 * machine.prize.x as f64)
/ determinant;
let b = (machine.button_a.x as f64 * machine.prize.y as f64
- machine.button_a.y as f64 * machine.prize.x as f64)
/ determinant;
if a.floor() == a && b.floor() == b && a >= 0.0 && b >= 0.0 {
Some(a.round() as usize * 3 + b as usize)
} else {
None
}
}
}
pub fn pt1(input: &str) -> usize {
input
.split("\n\n")
.flat_map(Machine::from_str)
.filter_map(|m| m.play())
.sum()
}
pub fn pt2(input: &str) -> usize {
input
.split("\n\n")
.flat_map(Machine::from_str)
.map(|mut m| {
m.prize.x += 10_000_000_000_000;
m.prize.y += 10_000_000_000_000;
m
})
.filter_map(|m| m.play_without_limits())
.sum()
}
#[cfg(test)]
mod tests {
use tracing_test::traced_test;
use super::{pt1, pt2, Machine};
use std::fs::read_to_string;
#[traced_test]
#[test]
pub fn test_pt1() {
let input =
read_to_string("./inputs/day13/example.txt").expect("Missing example.txt for day13");
assert_eq!(pt1(&input), 480)
}
#[traced_test]
#[test]
pub fn test_machine_execution1() {
let m = Machine {
button_a: crate::day13::Elem { x: 94, y: 34 },
button_b: crate::day13::Elem { x: 22, y: 67 },
prize: crate::day13::Elem { x: 8400, y: 5400 },
};
assert_eq!(m.play(), Some(280))
}
#[traced_test]
#[test]
pub fn test_machine_execution2() {
let m = Machine {
button_a: crate::day13::Elem { x: 26, y: 66 },
button_b: crate::day13::Elem { x: 67, y: 21 },
prize: crate::day13::Elem { x: 12748, y: 12176 },
};
assert_eq!(m.play(), None)
}
#[traced_test]
#[test]
pub fn test_machine_execution3() {
let m = Machine {
button_a: crate::day13::Elem { x: 17, y: 86 },
button_b: crate::day13::Elem { x: 84, y: 37 },
prize: crate::day13::Elem { x: 7870, y: 6450 },
};
assert_eq!(m.play(), Some(200))
}
#[traced_test]
#[test]
pub fn test_machine_execution4() {
let m = Machine {
button_a: crate::day13::Elem { x: 69, y: 23 },
button_b: crate::day13::Elem { x: 27, y: 71 },
prize: crate::day13::Elem { x: 18641, y: 10279 },
};
assert_eq!(m.play(), None)
}
}

View file

@ -27,6 +27,8 @@ mod day10;
mod day11;
#[cfg(feature = "day12")]
mod day12;
#[cfg(feature = "day13")]
mod day13;
fn main() -> eyre::Result<()> {
color_eyre::install()?;
@ -57,6 +59,8 @@ fn main() -> eyre::Result<()> {
solve_day(11, day11::pt1, day11::pt2);
#[cfg(feature = "day12")]
solve_day(12, day12::pt1, day12::pt2);
#[cfg(feature = "day13")]
solve_day(13, day13::pt1, day13::pt2);
Ok(())
}