feat: day 13
This commit is contained in:
parent
0dc10ff73c
commit
75d1327446
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -33,6 +33,7 @@ dependencies = [
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-test",
|
"tracing-test",
|
||||||
|
@ -353,6 +354,26 @@ dependencies = [
|
||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.8"
|
version = "1.1.8"
|
||||||
|
|
|
@ -17,6 +17,7 @@ full = [
|
||||||
"day10",
|
"day10",
|
||||||
"day11",
|
"day11",
|
||||||
"day12",
|
"day12",
|
||||||
|
"day13",
|
||||||
]
|
]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
day1 = []
|
day1 = []
|
||||||
|
@ -31,6 +32,7 @@ day9 = []
|
||||||
day10 = []
|
day10 = []
|
||||||
day11 = []
|
day11 = []
|
||||||
day12 = []
|
day12 = []
|
||||||
|
day13 = []
|
||||||
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# 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"
|
color-eyre = "0.6.3"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
thiserror = "2.0.6"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
tracing-test = "0.2.5"
|
tracing-test = "0.2.5"
|
||||||
|
|
15
inputs/day13/example.txt
Normal file
15
inputs/day13/example.txt
Normal 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
186
src/day13.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ mod day10;
|
||||||
mod day11;
|
mod day11;
|
||||||
#[cfg(feature = "day12")]
|
#[cfg(feature = "day12")]
|
||||||
mod day12;
|
mod day12;
|
||||||
|
#[cfg(feature = "day13")]
|
||||||
|
mod day13;
|
||||||
|
|
||||||
fn main() -> eyre::Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
@ -57,6 +59,8 @@ fn main() -> eyre::Result<()> {
|
||||||
solve_day(11, day11::pt1, day11::pt2);
|
solve_day(11, day11::pt1, day11::pt2);
|
||||||
#[cfg(feature = "day12")]
|
#[cfg(feature = "day12")]
|
||||||
solve_day(12, day12::pt1, day12::pt2);
|
solve_day(12, day12::pt1, day12::pt2);
|
||||||
|
#[cfg(feature = "day13")]
|
||||||
|
solve_day(13, day13::pt1, day13::pt2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue