From bf7f2fede6c6c7208a9478c75cae274010c61745 Mon Sep 17 00:00:00 2001 From: eutro Date: Sat, 21 Dec 2024 09:26:43 +0000 Subject: [PATCH] Day 21 --- days/day21.st | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ days/utils.st | 26 ++++--- 2 files changed, 202 insertions(+), 9 deletions(-) create mode 100644 days/day21.st diff --git a/days/day21.st b/days/day21.st new file mode 100644 index 0000000..55af2b4 --- /dev/null +++ b/days/day21.st @@ -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' + '')]] +] + +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. diff --git a/days/utils.st b/days/utils.st index e9647d2..ff47bca 100644 --- a/days/utils.st +++ b/days/utils.st @@ -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 [