1
0
Fork 0
aoc2025/src/bin/10.rs
Lars Martens 9abfac63da
Replace assert_example!
It does not provide much value and cannot be used in some cases.
2025-12-12 07:40:25 +01:00

144 lines
3.6 KiB
Rust

use microlp::{ComparisonOp, OptimizationDirection, Problem};
use std::collections::VecDeque;
fn main() {
let input = include_str!("../../input/10");
println!("Part 1: {}", part1(input));
println!("Part 2: {}", part2(input));
}
#[test]
fn example() {
let input = include_str!("../../input/10-test");
assert_eq!(part1(input), 7);
assert_eq!(part2(input), 33);
}
fn part1(input: &str) -> usize {
parse_input(input).iter().map(minimum_light_presses).sum()
}
fn part2(input: &str) -> usize {
parse_input(input).iter().map(minimum_joltage_presses).sum()
}
#[derive(Default, Debug)]
struct Machine {
required_lights: Vec<bool>,
buttons: Vec<Button>,
required_joltages: Vec<usize>,
}
impl Machine {
fn parse(input: &str) -> Self {
let mut m = Machine::default();
for part in input.split_whitespace() {
if part.starts_with('[') {
m.required_lights = parse_lights(part);
} else if part.starts_with('(') {
m.buttons.push(parse_button(part));
} else if part.starts_with('{') {
m.required_joltages = parse_joltages(part);
} else {
panic!("Unknown part '{}'", part);
}
}
m
}
}
fn minimum_light_presses(machine: &Machine) -> usize {
let mut work = VecDeque::new();
work.push_back((vec![false; machine.required_lights.len()], 0usize));
while let Some((lights, presses)) = work.pop_front() {
let matches = lights.iter().eq(&machine.required_lights);
if matches {
return presses;
}
let new_lights = machine
.buttons
.iter()
.map(|b| (press_light(&lights, b), presses + 1));
work.extend(new_lights);
}
unreachable!()
}
fn minimum_joltage_presses(machine: &Machine) -> usize {
let mut problem = Problem::new(OptimizationDirection::Minimize);
let mut buttons_vars = Vec::new();
for _ in 0..machine.buttons.len() {
let var = problem.add_integer_var(1.0, (0, i32::MAX));
buttons_vars.push(var);
}
for (i, joltage) in machine.required_joltages.iter().copied().enumerate() {
let vars = buttons_vars
.iter()
.copied()
.zip(&machine.buttons)
.filter(|(_var, button)| button.contains(&i))
.map(|(var, _button)| (var, 1.0));
problem.add_constraint(vars, ComparisonOp::Eq, joltage as f64);
}
let solution = problem.solve().unwrap();
buttons_vars
.into_iter()
.map(|var| solution[var].round() as usize)
.sum()
}
fn press_light(lights: &[bool], button: &Button) -> Vec<bool> {
let mut lights = lights.to_vec();
for &b in button {
lights[b] ^= true;
}
lights
}
fn parse_lights(input: &str) -> Vec<bool> {
input
.strip_prefix('[')
.unwrap()
.strip_suffix(']')
.unwrap()
.chars()
.map(|c| match c {
'.' => false,
'#' => true,
other => panic!("unknown light char '{other}'"),
})
.collect()
}
type Button = Vec<usize>;
fn parse_button(input: &str) -> Button {
input
.strip_prefix('(')
.unwrap()
.strip_suffix(')')
.unwrap()
.split(',')
.map(|c| c.parse().unwrap())
.collect()
}
fn parse_joltages(input: &str) -> Vec<usize> {
input
.strip_prefix('{')
.unwrap()
.strip_suffix('}')
.unwrap()
.split(',')
.map(|c| c.parse().unwrap())
.collect()
}
fn parse_input(input: &str) -> Vec<Machine> {
input.lines().map(Machine::parse).collect()
}