Day 13
This commit is contained in:
parent
499c2ab588
commit
9784fa357e
1 changed files with 159 additions and 0 deletions
159
src/bin/day13.rs
Normal file
159
src/bin/day13.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use aoc_2024::{load, print_res};
|
||||||
|
use bstr::BString;
|
||||||
|
use color_eyre::eyre::{bail, Context};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Point {
|
||||||
|
x: i64,
|
||||||
|
y: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Crane {
|
||||||
|
a: Point,
|
||||||
|
b: Point,
|
||||||
|
prize: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parsed = Vec<Crane>;
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
|
||||||
|
fn parse_point(operation: char, s: &str) -> color_eyre::Result<Point> {
|
||||||
|
let Some((x, y)) = s.split_once(", ") else {
|
||||||
|
bail!("Missing separator in `{s}`")
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(x) = x.strip_prefix('X').and_then(|s| s.strip_prefix(operation)) else {
|
||||||
|
bail!("Missing X in `${x}`")
|
||||||
|
};
|
||||||
|
let Some(y) = y.strip_prefix("Y").and_then(|s| s.strip_prefix(operation)) else {
|
||||||
|
bail!("Missing Y in `${y}`")
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Point {
|
||||||
|
x: x.parse().with_context(|| format!("Invalid X: `{x}`"))?,
|
||||||
|
y: y.parse().with_context(|| format!("Invalid Y: `{y}`"))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
std::str::from_utf8(input)?
|
||||||
|
.split("\n\n")
|
||||||
|
.map(|desc| {
|
||||||
|
let Some((a, rest)) = desc.trim().split_once('\n') else {
|
||||||
|
bail!("Missing first linebreak")
|
||||||
|
};
|
||||||
|
let Some((b, prize)) = rest.split_once('\n') else {
|
||||||
|
bail!("Missing second linebreak")
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(a) = a.strip_prefix("Button A: ") else {
|
||||||
|
bail!("Invalid button A: `{a}`")
|
||||||
|
};
|
||||||
|
let Some(b) = b.strip_prefix("Button B: ") else {
|
||||||
|
bail!("Invalid button B: `{b}`")
|
||||||
|
};
|
||||||
|
let Some(prize) = prize.strip_prefix("Prize: ") else {
|
||||||
|
bail!("Invalid prize: `{prize}`")
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Crane {
|
||||||
|
a: parse_point('+', a)?,
|
||||||
|
b: parse_point('+', b)?,
|
||||||
|
prize: parse_point('=', prize)?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crane {
|
||||||
|
pub fn presses(&self) -> Option<(i64, i64)> {
|
||||||
|
// X = a * AX + b * BX
|
||||||
|
// Y = a * AY + b * BY
|
||||||
|
//
|
||||||
|
// M = ( AX BX ) T = ( X ) B = ( a )
|
||||||
|
// ( AY BY ) ( Y ) ( b )
|
||||||
|
//
|
||||||
|
// M * B = ( a * AX + b * BX ) = T
|
||||||
|
// ( a * AY + b * BY )
|
||||||
|
// B = M^-1 * T
|
||||||
|
|
||||||
|
let det = self.a.x * self.b.y - self.b.x * self.a.y;
|
||||||
|
if det == 0 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// M^-1 = 1/det * ( BY -BX )
|
||||||
|
// ( -AY AX )
|
||||||
|
//
|
||||||
|
// M^-1 * T = 1/det * ( BY * X - BX * Y )
|
||||||
|
// ( -AY * X + AX * Y )
|
||||||
|
|
||||||
|
let raw_a = det.signum() * (self.b.y * self.prize.x - self.b.x * self.prize.y);
|
||||||
|
let raw_b = det.signum() * (-self.a.y * self.prize.x + self.a.x * self.prize.y);
|
||||||
|
|
||||||
|
if raw_a < 0 || raw_a % det != 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw_b < 0 || raw_b % det != 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((raw_a / det.abs(), raw_b / det.abs()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn part1(input: Parsed) {
|
||||||
|
let mut cost = 0;
|
||||||
|
for crane in input {
|
||||||
|
let Some((a, b)) = crane.presses() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
cost += a * 3 + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_res!("Total cost: {cost}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn part2(input: Parsed) {
|
||||||
|
let mut cost = 0;
|
||||||
|
for mut crane in input {
|
||||||
|
crane.prize.x += 10000000000000;
|
||||||
|
crane.prize.y += 10000000000000;
|
||||||
|
|
||||||
|
let Some((a, b)) = crane.presses() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
cost += a * 3 + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_res!("Total cost: {cost}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> color_eyre::Result<()> {
|
||||||
|
let context = load()?;
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let parsed = parsing(&context.input)?;
|
||||||
|
let elapsed = humantime::format_duration(start.elapsed());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
if context.part == 1 {
|
||||||
|
part1(parsed);
|
||||||
|
} else {
|
||||||
|
part2(parsed);
|
||||||
|
}
|
||||||
|
let elapsed_part = humantime::format_duration(start.elapsed());
|
||||||
|
|
||||||
|
println!(" Parsing: {elapsed}");
|
||||||
|
println!(" Solving: {elapsed_part}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue