Skip to content

Commit a76dfbe

Browse files
committed
use literate mode for dropBricks
1 parent e252522 commit a76dfbe

File tree

1 file changed

+41
-47
lines changed

1 file changed

+41
-47
lines changed

docs/2023/puzzles/day22.md

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Solver from "../../../../../website/src/components/Solver.js"
2+
import Literate from "../../../../../website/src/components/Literate.js"
23

34
# Day 22: Sand Slabs
45

@@ -60,7 +61,7 @@ Another vital operation involves determining whether two bricks collide with eac
6061
1D line segments on X axis collide with each other when:
6162

6263
```scala 3
63-
maxX >= otherMinX && otherMaxX >= minX
64+
maxX >= otherMinX && otherMaxX >= minX
6465
```
6566

6667
We are not guaranteed the order of coordinates, so we have to determine min values and max values ourselves. Let's
@@ -101,92 +102,85 @@ def collidesWith(other: Brick): Boolean =
101102

102103
### Dropping groups of bricks
103104

104-
As the input is a snapshot of falling bricks, we must first drop them all to a stationary position. Additionally, since
105-
we will determine the collisions at this point, we'll create a `Map` that will associate each brick with the bricks
106-
supporting it. Let's define the `dropBricks` function.
105+
As the input is a snapshot of falling bricks, we must first drop them all to a stationary position.
107106

108-
First, sort the bricks by the `z` axis to handle the falling order. This is necessary because the input doesn't
109-
guarantee the order of the bricks:
107+
First, define a way to check if a brick has collided with the ground, determined with a simple check on the `z` axis:
110108

111109
```scala 3
112-
val bricksByZAsc = bricks.sortBy(brick => (brick.start.z) min (brick.end.z))
110+
def collidesWithGround(brick: Brick): Boolean =
111+
brick.start.z == 0 || brick.end.z == 0
113112
```
114113

115-
Next, initialize the collection of bricks to drop and the `Map` of already dropped ones:
114+
Next, since we will determine the collisions at this point, we'll create a `Map` that will associate each brick with the bricks supporting it.
115+
116+
Let's define the `dropBricks` function that can do this:
117+
118+
<Literate>
116119

117120
```scala 3
118121
import scala.collection.mutable
119122

120-
val remainingBricks = mutable.Stack.from(bricksByZAsc)
121-
val droppedBricks = mutable.Map[Brick, Set[Brick]]()
123+
def dropBricks(bricks: Seq[Brick]): Map[Brick, Set[Brick]] = {
122124
```
123125

124-
Then, loop over the remaining bricks with `while (remainingBricks.nonEmpty)`. On each iteration, simulate the fall of a
125-
single brick:
126+
First, sort the bricks by the `z` axis to handle the falling order. This is necessary because the input doesn't
127+
guarantee the order of the bricks:
126128

127129
```scala 3
128-
val brick = remainingBricks.pop()
129-
val brickMovedDown = brick.moveDown
130+
val bricksByZAsc = bricks.sortBy(brick => (brick.start.z) min (brick.end.z))
130131
```
131132

132-
Now, determine whether the brick is stationary. "Stationary" means that it either collides with the ground or another
133-
brick. If it is stationary, put it into the `droppedBricks` along with the dropped bricks that collide with it. If not,
134-
put it back into the `remainingBricks` to move it further down in the next step:
133+
Next, initialize the `Stack` of bricks to drop and the `Map` of already dropped ones:
135134

136135
```scala 3
137-
if (collidesWithGround(brickMovedDown) || collidingBricks.nonEmpty)
138-
droppedBricks.put(brick, collidingBricks)
139-
else
140-
remainingBricks.push(brickMovedDown)
136+
val remainingBricks = mutable.Stack.from(bricksByZAsc)
137+
val droppedBricks = mutable.Map[Brick, Set[Brick]]()
141138
```
142139

143-
The collision with the ground is determined with a simple check on the `z` axis:
140+
Then, loop over the remaining bricks with `while (remainingBricks.nonEmpty)`.
144141

145142
```scala 3
146-
def collidesWithGround(brick: Brick): Boolean =
147-
brick.start.z == 0 || brick.end.z == 0
143+
while (remainingBricks.nonEmpty) {
148144
```
149145

150-
The `Set` of colliding dropped bricks is determined using the previously defined `Brick#collidesWith` method:
146+
On each iteration, simulate the fall of a single brick:
151147

152-
```scala 3
153-
val collidingBricks = droppedBricks.collect {
154-
case (droppedBrick, _) if brickMovedDown.collidesWith(droppedBrick) =>
155-
droppedBrick
156-
}.toSet
148+
```scala
149+
val brick = remainingBricks.pop()
150+
val brickMovedDown = brick.moveDown
157151
```
158152

159-
After all the bricks finish falling, return the `droppedBricks` as an immutable `Map`. The full function looks like
160-
this:
153+
First, determine if there are any colliding bricks from the currently known `droppedBricks`. We can produce this as a `Set`, with contents determined using the previously defined `Brick#collidesWith` method:
161154

162155
```scala 3
163-
import scala.collection.mutable
156+
val collidingBricks = droppedBricks.collect {
157+
case (droppedBrick, _) if brickMovedDown.collidesWith(droppedBrick) =>
158+
droppedBrick
159+
}.toSet
160+
```
164161

165-
def dropBricks(bricks: Seq[Brick]): Map[Brick, Set[Brick]] = {
166-
val bricksByZAsc = bricks.sortBy(brick => (brick.start.z) min (brick.end.z))
167-
val remainingBricks = mutable.Stack.from(bricksByZAsc)
168-
val droppedBricks = mutable.Map[Brick, Set[Brick]]()
162+
Now, determine whether the brick is stationary. "Stationary" means that it either collides with the ground or another
163+
brick. If it is stationary, put it into the `droppedBricks` along with the dropped bricks that collide with it. If not,
164+
put it back into the `remainingBricks` to move it further down in the next step:
169165

170-
while (remainingBricks.nonEmpty) {
171-
val brick = remainingBricks.pop()
172-
val brickMovedDown = brick.moveDown
173-
val collidingBricks = droppedBricks.collect {
174-
case (droppedBrick, _) if brickMovedDown.collidesWith(droppedBrick) =>
175-
droppedBrick
176-
}.toSet
166+
```scala 3
177167
if (collidesWithGround(brickMovedDown) || collidingBricks.nonEmpty)
178168
droppedBricks.put(brick, collidingBricks)
179169
else
180170
remainingBricks.push(brickMovedDown)
181171
}
172+
```
173+
174+
After all the bricks finish falling, return the `droppedBricks` by converting to an immutable `Map`.
182175

176+
```scala 3
183177
droppedBricks.toMap
184178
}
185-
186-
def collidesWithGround(brick: Brick): Boolean =
187-
brick.start.z == 0 || brick.end.z == 0
188179
```
189180

181+
</Literate>
182+
183+
190184
### Determining the disintegrable bricks
191185

192186
Now, let's get back to the core challenge. We want to figure out how many bricks we can safely disintegrate. A brick is

0 commit comments

Comments
 (0)