mirror of
https://github.com/RussellChamp/advent-of-code-2025-cursor.git
synced 2026-10-27 23:52:34 +00:00
Add Day 10 and Day 11 solutions
Day 10: Indicator light configuration with bitmask brute-force Day 11: Graph path counting with memoized DFS - Part 1: Count all paths from 'you' to 'out' - Part 2: Count paths from 'svr' to 'out' visiting both 'dac' and 'fft'
This commit is contained in:
1 parent
cb01d29665
commit
3cfad8af27
7 files changed
+290
-12
No files matched your search
@@ -11,17 +11,19 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
|
|||||||
|
|
||||||
| Day | Part 1 | Part 2 |
|
| Day | Part 1 | Part 2 |
|
||||||
| :---: | :---: | :---: |
|
| :---: | :---: | :---: |
|
||||||
| [Day 1](./src/bin/01.rs) | `93.9µs` | `113.6µs` |
|
| [Day 1](./src/bin/01.rs) | `96.2µs` | `116.5µs` |
|
||||||
| [Day 2](./src/bin/02.rs) | `125.7µs` | `330.4µs` |
|
| [Day 2](./src/bin/02.rs) | `122.0µs` | `300.3µs` |
|
||||||
| [Day 3](./src/bin/03.rs) | `118.8µs` | `108.4µs` |
|
| [Day 3](./src/bin/03.rs) | `115.6µs` | `122.4µs` |
|
||||||
| [Day 4](./src/bin/04.rs) | `160.6µs` | `838.6µs` |
|
| [Day 4](./src/bin/04.rs) | `232.9µs` | `921.4µs` |
|
||||||
| [Day 5](./src/bin/05.rs) | `53.3µs` | `44.4µs` |
|
| [Day 5](./src/bin/05.rs) | `54.3µs` | `46.6µs` |
|
||||||
| [Day 6](./src/bin/06.rs) | `118.7µs` | `124.8µs` |
|
| [Day 6](./src/bin/06.rs) | `121.5µs` | `141.6µs` |
|
||||||
| [Day 7](./src/bin/07.rs) | `91.8µs` | `72.4µs` |
|
| [Day 7](./src/bin/07.rs) | `76.9µs` | `68.5µs` |
|
||||||
| [Day 8](./src/bin/08.rs) | `11.2ms` | `9.8ms` |
|
| [Day 8](./src/bin/08.rs) | `9.4ms` | `7.6ms` |
|
||||||
| [Day 9](./src/bin/09.rs) | `114.1µs` | `-` |
|
| [Day 9](./src/bin/09.rs) | `125.9µs` | `-` |
|
||||||
|
| [Day 10](./src/bin/10.rs) | `4.8ms` | `-` |
|
||||||
|
| [Day 11](./src/bin/11.rs) | `125.4µs` | `293.5µs` |
|
||||||
|
|
||||||
**Total: 23.51ms**
|
**Total: 24.88ms**
|
||||||
<!--- benchmarking table --->
|
<!--- benchmarking table --->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -6,5 +6,3 @@
|
|||||||
2,5
|
2,5
|
||||||
2,3
|
2,3
|
||||||
7,3
|
7,3
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[.##.] (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}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
svr: aaa bbb
|
||||||
|
aaa: fft
|
||||||
|
fft: ccc
|
||||||
|
bbb: tty
|
||||||
|
tty: ccc
|
||||||
|
ccc: ddd eee
|
||||||
|
ddd: hub
|
||||||
|
hub: fff
|
||||||
|
eee: dac
|
||||||
|
dac: fff
|
||||||
|
fff: ggg hhh
|
||||||
|
ggg: out
|
||||||
|
hhh: out
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
aaa: you hhh
|
||||||
|
you: bbb ccc
|
||||||
|
bbb: ddd eee
|
||||||
|
ccc: ddd eee fff
|
||||||
|
ddd: ggg
|
||||||
|
eee: out
|
||||||
|
fff: out
|
||||||
|
ggg: out
|
||||||
|
hhh: ccc fff iii
|
||||||
|
iii: out
|
||||||
|
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
advent_of_code::solution!(10);
|
||||||
|
|
||||||
|
/// Parse a machine line into (target_bitmask, buttons_bitmasks)
|
||||||
|
fn parse_machine(line: &str) -> Option<(u64, Vec<u64>)> {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the pattern between []
|
||||||
|
let bracket_start = line.find('[')?;
|
||||||
|
let bracket_end = line.find(']')?;
|
||||||
|
let pattern = &line[bracket_start + 1..bracket_end];
|
||||||
|
|
||||||
|
// Convert pattern to target bitmask (# = on = 1, . = off = 0)
|
||||||
|
let mut target: u64 = 0;
|
||||||
|
for (i, c) in pattern.chars().enumerate() {
|
||||||
|
if c == '#' {
|
||||||
|
target |= 1 << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find where joltages start (curly brace)
|
||||||
|
let joltage_start = line.find('{')?;
|
||||||
|
|
||||||
|
// Parse all buttons between ] and {
|
||||||
|
let button_section = &line[bracket_end + 1..joltage_start];
|
||||||
|
let mut buttons = Vec::new();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let chars: Vec<char> = button_section.chars().collect();
|
||||||
|
while i < chars.len() {
|
||||||
|
if chars[i] == '(' {
|
||||||
|
// Find matching )
|
||||||
|
let start = i + 1;
|
||||||
|
while i < chars.len() && chars[i] != ')' {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let button_str: String = chars[start..i].iter().collect();
|
||||||
|
|
||||||
|
// Parse the indices
|
||||||
|
let mut button_mask: u64 = 0;
|
||||||
|
for num_str in button_str.split(',') {
|
||||||
|
if let Ok(idx) = num_str.trim().parse::<usize>() {
|
||||||
|
button_mask |= 1 << idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttons.push(button_mask);
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((target, buttons))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find minimum button presses to reach target state (Part 1: toggle/XOR)
|
||||||
|
fn min_presses_part1(target: u64, buttons: &[u64]) -> u64 {
|
||||||
|
let n = buttons.len();
|
||||||
|
let mut min_presses = u64::MAX;
|
||||||
|
|
||||||
|
// Try all 2^n combinations of buttons (each pressed 0 or 1 times)
|
||||||
|
for mask in 0..(1u64 << n) {
|
||||||
|
let mut result: u64 = 0;
|
||||||
|
let mut presses = 0;
|
||||||
|
|
||||||
|
for (i, &button) in buttons.iter().enumerate() {
|
||||||
|
if mask & (1 << i) != 0 {
|
||||||
|
result ^= button;
|
||||||
|
presses += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == target && presses < min_presses {
|
||||||
|
min_presses = presses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min_presses
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
if let Some((target, buttons)) = parse_machine(line) {
|
||||||
|
let presses = min_presses_part1(target, &buttons);
|
||||||
|
if presses != u64::MAX {
|
||||||
|
total += presses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(total)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(7)); // 2 + 3 + 2 = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
advent_of_code::solution!(11);
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Parse the input into a graph (device -> list of outputs)
|
||||||
|
fn parse_graph(input: &str) -> HashMap<&str, Vec<&str>> {
|
||||||
|
let mut graph: HashMap<&str, Vec<&str>> = HashMap::new();
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parts = line.split(':');
|
||||||
|
let device = parts.next().unwrap().trim();
|
||||||
|
let outputs: Vec<&str> = parts
|
||||||
|
.next()
|
||||||
|
.unwrap_or("")
|
||||||
|
.split_whitespace()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
graph.insert(device, outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count all paths from start to "out" using DFS with memoization
|
||||||
|
fn count_paths<'a>(
|
||||||
|
graph: &HashMap<&'a str, Vec<&'a str>>,
|
||||||
|
current: &'a str,
|
||||||
|
memo: &mut HashMap<&'a str, u64>,
|
||||||
|
) -> u64 {
|
||||||
|
// Base case: reached the destination
|
||||||
|
if current == "out" {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check memoization
|
||||||
|
if let Some(&cached) = memo.get(current) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get outputs for current device
|
||||||
|
let outputs = match graph.get(current) {
|
||||||
|
Some(outputs) => outputs,
|
||||||
|
None => return 0, // Dead end
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sum paths through all outputs
|
||||||
|
let total: u64 = outputs
|
||||||
|
.iter()
|
||||||
|
.map(|&next| count_paths(graph, next, memo))
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
memo.insert(current, total);
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count paths from current to "out" that visit both dac and fft
|
||||||
|
/// State: (node, visited_dac, visited_fft) where visited flags are encoded as bits
|
||||||
|
fn count_paths_through_both<'a>(
|
||||||
|
graph: &HashMap<&'a str, Vec<&'a str>>,
|
||||||
|
current: &'a str,
|
||||||
|
visited_dac: bool,
|
||||||
|
visited_fft: bool,
|
||||||
|
memo: &mut HashMap<(&'a str, bool, bool), u64>,
|
||||||
|
) -> u64 {
|
||||||
|
// Update visited flags based on current node
|
||||||
|
let visited_dac = visited_dac || current == "dac";
|
||||||
|
let visited_fft = visited_fft || current == "fft";
|
||||||
|
|
||||||
|
// Base case: reached the destination
|
||||||
|
if current == "out" {
|
||||||
|
// Only count if we visited both dac and fft
|
||||||
|
return if visited_dac && visited_fft { 1 } else { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check memoization
|
||||||
|
let key = (current, visited_dac, visited_fft);
|
||||||
|
if let Some(&cached) = memo.get(&key) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get outputs for current device
|
||||||
|
let outputs = match graph.get(current) {
|
||||||
|
Some(outputs) => outputs,
|
||||||
|
None => return 0, // Dead end
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sum paths through all outputs
|
||||||
|
let total: u64 = outputs
|
||||||
|
.iter()
|
||||||
|
.map(|&next| count_paths_through_both(graph, next, visited_dac, visited_fft, memo))
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
memo.insert(key, total);
|
||||||
|
total
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
let graph = parse_graph(input);
|
||||||
|
let mut memo: HashMap<&str, u64> = HashMap::new();
|
||||||
|
|
||||||
|
let count = count_paths(&graph, "you", &mut memo);
|
||||||
|
Some(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
let graph = parse_graph(input);
|
||||||
|
let mut memo: HashMap<(&str, bool, bool), u64> = HashMap::new();
|
||||||
|
|
||||||
|
let count = count_paths_through_both(&graph, "svr", false, false, &mut memo);
|
||||||
|
Some(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file_part("examples", DAY, 2));
|
||||||
|
assert_eq!(result, Some(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user