2025 day 10 part 2 in Rust

Using a library. I'm not happy about it but also I have thought about
this enough.
This commit is contained in:
2025-12-11 21:46:15 +01:00
parent a8a1c85498
commit bc6f3dc8c6
2 changed files with 47 additions and 8 deletions

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
microlp = "0.2.11"

View File

@@ -3,7 +3,12 @@ use std::env;
use std::fs; use std::fs;
use std::io; use std::io;
fn parse_line(line: &str) -> (u32, Vec<u32>) { use microlp::ComparisonOp;
use microlp::LinearExpr;
use microlp::OptimizationDirection;
use microlp::Problem;
fn parse_line(line: &str) -> (u32, Vec<u32>, Vec<u8>) {
let mut buttons = vec![]; let mut buttons = vec![];
let mut target = 0; let mut target = 0;
@@ -38,7 +43,35 @@ fn parse_line(line: &str) -> (u32, Vec<u32>) {
buttons.push(button); buttons.push(button);
} }
(target, buttons) let rem = it.as_str().trim().trim_end_matches('}');
let joltage = rem.split(',').map(|j| j.parse().unwrap()).collect();
(target, buttons, joltage)
}
fn min_joltage(buttons: &[u32], joltage: &[u8]) -> i32 {
let mut problem = Problem::new(OptimizationDirection::Minimize);
let max = i32::from(*joltage.iter().max().unwrap_or(&0));
let variables: Vec<_> = buttons
.iter()
.map(|_| problem.add_integer_var(1.0, (0, max)))
.collect();
for (bit, &value) in joltage.iter().enumerate() {
let mut equation = LinearExpr::empty();
for (&button, &variable) in buttons.iter().zip(&variables) {
if button & (1 << bit) != 0 {
equation.add(variable, 1.0);
}
}
problem.add_constraint(equation, ComparisonOp::Eq, value.into());
}
problem.solve().unwrap().objective().round() as i32
} }
fn minimum_clicks(target: u32, buttons: &[u32]) -> i32 { fn minimum_clicks(target: u32, buttons: &[u32]) -> i32 {
@@ -68,21 +101,24 @@ fn minimum_clicks(target: u32, buttons: &[u32]) -> i32 {
unreachable!("Did not find target"); unreachable!("Did not find target");
} }
fn solve(input: &str) -> i32 { fn solve(input: &str) -> (i32, i32) {
let mut total_clicks = 0; let mut total_clicks = 0;
let mut total_presses = 0;
for line in input.trim().lines() { for line in input.trim().lines() {
let (target, buttons) = parse_line(line); let (target, buttons, joltage) = parse_line(line);
total_clicks += minimum_clicks(target, &buttons); total_clicks += minimum_clicks(target, &buttons);
total_presses += min_joltage(&buttons, &joltage)
} }
total_clicks (total_clicks, total_presses)
} }
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
if let Some(path) = env::args_os().nth(1) { if let Some(path) = env::args_os().nth(1) {
let input = fs::read_to_string(path)?; let input = fs::read_to_string(path)?;
println!("{}", solve(&input)); let (part1, part2) = solve(&input);
println!("Part 1: {part1}\nPart 2: {part2}");
Ok(()) Ok(())
} else { } else {
eprintln!("Usage: {} INPUT_FILE", env::args().next().unwrap()); eprintln!("Usage: {} INPUT_FILE", env::args().next().unwrap());
@@ -97,7 +133,9 @@ mod tests {
const SAMPLE: &str = include_str!("../sample.txt"); const SAMPLE: &str = include_str!("../sample.txt");
#[test] #[test]
fn test_part1() { fn test_sample() {
assert_eq!(7, solve(SAMPLE)); let (part1, part2) = solve(SAMPLE);
assert_eq!(7, part1);
assert_eq!(33, part2);
} }
} }