Skip to content

Commit a8570dd

Browse files
Add day18 explanations
1 parent a33694f commit a8570dd

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

docs/2023/puzzles/day18.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,137 @@ 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 to `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 whatever 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+
63+
And then proceed as above with the obtained `digPlan`.
64+
65+
```scala 3
66+
object Direction:
67+
def fromChar(c: Char): Direction = c match
68+
case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
69+
70+
val digPlan = for
71+
case s"$dirC $len (#$_)" <- input.linesIterator
72+
dir = Direction.fromChar(dirC.head)
73+
yield Trench(dir, len.toInt)
74+
```
75+
76+
### Part 2
77+
78+
We do the same for part 2, except we use the color to
79+
get the trench fields:
80+
the direction from the last digit,
81+
and the length by converting from the hexadecimal encoding of remaining digits.
82+
83+
```scala 3
84+
object Direction:
85+
def fromInt(i: Char): Direction = i match
86+
case '0' => Right case '1' => Down case '2' => Left case '3' => Up
87+
88+
val digPlan = for
89+
case s"$_ $_ (#$color)" <- input.linesIterator
90+
dir = Direction.fromInt(color.last)
91+
len = BigInt(x = color.init, radix = 16)
92+
yield Trench(dir, len.toInt)
93+
```
94+
95+
## Final code
96+
97+
```scala 3
98+
enum Direction:
99+
case Up, Down, Left, Right
100+
object Direction:
101+
def fromChar(c: Char): Direction = c match
102+
case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
103+
def fromInt(i: Char): Direction = i match
104+
case '0' => Right case '1' => Down case '2' => Left case '3' => Up
105+
import Direction.*
106+
107+
case class Trench(dir: Direction, length: Int)
108+
109+
def area(digPlan: Seq[Trench]): Long =
110+
val (_, area) = digPlan.foldLeft((0, 0), 1L):
111+
case (((x, y), area), Trench(dir, len)) => dir match
112+
case Right => ((x + len, y), area + len)
113+
case Down => ((x, y + len), area + (x + 1) * len.toLong)
114+
case Left => ((x - len, y), area)
115+
case Up => ((x, y - len), area - x * len.toLong)
116+
area
117+
118+
def part1(input: String): String =
119+
val digPlan = for
120+
case s"$dirC $len (#$_)" <- input.linesIterator
121+
dir = Direction.fromChar(dirC.head)
122+
yield Trench(dir, len.toInt)
123+
124+
area(digPlan.toSeq).toString
125+
126+
def part2(input: String): String =
127+
val digPlan = for
128+
case s"$_ $_ (#$color)" <- input.linesIterator
129+
dir = Direction.fromInt(color.last)
130+
len = BigInt(x = color.init, radix = 16)
131+
yield Trench(dir, len.toInt)
132+
133+
area(digPlan.toSeq).toString
134+
```
135+
9136
## Solutions from the community
10137

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

0 commit comments

Comments
 (0)