add day 7 solution
solved by claude.ai
This commit is contained in:
@@ -2,5 +2,3 @@
|
|||||||
name = "day07"
|
name = "day07"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|||||||
+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() {
|
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
|
// https://adventofcode.com/2015/day/7
|
||||||
|
|||||||
Reference in New Issue
Block a user