Advertisement
musifter

AoC 2024 day 21 (smalltalk)

Dec 21st, 2024 (edited)
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Smalltalk 4.60 KB | Source Code | 0 0
  1. #!/usr/local/bin/gst -q
  2.  
  3. Character  extend  [ , chr  [^self asString, chr asString] ]
  4. Collection extend  [
  5.     sum    [^self inject: 0 into: [:a :b | a + b]]
  6.     min    [^self inject: SmallInteger largest into: [:a :b | a min: b]]
  7. ]
  8.  
  9. SequenceableCollection extend [
  10.     " Returns collection of results from calling binBlock for each link pair "
  11.     chain: binBlock [
  12.         | res |
  13.         res := self copyEmptyForCollect.
  14.         self fold: [:curr :next | res add: (binBlock value: curr value: next). next].
  15.         ^res
  16.     ]
  17. ]
  18.  
  19. " Class to hold details of a keypad. "
  20. Object subclass: Keypad [
  21.     | keyLoc dim paths grid |
  22.     dirs := Dictionary from: {'^' -> (0@-1). '<' -> (-1@0). '>' -> (1@0). 'v' -> (0@1)}.
  23.  
  24.     Keypad class >> new: textLines  [^super new init: textLines]
  25.     init: textLines [
  26.         | sent delta steps queue state move |
  27.         " Build grid with sentinel Xs "
  28.         dim  := (textLines first size + 2) @ (textLines size + 2).
  29.         sent := $X * dim x.
  30.         grid := sent, (textLines collect: [:line | 'X',line,'X']) join, sent.
  31.  
  32.         " Find location of all keys "
  33.         keyLoc := Dictionary new.
  34.         grid keysAndValuesDo: [:idx :chr |
  35.             (chr ~= $X) ifTrue: [keyLoc at: chr put: (self toCoord: idx)]
  36.         ].
  37.  
  38.         " Find paths between all pairs of keys "
  39.         paths := Dictionary new.
  40.         keyLoc keysAndValuesDo: [:ikey :iloc |
  41.             keyLoc keysAndValuesDo: [:jkey :jloc |
  42.                 (ikey = jkey) ifTrue: [
  43.                     paths at: (ikey,jkey) put: {'A'}.
  44.                 ] ifFalse: [
  45.                     " Only head towards key, no expensive looping: "
  46.                     delta := jloc - iloc.
  47.                     steps := Set from: {(delta x < 0) ifTrue: ['<'] ifFalse: ['>'].
  48.                                         (delta y < 0) ifTrue: ['^'] ifFalse: ['v']}.
  49.  
  50.                     " BFS routes from start (iloc) to end (jloc): "
  51.                     queue := OrderedCollection from: {{iloc. ''}}.
  52.                     [queue notEmpty] whileTrue: [
  53.                         state := queue removeFirst.  " first: pos; second: path string "
  54.  
  55.                         (state first = jloc) ifTrue: [
  56.                             (paths at: (ikey,jkey) ifAbsentPut: [OrderedCollection new])
  57.                                    add: (state second, 'A').
  58.                         ] ifFalse: [
  59.                             steps do: [:dstr |
  60.                                 move := state first + (dirs at: dstr).
  61.                                 ((self at: move) ~= $X) ifTrue: [
  62.                                     queue add: {move. state second,dstr}.
  63.                                 ]
  64.                             ]
  65.                         ]
  66.                     ]
  67.                 ]
  68.             ]
  69.         ].
  70.         ^self
  71.     ]
  72.  
  73.     at: pt           [^grid at: (pt y * dim x) + pt x + 1]
  74.     toCoord: idx     [^(idx - 1 \\ dim x) @ (idx - 1 // dim x)]
  75.  
  76.     " Return list of paths between two keys: "
  77.     paths: toFrom    [^paths at: toFrom]
  78. ]
  79.  
  80. " Class to represent relay of robots using Keypads. "
  81. Object subclass: RobotRelay [
  82.     | robots memo |
  83.     keypad := Keypad new: #('789'
  84.                             '456'
  85.                             '123'
  86.                             'X0A').
  87.  
  88.     dirpad := Keypad new: #('X^A'
  89.                             '<v>').
  90.  
  91.     RobotRelay class >> new: num  [^super new init: num]
  92.     init: num  [robots := num. memo := Dictionary new. ^self]
  93.  
  94.     " Internal recursive method.  For a given sequence and depth, returns minimum length. "
  95.     recurseSequence: seq depth: depth [
  96.         | pad paths |
  97.  
  98.         ^memo at: (seq->depth) ifAbsentPut: [
  99.             pad := (depth == 0) ifTrue: [keypad] ifFalse: [dirpad].
  100.  
  101.             " Always start (and end) at $A so append it for first. "
  102.             (($A,seq) asOrderedCollection chain: [:start :end |
  103.                 paths := pad paths: (start,end).
  104.  
  105.                 (depth == robots) ifTrue: [
  106.                     paths first size  " all paths same size, no chance of variance at bottom "
  107.                 ] ifFalse: [
  108.                     (paths collect: [:subseq | self recurseSequence: subseq depth: depth+1]) min
  109.                 ]
  110.             ]) sum.
  111.         ]
  112.     ]
  113.  
  114.     " Call this to start recursion: "
  115.     getSequenceLen: seq [^self recurseSequence: seq depth: 0]
  116. ]
  117.  
  118. "
  119. | Mainline
  120. "
  121. input := stdin lines contents.
  122.  
  123. {1 -> 2. 2 -> 25} do: [:test |
  124.     relay := RobotRelay new: test value.
  125.     part  := (input collect: [:seq | (relay getSequenceLen: seq) * seq asNumber]) sum.
  126.     ('Part %1: %2' % {test key. part}) displayNl.
  127. ]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement