Skip to content

Commit 726c27f

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

File tree

4 files changed

+223
-0
lines changed

4 files changed

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

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)