Compare commits

...

2 Commits

Author SHA1 Message Date
0a5e5c8798 day10: small performance boost 2025-12-09 22:45:38 -08:00
73b44b1377 day10: part1 2025-12-09 22:13:56 -08:00
4 changed files with 202 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -1,6 +1,6 @@
<!-- AOC TILES BEGIN -->
<h1 align="center">
2025 - 17 ⭐ - Rust
2025 - 18 ⭐ - Rust
</h1>
<a href="src/day1.rs">
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
@@ -29,4 +29,7 @@
<a href="src/day9.rs">
<img src=".aoc_tiles/tiles/2025/09.png" width="161px">
</a>
<a href="src/day10.rs">
<img src=".aoc_tiles/tiles/2025/10.png" width="161px">
</a>
<!-- AOC TILES END -->

197
src/day10.rs Normal file
View File

@@ -0,0 +1,197 @@
use std::{collections::BinaryHeap, hash::Hash, iter::repeat_n};
use aoc_runner_derive::{aoc, aoc_generator};
use regex::Regex;
#[derive(Clone, Debug, Default)]
struct MachineDefinition {
desired: Vec<bool>,
buttons: Vec<Vec<usize>>,
joltages: Vec<u64>,
}
impl MachineDefinition {
fn create<'a>(&'a self) -> Machine<'a> {
Machine {
d: self,
lights: Vec::from_iter(repeat_n(false, self.desired.len())),
}
}
}
impl From<&str> for MachineDefinition {
fn from(value: &str) -> Self {
let parse_re = Regex::new(
r##"\[(?<desired>[.#]+)\] (?<buttons>(\([0-9,]+\) ?)+) \{(?<joltages>[0-9,]+)\}"##,
)
.unwrap();
let parts = parse_re.captures(value).unwrap();
MachineDefinition {
desired: parts["desired"].chars().map(|c| c == '#').collect(),
buttons: parts["buttons"]
.split_ascii_whitespace()
.map(|s| {
s[1..s.len() - 1]
.split(',')
.map(|n| n.parse().unwrap())
.collect()
})
.collect(),
joltages: parts["joltages"]
.split(',')
.map(|n| n.parse().unwrap())
.collect(),
}
}
}
#[derive(Clone, Debug)]
struct Machine<'a> {
d: &'a MachineDefinition,
lights: Vec<bool>,
}
impl<'a> Eq for Machine<'a> {}
impl<'a> PartialEq for Machine<'a> {
fn eq(&self, other: &Self) -> bool {
self.lights == other.lights
}
}
impl<'a> Hash for Machine<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.lights.hash(state)
}
}
impl<'a> Machine<'a> {
/// Get the state after pressing `button`, returns None if the state is as desired
fn press(&self, button: usize) -> Option<Self> {
let mut new_state = self.lights.clone();
for light in &self.d.buttons[button] {
new_state[*light] = !new_state[*light]
}
if new_state == self.d.desired {
None
} else {
Some(Machine {
d: self.d,
lights: new_state,
})
}
}
/// Get the possible states from the current position
fn next_states(&self) -> Vec<(usize, Option<Machine<'a>>)> {
self.d
.buttons
.iter()
.enumerate()
.map(|(i, _but)| (i, self.press(i)))
.collect()
}
}
#[derive(Debug, Clone)]
struct PressSet<'a> {
machine: Machine<'a>,
presses: usize,
}
// NOTE: All compares are reversed so our max heap becomes a min heap
impl<'a> Eq for PressSet<'a> {}
impl<'a> PartialEq for PressSet<'a> {
fn eq(&self, other: &Self) -> bool {
other.presses.eq(&self.presses)
}
}
impl<'a> PartialOrd for PressSet<'a> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for PressSet<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.presses.cmp(&self.presses)
}
}
fn find_best(md: &MachineDefinition) -> usize {
let m = md.create();
let mut to_check = BinaryHeap::new();
for next in m.next_states() {
if let Some(new_m) = next.1 {
to_check.push(PressSet {
presses: 1,
machine: new_m.clone(),
})
} else {
// what we found a solution on the first move?
return 1;
}
}
while let Some(candidate) = to_check.pop() {
let cm = candidate.machine.clone();
for next in cm.next_states() {
let presses = candidate.presses + 1;
if let Some(new_m) = next.1 {
to_check.push(PressSet {
presses,
machine: new_m.clone(),
})
} else {
return presses;
}
}
}
panic!()
}
#[aoc_generator(day10)]
fn parse(input: &str) -> Vec<MachineDefinition> {
input.lines().map(|l| l.into()).collect()
}
#[aoc(day10, part1)]
fn part1(input: &[MachineDefinition]) -> u64 {
input
.iter()
.map(find_best)
// .inspect(|sol| println!(" [{sol:?}]"))
.map(|sol| sol as u64)
.sum()
}
#[aoc(day10, part2)]
fn part2(input: &[MachineDefinition]) -> u64 {
0
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
const EXAMPLE: &str = "[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}";
#[rstest]
#[case(EXAMPLE, 7)]
fn part1_example(#[case] input: &str, #[case] expected: u64) {
assert_eq!(part1(&parse(input)), expected);
}
#[rstest]
#[case(EXAMPLE, 33)]
fn part2_example(#[case] input: &str, #[case] expected: u64) {
assert_eq!(part2(&parse(input)), expected);
}
}

View File

@@ -1,4 +1,5 @@
mod day1;
mod day10;
mod day2;
mod day3;
mod day4;