add day 7 solution
solved by claude.ai
This commit is contained in:
+139
-1
@@ -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<String, Gate>;
|
||||
|
||||
// ── Parsing ────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn parse_operand(s: &str) -> Operand {
|
||||
match s.parse::<u16>() {
|
||||
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<String, u16>) -> 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<String, u16>) -> 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<String, u16> = 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<String, u16>| 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
|
||||
|
||||
Reference in New Issue
Block a user