aoc2024/days/day21.st
2024-12-21 09:26:43 +00:00

185 lines
5.5 KiB
Smalltalk

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.