From 037c0d649c5ff0e672ef1cf0920108c02de0650f Mon Sep 17 00:00:00 2001 From: timeshifter Date: Sat, 13 Jun 2026 17:54:34 +0200 Subject: [PATCH] add day 7 solution solved by claude.ai --- day07/Cargo.toml | 2 - day07/src/main.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/day07/Cargo.toml b/day07/Cargo.toml index 29d44f3..62efbb8 100644 --- a/day07/Cargo.toml +++ b/day07/Cargo.toml @@ -2,5 +2,3 @@ name = "day07" version = "0.1.0" edition = "2024" - -[dependencies] diff --git a/day07/src/main.rs b/day07/src/main.rs index a40c6f5..9eb14d6 100644 --- a/day07/src/main.rs +++ b/day07/src/main.rs @@ -1,5 +1,143 @@ +use std::collections::HashMap; + +// An operand is either a hard-coded 16-bit value or a reference to another wire. +#[derive(Debug, Clone)] +enum Operand { + Literal(u16), + Wire(String), +} + +// Each gate variant carries its operand(s) as parsed from the instruction. +#[derive(Debug, Clone)] +enum Gate { + Signal(Operand), + Not(Operand), + And(Operand, Operand), + Or(Operand, Operand), + LShift(Operand, u16), + RShift(Operand, u16), +} + +type Circuit = HashMap; + +// ── Parsing ──────────────────────────────────────────────────────────────────── + +fn parse_operand(s: &str) -> Operand { + match s.parse::() { + Ok(n) => Operand::Literal(n), + Err(_) => Operand::Wire(s.to_string()), + } +} + +fn parse_line(line: &str) -> (String, Gate) { + let (expr, target) = line + .split_once(" -> ") + .expect("every line must contain ' -> '"); + + let tokens: Vec<&str> = expr.split_whitespace().collect(); + + let gate = match tokens.as_slice() { + [a] => Gate::Signal(parse_operand(a)), + ["NOT", a] => Gate::Not(parse_operand(a)), + [a, "AND", b] => Gate::And(parse_operand(a), parse_operand(b)), + [a, "OR", b] => Gate::Or(parse_operand(a), parse_operand(b)), + [a, "LSHIFT", n] => Gate::LShift(parse_operand(a), n.parse().expect("shift amount")), + [a, "RSHIFT", n] => Gate::RShift(parse_operand(a), n.parse().expect("shift amount")), + _ => panic!("unrecognised expression: {expr}"), + }; + + (target.to_string(), gate) +} + +// ── Evaluation ───────────────────────────────────────────────────────────────── + +// Resolves a single operand: literals are returned as-is, wire names recurse +// into `evaluate`. +fn resolve(op: &Operand, circuit: &Circuit, cache: &mut HashMap) -> u16 { + match op { + Operand::Literal(n) => *n, + Operand::Wire(name) => evaluate(name, circuit, cache), + } +} + +// Returns the 16-bit signal on `wire`, computing it recursively if not yet +// cached. The gate is cloned before recursing so that the immutable borrow +// of `circuit` is released before we mutably borrow `cache` deeper in the +// call stack — keeping the borrow checker happy without needing unsafe code. +fn evaluate(wire: &str, circuit: &Circuit, cache: &mut HashMap) -> u16 { + if let Some(&val) = cache.get(wire) { + return val; + } + + let gate = circuit + .get(wire) + .unwrap_or_else(|| panic!("unknown wire: {wire}")) + .clone(); + + let val = match &gate { + Gate::Signal(a) => resolve(a, circuit, cache), + Gate::Not(a) => !resolve(a, circuit, cache), + Gate::And(a, b) => resolve(a, circuit, cache) & resolve(b, circuit, cache), + Gate::Or(a, b) => resolve(a, circuit, cache) | resolve(b, circuit, cache), + Gate::LShift(a, n) => resolve(a, circuit, cache) << n, + Gate::RShift(a, n) => resolve(a, circuit, cache) >> n, + }; + + cache.insert(wire.to_string(), val); + val +} + +// ── Entry point ──────────────────────────────────────────────────────────────── + fn main() { - println!("Hello, world!"); + let input = std::fs::read_to_string("input.txt").expect("could not read input.txt"); + + let circuit: Circuit = input + .lines() + .filter(|l| !l.is_empty()) + .map(parse_line) + .collect(); + + let mut cache: HashMap = HashMap::new(); + println!("{}", evaluate("a", &circuit, &mut cache)); +} + +// ── Tests ────────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + fn build_example_circuit() -> Circuit { + let src = "\ +123 -> x +456 -> y +x AND y -> d +x OR y -> e +x LSHIFT 2 -> f +y RSHIFT 2 -> g +NOT x -> h +NOT y -> i"; + src.lines().map(parse_line).collect() + } + + #[test] + fn test_example_signals() { + let circuit = build_example_circuit(); + let mut cache = HashMap::new(); + + // Evaluate every wire once; the cache is shared across all calls. + let get = |w: &str, cache: &mut HashMap| evaluate(w, &circuit, cache); + + assert_eq!(get("d", &mut cache), 72); + assert_eq!(get("e", &mut cache), 507); + assert_eq!(get("f", &mut cache), 492); + assert_eq!(get("g", &mut cache), 114); + assert_eq!(get("h", &mut cache), 65412); + assert_eq!(get("i", &mut cache), 65079); + assert_eq!(get("x", &mut cache), 123); + assert_eq!(get("y", &mut cache), 456); + } } // https://adventofcode.com/2015/day/7