Day 24
This commit is contained in:
parent
6a78167ea5
commit
b3ebcbd041
2 changed files with 267 additions and 0 deletions
253
days/day24.st
Normal file
253
days/day24.st
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
Object subclass: Gate [
|
||||
| name classif |
|
||||
name: nm [name := nm]
|
||||
name [^name]
|
||||
printOn: st [ st << self class << ' ' << name ]
|
||||
eval: cq [self subclassResponsibility]
|
||||
|
||||
inputs [^nil]
|
||||
opnm [self subclassResponsibility]
|
||||
visualize: st
|
||||
[st << name << '[label="' << name << ' ' << self opnm.
|
||||
classif ifNotNil: [:c | st << ' (' << c << ')'].
|
||||
st << '"];']
|
||||
|
||||
classif: c [classif := c]
|
||||
classif [^classif]
|
||||
|
||||
isInput [^false]
|
||||
]
|
||||
Gate subclass: InputGate [
|
||||
isInput [^true]
|
||||
]
|
||||
InputGate subclass: OneGate [opnm [^$1] eval: cq [^true]]
|
||||
InputGate subclass: ZeroGate [opnm [^$0] eval: cq [^false]]
|
||||
Gate subclass: LogicGate [
|
||||
| lhs rhs cache |
|
||||
lhs: l rhs: r [lhs := l. rhs := r]
|
||||
eval: cq [^cache ifNil: [cache := self doEval: (cq eval: lhs) and: (cq eval: rhs)]]
|
||||
doEval: lv and: rv [self subclassResponsibility]
|
||||
printOn: st [ st << self class << $( << name << ': ' << lhs << ', ' << rhs << $) ]
|
||||
|
||||
inputs [^{lhs . rhs}]
|
||||
|
||||
visualize: st
|
||||
[super visualize: st.
|
||||
st << lhs << '->' << name << ';'.
|
||||
st << rhs << '->' << name << ';']
|
||||
]
|
||||
LogicGate subclass: AndGate [opnm [^$&] doEval: lv and: rv [^lv & rv]]
|
||||
LogicGate subclass: OrGate [opnm [^$|] doEval: lv and: rv [^lv | rv]]
|
||||
LogicGate subclass: XorGate [opnm [^$^] doEval: lv and: rv [^lv xor: rv]]
|
||||
|
||||
Object subclass: Circuit [
|
||||
| gates forwardMap swaps locked classMap |
|
||||
initialize
|
||||
[ gates := LookupTable new.
|
||||
swaps := Set new.
|
||||
locked := Bag new. ]
|
||||
gates [^gates]
|
||||
at: nm put: gate [^gates at: nm put: (gate name: nm)]
|
||||
at: nm [^gates at: nm]
|
||||
|
||||
eval: nm [^(gates at: nm) eval: self]
|
||||
|
||||
parseInits: inits logic: logic
|
||||
[ | optbl |
|
||||
optbl := LookupTable new.
|
||||
optbl at: 'AND' put: AndGate;
|
||||
at: 'OR' put: OrGate;
|
||||
at: 'XOR' put: XorGate.
|
||||
inits lines do: [
|
||||
:ln | ln scanf: '%[^:]: %d'
|
||||
with: [
|
||||
:nm :v |
|
||||
self at: nm put:
|
||||
(v = 1 ifTrue: [OneGate] ifFalse: [ZeroGate])
|
||||
new]].
|
||||
logic lines do: [
|
||||
:ln | ln scanf: '%s %s %s -> %s'
|
||||
with: [
|
||||
:g1 :op :g2 :nm |
|
||||
self at: nm put: ((optbl at: op) new lhs: g1 rhs: g2)]]]
|
||||
|
||||
gateAssocs
|
||||
[ | ord | ord := SortedCollection new.
|
||||
gates keysAndValuesDo: [:nm :g | ord add: (nm -> g)].
|
||||
^ord asArray ]
|
||||
|
||||
printOn: st
|
||||
[ st << self class << $(; nl.
|
||||
self gateAssocs do: [:ass | st tab; << ass value; nl].
|
||||
st << $) ]
|
||||
|
||||
visualize
|
||||
[ AOC visualizeWithExt: '.dot' do: [
|
||||
:st |
|
||||
st << 'digraph {'.
|
||||
self gateAssocs do: [:ass | ass value visualize: st].
|
||||
st << '}'; nl] ]
|
||||
|
||||
swap: nm1 and: nm2
|
||||
[ | tmp key undoing |
|
||||
key := {nm1. nm2} sort.
|
||||
(swaps includes: key) ifTrue: [
|
||||
swaps remove: key.
|
||||
locked removeAll: {nm1 . nm2}.
|
||||
] ifFalse: [
|
||||
swaps size >= 4 ifTrue: [^false].
|
||||
((locked includes: nm1) or: [locked includes: nm2])
|
||||
ifTrue: [^false].
|
||||
swaps add: key.
|
||||
locked addAll: {nm1 . nm2}.
|
||||
].
|
||||
tmp := self at: nm1.
|
||||
self at: nm1 put: (gates at: nm2).
|
||||
self at: nm2 put: tmp.
|
||||
^true ]
|
||||
|
||||
try: block unwind: otherwise
|
||||
[ ^block value or: [otherwise value. false] ]
|
||||
|
||||
swapped: nm1 and: nm2 do: block
|
||||
[ ^(self swap: nm1 and: nm2) and: [
|
||||
self try: block
|
||||
unwind: [self swap: nm1 and: nm2]]]
|
||||
locked: nm do: block
|
||||
[ ^self try: [locked add: nm. block value]
|
||||
unwind: [locked remove: nm] ]
|
||||
|
||||
classifyGates
|
||||
[ classMap := Dictionary new.
|
||||
gates do: [
|
||||
:g | | k |
|
||||
k := self classify: g.
|
||||
g classif: k.
|
||||
classMap at: k inA: Set add: g ] ]
|
||||
|
||||
classify: g
|
||||
[ | halfGate |
|
||||
"half adder:
|
||||
- I, I -> H&, H^
|
||||
- C' = H^ ; O = H&"
|
||||
"full adder:
|
||||
- H^, C -> F&, F^
|
||||
- F| <- H&, F&
|
||||
- C' = F| ; O = F&"
|
||||
g isInput ifTrue: [ ^'I' ].
|
||||
halfGate := g inputs allSatisfy: [:i | (self at: i) isInput].
|
||||
halfGate ifTrue: [ ^String with: $H with: g opnm ].
|
||||
^String with: $F with: g opnm ]
|
||||
|
||||
withInput: nm
|
||||
[ forwardMap ifNil: [
|
||||
forwardMap := Dictionary new.
|
||||
gates do: [:g | g inputs ifNotNilDo: [
|
||||
:i | forwardMap at: i inA: Set add: g]]].
|
||||
^forwardMap at: nm ifAbsent: [#()] ]
|
||||
classifySet: set
|
||||
[ | map | map := LookupTable new.
|
||||
set do: [:elt | map at: elt classif put: elt].
|
||||
^map ]
|
||||
withInputClassify: nm [ ^self classifySet: (self withInput: nm) ]
|
||||
withInputClassify: nm1 and: nm2 mustHave: classes
|
||||
[ | cls1 cls2 |
|
||||
cls1 := self withInputClassify: nm1.
|
||||
cls2 := self withInputClassify: nm2.
|
||||
cls1 = cls2 ifFalse: [^nil].
|
||||
classes do: [:c | cls1 at: c ifAbsent: [^nil]].
|
||||
^cls1 ]
|
||||
withInputClassify: nm1 and: nm2 mustHave: classes do: block
|
||||
[ (self withInputClassify: nm1 and: nm2 mustHave: classes)
|
||||
ifNil: [^false]
|
||||
ifNotNil: [:v | ^block value: v] ]
|
||||
|
||||
findFaults
|
||||
[ self classifyGates.
|
||||
(self findFaults: 0 carry: nil)
|
||||
ifFalse: [self error: 'No working swaps']
|
||||
ifTrue: [^swaps]. ]
|
||||
|
||||
gate: gate mustHaveName: nm do: block
|
||||
[ ^(gate name = nm)
|
||||
ifTrue: [block value]
|
||||
ifFalse: [self swapped: gate name and: nm do: block] ]
|
||||
|
||||
ioName: c at: idx
|
||||
[ ^String with: c
|
||||
with: (Character digitValue: idx // 10)
|
||||
with: (Character digitValue: idx \\ 10) ]
|
||||
|
||||
maybeRenameGate: gate do: nameBlock
|
||||
[ ^(self locked: gate name do: [
|
||||
nameBlock value: gate name]) or: [
|
||||
(classMap at: gate classif) anySatisfy: [
|
||||
:ogate |
|
||||
(ogate == gate) not and: [
|
||||
(locked includes: ogate name) not and: [
|
||||
self swapped: gate name and: ogate name do: [
|
||||
nameBlock value: gate name ]]]]]]
|
||||
|
||||
maybeSwap: gate1 and: gate2 do: block
|
||||
[ ^block value or: [ self swapped: gate1 name and: gate2 name do: block ] ]
|
||||
|
||||
findFaults: idx carry: carry
|
||||
[ | halfAdder zNN |
|
||||
"Try and find the adder which outputs zNN.
|
||||
|
||||
This involves finding the half adder, then its full adder,
|
||||
ensuring the output bit is to the correct zNN, and possibly
|
||||
swapping out each output along the way."
|
||||
zNN := self ioName: $z at: idx.
|
||||
"inputs can't be wired wrong"
|
||||
halfAdder := self withInputClassify: (self ioName: $x at: idx).
|
||||
halfAdder isEmpty ifTrue: ["Done!" ^carry = zNN].
|
||||
^self maybeSwap: (halfAdder at: 'H^') and: (halfAdder at: 'H&') do: [
|
||||
carry ifNil: [
|
||||
self gate: (halfAdder at: 'H^')
|
||||
mustHaveName: zNN
|
||||
do: [
|
||||
self maybeRenameGate: (halfAdder at: 'H&') do: [
|
||||
:hAnm | self findFaults: idx + 1 carry: hAnm ]]]
|
||||
ifNotNil: [
|
||||
self maybeRenameGate: (halfAdder at: 'H^') do: [
|
||||
:hXnm |
|
||||
self withInputClassify: carry
|
||||
and: hXnm
|
||||
mustHave: #('F&' 'F^')
|
||||
do: [
|
||||
:fullAdder |
|
||||
self maybeSwap: (fullAdder at: 'F^') and: (fullAdder at: 'F&') do: [
|
||||
self gate: (fullAdder at: 'F^')
|
||||
mustHaveName: zNN
|
||||
do: [
|
||||
self maybeRenameGate: (fullAdder at: 'F&') do: [
|
||||
:fAnm |
|
||||
self maybeRenameGate: (halfAdder at: 'H&') do: [
|
||||
:hAnm |
|
||||
self withInputClassify: hAnm
|
||||
and: fAnm
|
||||
mustHave: #('F|')
|
||||
do: [
|
||||
:fullOr |
|
||||
self maybeRenameGate: (fullOr at: 'F|') do: [
|
||||
:fOnm |
|
||||
self findFaults: idx + 1 carry: fOnm ]]]]]]]]]]]
|
||||
]
|
||||
|
||||
AOC input: [ stdin contents splitDoubleNl letArrayInBlock: [
|
||||
:inits :logic | Circuit new parseInits: inits logic: logic ]];
|
||||
part1: [ :cq | | outs res |
|
||||
outs := SortedCollection sortBlock: [:l :r | r <= l].
|
||||
cq gates keysAndValuesDo: [
|
||||
:nm :i | (nm startsWith: 'z') ifTrue: [outs add: nm]].
|
||||
res := 0.
|
||||
outs do: [
|
||||
:nm | | bit |
|
||||
bit := (cq eval: nm) ifTrue: [1] ifFalse: [0].
|
||||
res := (res bitShift: 1) bitOr: bit.
|
||||
].
|
||||
res ];
|
||||
part2: [ :cq | cq findFaults join sort join: ','. ];
|
||||
result: [ :cq :part | part value: cq ];
|
||||
finish.
|
||||
|
|
@ -164,6 +164,20 @@ Iterable extend [
|
|||
intoCollection: acc [ self do: [:x | acc add: x]. ^acc ]
|
||||
]
|
||||
|
||||
Set extend [
|
||||
flip: key
|
||||
[ (self includes: key)
|
||||
ifTrue: [self remove: key. ^true]
|
||||
ifFalse: [self add: key. ^false] ]
|
||||
]
|
||||
|
||||
Iterable extend [
|
||||
findFirstElt: block
|
||||
[ self do: [:v | (block value: v) ifTrue: [^v]]. ^nil ]
|
||||
findFirstElt: block ifAbsent: abs
|
||||
[ self do: [:v | (block value: v) ifTrue: [^v]]. ^abs value ]
|
||||
]
|
||||
|
||||
SequenceableCollection extend [
|
||||
findFirstElt: block [ ^self at: (self findFirst: block) ]
|
||||
findFirstElt: block ifAbsent: abs
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue