Skip to content

Commit e5e65fa

Browse files
committed
[2025] Solution for Day 10
1 parent 75b603d commit e5e65fa

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

2025/src/bin/day10.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use aoc2025::days::day10::{part1, part2};
2+
use std::env;
3+
use std::fs;
4+
use std::process;
5+
6+
fn main() {
7+
let args: Vec<String> = env::args().collect();
8+
if args.len() < 2 {
9+
println!("Missing required arguments");
10+
process::exit(1);
11+
}
12+
13+
let contents = fs::read_to_string(&args[1]).expect("Something went wrong");
14+
println!("Part 1: {}", part1(&contents));
15+
println!("Part 2: {}", part2(&contents));
16+
}

2025/src/days.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ pub mod day06;
77
pub mod day07;
88
pub mod day08;
99
pub mod day09;
10+
pub mod day10;
1011
pub mod day11;

2025/src/days/day10.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
use std::{
2+
collections::{HashMap, VecDeque},
3+
vec,
4+
};
5+
6+
pub fn part1(contents: &str) -> i64 {
7+
let mut count = 0;
8+
let diagrams = parse_input(contents);
9+
for diagram in &diagrams {
10+
let combinations = generate_combinations(&diagram.buttons);
11+
let possible_presses = combinations.get(&diagram.lights).unwrap();
12+
count += possible_presses
13+
.iter()
14+
.map(|v| v.len() as i64)
15+
.min()
16+
.unwrap();
17+
}
18+
count
19+
}
20+
21+
pub fn part2(contents: &str) -> i64 {
22+
let mut count = 0;
23+
let diagrams = parse_input(contents);
24+
for diagram in &diagrams {
25+
let mut combinations = generate_combinations(&diagram.buttons);
26+
count += count_presses(&diagram.powers, &mut combinations).unwrap();
27+
}
28+
count
29+
}
30+
31+
fn parse_input(input: &str) -> Vec<Diagram> {
32+
input.lines().map(|line| Diagram::from_line(line)).collect()
33+
}
34+
35+
fn generate_combinations(buttons: &Vec<u32>) -> HashMap<u32, Vec<Vec<u32>>> {
36+
let mut map = HashMap::new();
37+
38+
// Include the empty combination to handle the case of zero lights
39+
map.insert(0, vec![vec![]]);
40+
41+
let mut queue = VecDeque::new();
42+
for (i, button) in buttons.iter().enumerate() {
43+
queue.push_back((*button, i + 1, vec![*button]));
44+
}
45+
46+
while !queue.is_empty() {
47+
let (current_lights, index, presses) = queue.pop_front().unwrap();
48+
map.entry(current_lights)
49+
.or_insert_with(Vec::new)
50+
.push(presses.clone());
51+
52+
for (i, button) in buttons.iter().skip(index).enumerate() {
53+
let mut presses = presses.clone();
54+
presses.push(*button);
55+
queue.push_back((current_lights ^ button, index + i + 1, presses));
56+
}
57+
}
58+
map
59+
}
60+
61+
fn get_parity(powers: &Vec<i32>) -> u32 {
62+
let mut parity: u32 = 0;
63+
for (i, &power) in powers.iter().enumerate() {
64+
if power % 2 == 1 {
65+
parity |= 1 << i;
66+
}
67+
}
68+
parity
69+
}
70+
71+
fn calculate_powers(powers: &Vec<i32>, buttons: &Vec<u32>) -> Option<Vec<i32>> {
72+
let mut powers = powers.clone();
73+
for (i, power) in powers.iter_mut().enumerate() {
74+
for button in buttons {
75+
if (button & (1 << i)) != 0 {
76+
*power -= 1;
77+
if *power < 0 {
78+
// We've gone below zero, no need to continue
79+
return None;
80+
}
81+
}
82+
}
83+
*power /= 2;
84+
}
85+
Some(powers)
86+
}
87+
88+
fn found_solution(powers: &Vec<i32>) -> bool {
89+
for power in powers {
90+
if *power != 0 {
91+
return false;
92+
}
93+
}
94+
true
95+
}
96+
97+
fn count_presses(powers: &Vec<i32>, map: &HashMap<u32, Vec<Vec<u32>>>) -> Option<i64> {
98+
if found_solution(powers) {
99+
return Some(0);
100+
}
101+
102+
let parity = get_parity(powers);
103+
if let Some(possible_presses) = map.get(&parity) {
104+
let mut counts = vec![];
105+
for presses in possible_presses {
106+
if let Some(new_powers) = calculate_powers(powers, &presses) {
107+
if let Some(sub_count) = count_presses(&new_powers, map) {
108+
counts.push(sub_count * 2 + presses.len() as i64);
109+
}
110+
}
111+
}
112+
113+
if let Some(min) = counts.iter().min() {
114+
return Some(*min);
115+
}
116+
}
117+
None
118+
}
119+
120+
struct Diagram {
121+
lights: u32,
122+
buttons: Vec<u32>,
123+
powers: Vec<i32>,
124+
}
125+
126+
impl Diagram {
127+
fn from_line(line: &str) -> Self {
128+
let parts: Vec<&str> = line.split(' ').collect();
129+
let mut lights: u32 = 0;
130+
let mut buttons = vec![];
131+
let mut powers = vec![];
132+
for part in parts {
133+
if part.starts_with('[') {
134+
let mut shift = 0;
135+
for c in part.chars().skip(1).take(part.len() - 2) {
136+
if c == '#' {
137+
lights |= 1 << shift;
138+
}
139+
shift += 1;
140+
}
141+
} else if part.starts_with('(') {
142+
let button_values: Vec<u32> = part[1..part.len() - 1]
143+
.split(',')
144+
.map(|s| s.parse().unwrap())
145+
.collect();
146+
let mut button: u32 = 0;
147+
for v in button_values {
148+
button |= 1 << v;
149+
}
150+
buttons.push(button);
151+
} else if part.starts_with('{') {
152+
let power_values: Vec<i32> = part[1..part.len() - 1]
153+
.split(',')
154+
.map(|s| s.parse().unwrap())
155+
.collect();
156+
powers.extend(power_values);
157+
}
158+
}
159+
Diagram {
160+
lights: lights,
161+
buttons: buttons,
162+
powers: powers,
163+
}
164+
}
165+
}
166+
167+
#[cfg(test)]
168+
mod tests {
169+
use super::*;
170+
171+
#[test]
172+
fn test_part1() {
173+
let input = vec![
174+
"[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}",
175+
"[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}",
176+
"[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}",
177+
];
178+
179+
assert_eq!(part1(&input.join("\n")), 7);
180+
}
181+
182+
#[test]
183+
fn test_part2() {
184+
let input = vec![
185+
"[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}",
186+
"[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}",
187+
"[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}",
188+
];
189+
190+
assert_eq!(part2(&input.join("\n")), 33);
191+
}
192+
}

2025/tests/day10.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use aoc2025::days::day10::{part1, part2};
2+
3+
#[test]
4+
fn test_part1() {
5+
let content = std::include_str!("../../private/inputs/2025/day10.txt");
6+
assert_eq!(part1(&content), 419);
7+
}
8+
9+
#[test]
10+
fn test_part2() {
11+
let content = std::include_str!("../../private/inputs/2025/day10.txt");
12+
assert_eq!(part2(&content), 18369);
13+
}

0 commit comments

Comments
 (0)