mirror of
https://github.com/Noratrieb/advent-of-code.git
synced 2026-01-14 09:35:01 +01:00
start part 2
This commit is contained in:
parent
4da0b11cab
commit
eb8f0855bf
1 changed files with 258 additions and 2 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use helper::{Day, IteratorExt, Variants};
|
||||
|
||||
|
|
@ -121,7 +121,263 @@ fn part1(input: &str) -> u64 {
|
|||
result
|
||||
}
|
||||
|
||||
fn part2(_input: &str) -> u64 {
|
||||
fn part2(input: &str) -> u64 {
|
||||
/*
|
||||
Adding two binary numbers
|
||||
|
||||
LSB:
|
||||
X Y
|
||||
---|---|---
|
||||
| |
|
||||
+---|--+
|
||||
(^)--+ |
|
||||
| (&)-+
|
||||
| |
|
||||
---|---|---
|
||||
Z Carry
|
||||
|
||||
|
||||
other bits:
|
||||
X Y Carry
|
||||
---|---|---|---
|
||||
| | |
|
||||
| +---|-----+
|
||||
+---|---|--+ |
|
||||
| | | | |
|
||||
(^)--+ | +-(&)
|
||||
| | |
|
||||
+---(&)-+ |
|
||||
| | | |
|
||||
(^)------+ |
|
||||
| | |
|
||||
| (|)-------+
|
||||
| ++
|
||||
---|---|---
|
||||
Z Carry
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Wire<'a> {
|
||||
X(u8),
|
||||
Y(u8),
|
||||
Z(u8),
|
||||
Intermediate(&'a str),
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Value {
|
||||
Unknown,
|
||||
Op(Op, usize, usize),
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum Op {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
}
|
||||
|
||||
let mut wires = Vec::new();
|
||||
let mut wire_names = Vec::new();
|
||||
let mut wire_by_name = HashMap::new();
|
||||
|
||||
fn intern<'i>(
|
||||
wires: &mut Vec<Value>,
|
||||
wire_names: &mut Vec<Wire<'i>>,
|
||||
wire_by_name: &mut HashMap<Wire<'i>, usize>,
|
||||
name: &'i str,
|
||||
) -> (usize, Wire<'i>) {
|
||||
let name = if let Some(n) = name.strip_prefix("x") {
|
||||
Wire::X(n.parse().unwrap())
|
||||
} else if let Some(n) = name.strip_prefix("y") {
|
||||
Wire::Y(n.parse().unwrap())
|
||||
} else if let Some(n) = name.strip_prefix("z") {
|
||||
Wire::Z(n.parse().unwrap())
|
||||
} else {
|
||||
Wire::Intermediate(name)
|
||||
};
|
||||
let idx = *wire_by_name.entry(name).or_insert_with(|| {
|
||||
let idx = wires.len();
|
||||
wires.push(Value::Unknown);
|
||||
wire_names.push(name);
|
||||
idx
|
||||
});
|
||||
(idx, name)
|
||||
}
|
||||
|
||||
let (_, gates) = input.split_once("\n\n").unwrap();
|
||||
|
||||
let mut zs = Vec::new();
|
||||
|
||||
for computed in gates.lines() {
|
||||
let (expr, wire) = computed.split_once(" -> ").unwrap();
|
||||
let [lhs, op, rhs] = expr.split(' ').collect_array().unwrap();
|
||||
|
||||
let (wire, wire_name) = intern(&mut wires, &mut wire_names, &mut wire_by_name, wire);
|
||||
let (lhs, _) = intern(&mut wires, &mut wire_names, &mut wire_by_name, lhs);
|
||||
let (rhs, _) = intern(&mut wires, &mut wire_names, &mut wire_by_name, rhs);
|
||||
|
||||
if let Wire::Z(_) = wire_name {
|
||||
zs.push(wire)
|
||||
}
|
||||
|
||||
let op = match op {
|
||||
"AND" => Op::And,
|
||||
"OR" => Op::Or,
|
||||
"XOR" => Op::Xor,
|
||||
_ => panic!("Invalid op: {op}"),
|
||||
};
|
||||
|
||||
wires[wire] = Value::Op(op, lhs, rhs);
|
||||
}
|
||||
|
||||
zs.sort_by_key(|wire| wire_names[*wire]);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum Pattern {
|
||||
Op(Op, Box<Pattern>, Box<Pattern>),
|
||||
X(u8),
|
||||
Y(u8),
|
||||
}
|
||||
|
||||
fn check_match(
|
||||
wire_names: &[Wire<'_>],
|
||||
wires: &[Value],
|
||||
check_cache: &mut HashMap<(usize, Pattern), Result<(), (usize, Pattern)>>,
|
||||
wire: usize,
|
||||
pattern: &Pattern,
|
||||
) -> Result<(), (usize, Pattern)> {
|
||||
let key = (wire, pattern.clone());
|
||||
if let Some(cache_value) = check_cache.get(&key) {
|
||||
return cache_value.clone();
|
||||
}
|
||||
let value = check_match_uncached(wire_names, wires, check_cache, wire, pattern);
|
||||
let prev_cache_entry = check_cache.insert(key, value.clone());
|
||||
assert!(prev_cache_entry.is_none());
|
||||
value
|
||||
}
|
||||
fn check_match_uncached(
|
||||
wire_names: &[Wire<'_>],
|
||||
wires: &[Value],
|
||||
check_cache: &mut HashMap<(usize, Pattern), Result<(), (usize, Pattern)>>,
|
||||
wire: usize,
|
||||
pattern: &Pattern,
|
||||
) -> Result<(), (usize, Pattern)> {
|
||||
//eprintln!("{:?} matches {:?}", wire_names[wire], pattern);
|
||||
|
||||
match (wire_names[wire], pattern) {
|
||||
(_, Pattern::Op(_, _, _)) => {}
|
||||
(Wire::X(x_value), Pattern::X(x_pattern)) if x_value == *x_pattern => return Ok(()),
|
||||
(Wire::Y(y_value), Pattern::Y(y_pattern)) if y_value == *y_pattern => return Ok(()),
|
||||
_ => {
|
||||
return Err((wire, pattern.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
match (wires[wire], pattern) {
|
||||
(
|
||||
Value::Op(op_value, lhs_wire, rhs_wire),
|
||||
Pattern::Op(op_pattern, pattern1, pattern2),
|
||||
) if op_value == *op_pattern => {
|
||||
let lhs_1 = check_match(wire_names, wires, check_cache, lhs_wire, &pattern1);
|
||||
let rhs_2 = check_match(wire_names, wires, check_cache, rhs_wire, &pattern2);
|
||||
if lhs_1.is_ok() && rhs_2.is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let lhs_2 = check_match(wire_names, wires, check_cache, lhs_wire, &pattern2);
|
||||
let rhs_1 = check_match(wire_names, wires, check_cache, rhs_wire, &pattern1);
|
||||
if lhs_2.is_ok() && rhs_1.is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If both inputs are wrong, this node is likely the culprit.
|
||||
// If only one input is wrong, this input is the culprit.
|
||||
if lhs_1.is_ok() {
|
||||
rhs_2
|
||||
} else if rhs_2.is_ok() {
|
||||
lhs_1
|
||||
} else if lhs_2.is_ok() {
|
||||
rhs_1
|
||||
} else if rhs_1.is_ok() {
|
||||
lhs_2
|
||||
} else {
|
||||
Err((wire, pattern.clone()))
|
||||
}
|
||||
}
|
||||
_ => Err((wire, pattern.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut incorrect_gates = HashSet::new();
|
||||
|
||||
let mut prev_carry_pat = Pattern::X(255); // dummy
|
||||
|
||||
let mut check_cache = HashMap::new();
|
||||
|
||||
for z_wire in zs {
|
||||
let Wire::Z(pos) = wire_names[z_wire] else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
eprintln!("------------ checking z{pos:0>2}");
|
||||
|
||||
let input_xor_pat = Pattern::Op(
|
||||
Op::Xor,
|
||||
Box::new(Pattern::X(pos)),
|
||||
Box::new(Pattern::Y(pos)),
|
||||
);
|
||||
|
||||
let (z_pat, carry_pat) = match pos {
|
||||
0 => {
|
||||
let carry_pat = Pattern::Op(
|
||||
Op::And,
|
||||
Box::new(Pattern::X(pos)),
|
||||
Box::new(Pattern::Y(pos)),
|
||||
);
|
||||
(input_xor_pat, carry_pat)
|
||||
}
|
||||
_ => {
|
||||
let carry_pat = Pattern::Op(
|
||||
Op::Or,
|
||||
Box::new(Pattern::Op(
|
||||
Op::And,
|
||||
Box::new(Pattern::X(pos)),
|
||||
Box::new(Pattern::Y(pos)),
|
||||
)),
|
||||
Box::new(Pattern::Op(
|
||||
Op::And,
|
||||
Box::new(prev_carry_pat.clone()),
|
||||
Box::new(input_xor_pat.clone()),
|
||||
)),
|
||||
);
|
||||
(
|
||||
Pattern::Op(Op::Xor, Box::new(input_xor_pat), Box::new(prev_carry_pat)),
|
||||
carry_pat,
|
||||
)
|
||||
}
|
||||
};
|
||||
prev_carry_pat = carry_pat;
|
||||
|
||||
if let Err((incorrect_wire, pat)) =
|
||||
check_match(&wire_names, &wires, &mut check_cache, z_wire, &z_pat)
|
||||
{
|
||||
eprintln!("error: {:?}", wire_names[incorrect_wire]);
|
||||
incorrect_gates.insert((incorrect_wire, pat));
|
||||
}
|
||||
}
|
||||
|
||||
dbg!(incorrect_gates.len());
|
||||
|
||||
for (wire, pat) in &incorrect_gates {
|
||||
eprintln!("{:?}", wire_names[*wire]);
|
||||
|
||||
let pair = incorrect_gates.iter().find(|(other_wire, other_pat)| {
|
||||
check_match(&wire_names, &wires, &mut check_cache, *wire, other_pat).is_ok()
|
||||
});
|
||||
if let Some(pair) = pair {
|
||||
eprintln!(" pairs with {:?}", wire_names[pair.0]);
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue