Skip to content

Commit 531bdfa

Browse files
Add day18 explanations
1 parent a33694f commit 531bdfa

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

docs/2023/puzzles/day18.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,136 @@ import Solver from "../../../../../website/src/components/Solver.js"
22

33
# Day 18: Lavaduct Lagoon
44

5+
by [@EugeneFlesselle](https://github.com/EugeneFlesselle)
6+
57
## Puzzle description
68

79
https://adventofcode.com/2023/day/18
810

11+
## Solution Summary
12+
13+
Assume we have a given `digPlan: Seq[Trench]` for which to compute the area,
14+
and let the following classes:
15+
```scala 3
16+
enum Direction:
17+
case Up, Down, Left, Right
18+
19+
case class Trench(dir: Direction, length: Int)
20+
```
21+
22+
We can go through the dig plan keeping track of the current position,
23+
by starting from `(x = 0, y = 0)`, increasing `x` when going right, increasing `y` when going down, and so on.
24+
25+
Provided our current position, we can then keep track of the lagoon area as follows:
26+
- When going `Right`: we count all the points in the line we cover, i.e the `length` of the trench.
27+
- When going `Down`: we count all the points which we leave on the left (or pass over),
28+
i.e. the `length` of the downwards trench times our current `x` coordinate,
29+
`+1` to count the current vertical trench as in the area.
30+
Of course, we may be including too much at this point,
31+
since we do not yet know what part of the left is actually in the lagoon,
32+
but we will account for it later.
33+
- When going `Right`: there is nothing to add,
34+
the position could only have been reached from a downwards trench,
35+
hence the area has already been counted.
36+
- When going `Up`: we now know by how much we had over increased the area when going down,
37+
and can remove everything strictly to the left of the current `x` position.
38+
39+
In summary, we assume we cover everything to the left when going down
40+
and remove the uncovered part when coming back up.
41+
Finally, we must start from an area of `1`as the starting position `(0, 0)` is naturally covered,
42+
but isn't counted by the first trench whichever it may be.
43+
All of which translates to the following foldLeft in scala 😉:
44+
45+
```scala 3
46+
val (_, area) = digPlan.foldLeft((0, 0), 1L):
47+
case (((x, y), area), Trench(dir, len)) => dir match
48+
case Right => ((x + len, y), area + len)
49+
case Down => ((x, y + len), area + (x + 1) * len.toLong)
50+
case Left => ((x - len, y), area)
51+
case Up => ((x, y - len), area - x * len.toLong)
52+
```
53+
54+
Also note we have to use `Long`s to avoid the computations from overflowing.
55+
56+
57+
### Part 1
58+
59+
We can get the `Trench` of each line in the `input: String`,
60+
by parsing the direction from the corresponding character
61+
and ignoring the color of the trench.
62+
And then proceed as above with the obtained `digPlan`.
63+
64+
```scala 3
65+
object Direction:
66+
def fromChar(c: Char): Direction = c match
67+
case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
68+
69+
val digPlan = for
70+
case s"$dirC $len (#$_)" <- input.linesIterator
71+
dir = Direction.fromChar(dirC.head)
72+
yield Trench(dir, len.toInt)
73+
```
74+
75+
### Part 2
76+
77+
We do the same for part 2, except we use the color to
78+
get the trench fields:
79+
its direction from the last digit,
80+
and its length by converting from the hexadecimal encoding of the remaining digits.
81+
82+
```scala 3
83+
object Direction:
84+
def fromInt(i: Char): Direction = i match
85+
case '0' => Right case '1' => Down case '2' => Left case '3' => Up
86+
87+
val digPlan = for
88+
case s"$_ $_ (#$color)" <- input.linesIterator
89+
dir = Direction.fromInt(color.last)
90+
len = BigInt(x = color.init, radix = 16)
91+
yield Trench(dir, len.toInt)
92+
```
93+
94+
## Final code
95+
96+
```scala 3
97+
enum Direction:
98+
case Up, Down, Left, Right
99+
object Direction:
100+
def fromChar(c: Char): Direction = c match
101+
case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
102+
def fromInt(i: Char): Direction = i match
103+
case '0' => Right case '1' => Down case '2' => Left case '3' => Up
104+
import Direction.*
105+
106+
case class Trench(dir: Direction, length: Int)
107+
108+
def area(digPlan: Seq[Trench]): Long =
109+
val (_, area) = digPlan.foldLeft((0, 0), 1L):
110+
case (((x, y), area), Trench(dir, len)) => dir match
111+
case Right => ((x + len, y), area + len)
112+
case Down => ((x, y + len), area + (x + 1) * len.toLong)
113+
case Left => ((x - len, y), area)
114+
case Up => ((x, y - len), area - x * len.toLong)
115+
area
116+
117+
def part1(input: String): String =
118+
val digPlan = for
119+
case s"$dirC $len (#$_)" <- input.linesIterator
120+
dir = Direction.fromChar(dirC.head)
121+
yield Trench(dir, len.toInt)
122+
123+
area(digPlan.toSeq).toString
124+
125+
def part2(input: String): String =
126+
val digPlan = for
127+
case s"$_ $_ (#$color)" <- input.linesIterator
128+
dir = Direction.fromInt(color.last)
129+
len = BigInt(x = color.init, radix = 16)
130+
yield Trench(dir, len.toInt)
131+
132+
area(digPlan.toSeq).toString
133+
```
134+
9135
## Solutions from the community
10136

11137
Share your solution to the Scala community by editing this page.

0 commit comments

Comments
 (0)