Day 21
This commit is contained in:
parent
ae355f1a54
commit
bf7f2fede6
2 changed files with 202 additions and 9 deletions
185
days/day21.st
Normal file
185
days/day21.st
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
Object subclass: KeyseqSet [
|
||||
| seqs size length |
|
||||
size [^size]
|
||||
length [^length]
|
||||
|
||||
seqs: s
|
||||
[ s do: [
|
||||
:e | | len sz |
|
||||
len := self lengthOf: e.
|
||||
sz := self sizeOf: e.
|
||||
length
|
||||
ifNil: [
|
||||
length := len. size := sz.
|
||||
seqs := OrderedCollection with: e.
|
||||
]
|
||||
ifNotNil: [
|
||||
:l |
|
||||
len = l ifTrue: [
|
||||
size := size + 1.
|
||||
seqs add: e.
|
||||
] ifFalse: [
|
||||
len < l ifTrue: [
|
||||
length := len. size := sz.
|
||||
seqs := OrderedCollection with: e.
|
||||
]
|
||||
]]
|
||||
].
|
||||
seqs := seqs asArray.
|
||||
]
|
||||
|
||||
sizeOf: e [e isString ifTrue: [^1] ifFalse: [^e size]]
|
||||
lengthOf: e [e isString ifTrue: [^e size] ifFalse: [^e length]]
|
||||
allOf: e [e isString ifTrue: [^{e}] ifFalse: [^e allOf]]
|
||||
|
||||
allOf
|
||||
[ | set |
|
||||
set := Set new.
|
||||
seqs collect: [:e | set addAll: (self allOf: e)].
|
||||
^set ]
|
||||
|
||||
printOn: st [ st << self class << '['
|
||||
<< 's=' << size << ',l=' << length
|
||||
<< ']' << seqs ]
|
||||
]
|
||||
|
||||
KeyseqSet subclass: KeyseqCat [
|
||||
seqs: s
|
||||
[ seqs := s asArray.
|
||||
size := seqs inject: 1 into: [:a :r | a * (self sizeOf: r)].
|
||||
length := seqs inject: 0 into: [:a :r | a + (self lengthOf: r)] ]
|
||||
|
||||
allOf
|
||||
[ | set |
|
||||
set := Set new.
|
||||
self allOfInto: set from: 1 pfx: ''.
|
||||
^set ]
|
||||
|
||||
allOfInto: set from: i pfx: pfx
|
||||
[ | e | i > seqs size ifTrue: [^set add: pfx].
|
||||
e := seqs at: i.
|
||||
(self allOf: e) do: [
|
||||
:str | self allOfInto: set from: i + 1 pfx: pfx,str]]
|
||||
]
|
||||
|
||||
Object subclass: Keypad [
|
||||
| grid paths optimal |
|
||||
grid: g
|
||||
[ grid := g.
|
||||
paths := LookupTable new.
|
||||
grid allPosnsDo: [:pos | self pathsFrom: pos] ]
|
||||
paths [^paths]
|
||||
|
||||
pathsFrom: pos
|
||||
[ (grid at: pos) = $. ifTrue: [^self].
|
||||
grid allPosnsDo: [:opos | self pathsFrom: pos to: opos] ]
|
||||
|
||||
pathsFrom: pos to: opos
|
||||
[ | dx dy c1 c2 seqs |
|
||||
c2 := grid at: opos.
|
||||
c2 = $. ifTrue: [^self].
|
||||
c1 := grid at: pos.
|
||||
|
||||
dx := opos x - pos x.
|
||||
dy := opos y - pos y.
|
||||
seqs := self allPathsFrom: pos by: dx and: dy prefix: ''.
|
||||
paths at: (String with: c1 with: c2) put: seqs ]
|
||||
|
||||
allPathsFrom: pos by: dx and: dy prefix: pfx
|
||||
[ | set sym oset |
|
||||
(grid at: pos) = $. ifTrue: [^#()].
|
||||
(dx = 0 and: [dy = 0]) ifTrue: [^{pfx,'A'}].
|
||||
set := OrderedCollection new.
|
||||
dx = 0 ifFalse: [
|
||||
sym := dx < 0 ifTrue: ['<'] ifFalse: ['>'].
|
||||
oset := self allPathsFrom: pos + (Posn x: dx sign y: 0)
|
||||
by: dx - dx sign and: dy prefix: pfx,sym.
|
||||
set addAll: oset.
|
||||
].
|
||||
dy = 0 ifFalse: [
|
||||
sym := dy < 0 ifTrue: ['^'] ifFalse: ['v'].
|
||||
oset := self allPathsFrom: pos + (Posn x: 0 y: dy sign)
|
||||
by: dx and: dy - dy sign prefix: pfx,sym.
|
||||
set addAll: oset.
|
||||
].
|
||||
^set ]
|
||||
|
||||
pressPath: key to: okey [ ^paths at: (String with: key with: okey) ]
|
||||
|
||||
printOn: st [ st << grid ]
|
||||
|
||||
"Replace the path set for each key pair with a singleton set of the
|
||||
best path, in terms of keypresses required by a sufficiently long chain."
|
||||
optimisePaths
|
||||
[ | robot |
|
||||
optimal ifNotNil: [^self].
|
||||
optimal := true.
|
||||
robot := Robot robotChain: 4. "seems to be a fixed point"
|
||||
paths keysAndValuesDo: [
|
||||
:key :seqs | | nseqs |
|
||||
nseqs := seqs collect: [:it | {(robot keyseqFor: it) length . it}].
|
||||
nseqs sort: [:l :r | (l at: 1) <= (r at: 1)].
|
||||
paths at: key put: {(nseqs at: 1) at: 2}]]
|
||||
]
|
||||
Keypad class extend [
|
||||
| numpad dirpad |
|
||||
parse: rows [ ^Keypad new grid: (Grid new rows: rows) ]
|
||||
numpad [^numpad ifNil: [
|
||||
numpad := Keypad parse:
|
||||
#('789'
|
||||
'456'
|
||||
'123'
|
||||
'.0A')]]
|
||||
dirpad [^dirpad ifNil: [
|
||||
dirpad := Keypad parse:
|
||||
#('.^A'
|
||||
'<v>')]]
|
||||
]
|
||||
|
||||
Object subclass: Robot [
|
||||
| keypad controlledBy seqCache depth |
|
||||
keypad: kp [keypad := kp. depth := 0]
|
||||
keypad: kp controlledBy: robo
|
||||
[keypad := kp. controlledBy := robo.
|
||||
depth := robo ifNil: [0] ifNotNil: [:it | it depth + 1]]
|
||||
|
||||
depth [^depth]
|
||||
initialize [seqCache := LookupTable new]
|
||||
|
||||
keyseqFor: str
|
||||
[ ^seqCache at: str ifAbsent: [
|
||||
^seqCache at: str put: (self computeKeyseqFor: str)] ]
|
||||
|
||||
computeKeyseqFor: str
|
||||
[ | steps |
|
||||
steps := 0 to: str size - 1 collect: [
|
||||
:i | | paths |
|
||||
paths := keypad pressPath: (str at: i ifAbsent: [$A])
|
||||
to: (str at: i + 1).
|
||||
controlledBy ifNotNil: [
|
||||
paths := paths collect: [:it | controlledBy keyseqFor: it]].
|
||||
KeyseqSet new seqs: paths
|
||||
].
|
||||
^KeyseqCat new seqs: steps ]
|
||||
]
|
||||
|
||||
Robot class extend [
|
||||
robotChain: len
|
||||
[ len = 0 ifTrue: [^nil].
|
||||
^self new keypad: Keypad dirpad controlledBy:
|
||||
(self robotChain: len - 1) ]
|
||||
|
||||
fullChain: keypad length: len
|
||||
[ ^self new keypad: keypad controlledBy: (self robotChain: len) ]
|
||||
]
|
||||
|
||||
AOC input: [ Keypad dirpad optimisePaths.
|
||||
"Keypad numpad optimisePaths. -- it's not even worth it"
|
||||
stdin toLines asArray ];
|
||||
part1: 2; part2: 25; partN: 100 ":D";
|
||||
result: [ :codes :chainLen | | robot |
|
||||
robot := Robot fullChain: Keypad numpad length: chainLen.
|
||||
(codes collect: [
|
||||
:code | (robot keyseqFor: code) length * code asNumber])
|
||||
sum ];
|
||||
finish.
|
||||
|
|
@ -1,23 +1,30 @@
|
|||
"--- AOC helper class ---"
|
||||
Object subclass: AOC []
|
||||
AOC class extend [
|
||||
| savedP1 savedP2 mapper getInput actionDict |
|
||||
input: inpBlock [ getInput := inpBlock ]
|
||||
part1: p1Block [ savedP1 := p1Block ]
|
||||
part2: p2Block [ savedP2 := p2Block ]
|
||||
| savedParts mapper getInput actionDict |
|
||||
|
||||
initialize
|
||||
[ savedParts := Array new: 2.
|
||||
getInput := OrderedCollection new. ]
|
||||
|
||||
input: inpBlock [ getInput add: inpBlock ]
|
||||
part1: partBlock [ savedParts at: 1 put: partBlock ]
|
||||
part2: partBlock [ savedParts at: 2 put: partBlock ]
|
||||
partN: partBlock [ savedParts := savedParts,{partBlock} ]
|
||||
result: mapBlock [ mapper := mapBlock ]
|
||||
|
||||
output: value part: part [ stdout << 'Part ' << part << ': ' << value; nl; flush. ]
|
||||
|
||||
runPart: saved part: part arg: arg
|
||||
runPart: saved part: part args: args
|
||||
[ saved ifNotNil: [
|
||||
| mapped | mapped := mapper value: arg value: saved.
|
||||
| mapped |
|
||||
mapped := mapper valueWithArguments: args,{saved}.
|
||||
self output: mapped part: part ] ]
|
||||
|
||||
run
|
||||
[ | inp | inp := getInput value.
|
||||
self runPart: savedP1 part: 1 arg: inp.
|
||||
self runPart: savedP2 part: 2 arg: inp. ]
|
||||
[ | inps | inps := getInput asArray collect: [:it | it value].
|
||||
savedParts keysAndValuesDo: [
|
||||
:part :saved | self runPart: saved part: part args: inps ]]
|
||||
|
||||
action: name do: block
|
||||
[ actionDict ifNil: [actionDict := Dictionary new].
|
||||
|
|
@ -45,6 +52,7 @@ AOC class extend [
|
|||
file parent createDirectories.
|
||||
io := file writeStream. [ block value: io ] ensure: [io close] ]
|
||||
]
|
||||
AOC initialize.
|
||||
|
||||
"--- Scanf ---"
|
||||
Object subclass: ScannerState [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue