musifter

AoC 2024, day 15, part 2 (smalltalk)

Dec 15th, 2024 (edited)
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Smalltalk 5.35 KB | Source Code | 0 0
  1. #!/usr/local/bin/gst -q
  2.  
  3. Symbol extend [ value: arg [^arg perform: self] ]
  4.  
  5. " Class to hold results of vertical push tests "
  6. Object subclass: VerticalPushResults [
  7.     | can boxes |
  8.     VerticalPushResults class >> new  [^super new init]
  9.     init  [can := true. boxes := Set new. ^self]
  10.  
  11.     addBox: box     [^boxes add: box]
  12.     boxes           [^boxes]
  13.  
  14.     goodLeaf: bool  [^can := can & bool]
  15.     canPush         [^can]
  16. ]
  17.  
  18. Object subclass: DoubleWideTextGrid [
  19.     | grid dim robot |
  20.  
  21.     dirs := Dictionary from: {$< -> (-1@0). $^ -> (0@-1). $v -> (0@1). $> -> (1@0)}.
  22.  
  23.     DoubleWideTextGrid class >> new: mapArray [^super new init: mapArray]
  24.     init: mapArray [
  25.         " No sentinel needed, has boundary wall."
  26.         dim  := (2 * mapArray first size) @ (mapArray size).
  27.         grid := (mapArray join asArray gather: [:chr |
  28.                     (chr == $O) ifTrue: [#($[ $])] ifFalse: [{chr. chr}]
  29.                 ]) asString.
  30.  
  31.         robot := grid indexOfSubCollection: '@@'.
  32.         grid replaceFrom: robot to: robot + 1 with: '..'.
  33.         robot := self toCoord: robot.
  34.         ^self
  35.     ]
  36.  
  37.     " Access to grid via Points "
  38.     at: pt            [^grid at: (pt y * dim x) + pt x + 1]
  39.     at: pt put: chr   [^grid at: (pt y * dim x) + pt x + 1 put: chr]
  40.     toCoord: idx      [^(idx - 1 \\ dim x) @ (idx - 1 // dim x)]
  41.     toIndex: pt       [^(pt y * dim x) + pt x + 1]
  42.  
  43.     " Basic access "
  44.     dirs        [^dirs]
  45.     dim         [^dim]
  46.     robot       [^robot]
  47.     robot: pt   [^robot := pt]
  48.  
  49.     " Return array of Points where aBlock on that character returns true "
  50.     selectPoints: aBlock [
  51.         | res |
  52.         res := OrderedCollection new.
  53.         grid keysAndValuesDo: [:idx :chr |
  54.             (aBlock value: chr) ifTrue: [res add: (self toCoord: idx)].
  55.         ].
  56.         ^res
  57.     ]
  58.  
  59.     " Recursive function for vertial push test "
  60.     recursePos: boxLeft dir: dir result: res [
  61.         | ahead |
  62.         res addBox: boxLeft.
  63.         ahead := {dir. dir + (dirs at: $>)} collect: [:d | self at: (boxLeft + d)].
  64.  
  65.         " Leaf checks... all empty is good, any wall is bad "
  66.         (ahead conform:  [:chr | chr == $.]) ifTrue: [^res goodLeaf: true;  yourself].
  67.         (ahead contains: [:chr | chr == $#]) ifTrue: [^res goodLeaf: false; yourself].
  68.  
  69.         " More multipushing to test: "
  70.         ((ahead at: 1) == $[) ifTrue: [
  71.             " Straight ahead is [], a single box "
  72.             self recursePos: boxLeft + dir dir: dir result: res.
  73.  
  74.         ] ifFalse: [
  75.             " A box to left of right, maybe both "
  76.             ((ahead at: 1) == $]) ifTrue: [self recursePos: boxLeft + dir + (dirs at: $<) dir: dir result: res].
  77.             ((ahead at: 2) == $[) ifTrue: [self recursePos: boxLeft + dir + (dirs at: $>) dir: dir result: res].
  78.         ].
  79.         ^res
  80.     ]
  81.  
  82.     " Test vertical push, returns VerticalPushResults "
  83.     testVertical: pos dir: dir [
  84.         | res boxLeft |
  85.         res     := VerticalPushResults new.
  86.         boxLeft := ((self at: pos) == $[) ifTrue: [pos] ifFalse: [pos + (dirs at: $<)].
  87.  
  88.         ^self recursePos: boxLeft dir: dir result: res
  89.     ]
  90.  
  91.     " Print grid on stream "
  92.     printOn: aStream [
  93.         | robotIdx |
  94.         robotIdx := self toIndex: robot.
  95.         grid keysAndValuesDo: [:idx :chr |
  96.             aStream nextPut: (idx == robotIdx ifTrue: [$@] ifFalse: [chr]).
  97.             (idx \\ dim x = 0) ifTrue: [aStream nl].
  98.         ]
  99.     ]
  100. ]
  101.  
  102. "
  103. | Mainline
  104. "
  105. sections := (stdin contents tokenize: '\n\n') collect: #lines.
  106.  
  107. grid := DoubleWideTextGrid new: sections first.
  108.  
  109. sections second join do: [ :dchr |
  110.     dir  := grid dirs at: dchr.
  111.     move := grid robot + dir.
  112.     targ := grid at: move.
  113.  
  114.     (targ == $.) | (targ == $#) ifTrue: [
  115.         " Move or bump wall "
  116.         (targ == $.) ifTrue: [grid robot: move]
  117.     ] ifFalse: [
  118.         (dchr == $<) | (dchr == $>) ifTrue: [
  119.             " Horizonal push "
  120.             end := move.
  121.             [((grid at: end) == $[) | ((grid at: end) == $])] whileTrue: [end := end + dir].
  122.  
  123.             ((grid at: end) == $.) ifTrue: [
  124.                 " Toggle [ and ] along the push chain: "
  125.                 p := move.
  126.                 [p ~= end] whileTrue: [
  127.                     grid at: p put: (((grid at: p) == $[) ifTrue: [$]] ifFalse: [$[]).
  128.                     p := p + dir.
  129.                 ].
  130.  
  131.                 " Fix ends and move "
  132.                 grid at: end  put: (grid at: move).
  133.                 grid at: move put: $..
  134.                 grid robot: move.
  135.             ]
  136.  
  137.         ] ifFalse: [
  138.             " Vertical push "
  139.             results := grid testVertical: move dir: dir.
  140.  
  141.             results canPush ifTrue: [
  142.                 " Clear grid of old positions: "
  143.                 results boxes do: [:box |
  144.                     grid at: box put: $..
  145.                     grid at: box + (grid dirs at: $>) put: $..
  146.                 ].
  147.  
  148.                 " Set new positions: "
  149.                 results boxes do: [:box |
  150.                     newPos := box + dir.
  151.                     grid at: newPos put: $[.
  152.                     grid at: newPos + (grid dirs at: $>) put: $].
  153.                 ].
  154.                 grid robot: move.
  155.             ]
  156.         ]
  157.     ]
  158. ].
  159.  
  160. grid displayNl.
  161. part2 := (grid selectPoints: [:chr | chr == $[]) inject: 0 into: [:a :b| a + (100 * b y) + b x].
  162. ('Part 2: %1' % {part2}) displayNl.
Add Comment
Please, Sign In to add comment