Skip to content

Commit 05a3aab

Browse files
committed
Tidy
1 parent d60d19e commit 05a3aab

File tree

1 file changed

+85
-131
lines changed

1 file changed

+85
-131
lines changed

src/main/scala/AdventOfCode2022/Day22.scala

Lines changed: 85 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,107 @@
11
package AdventOfCode2022
22

33
object Day22:
4-
val facing = Seq(Point(1, 0), Point(0, 1), Point(-1, 0), Point(0, -1))
4+
val (right, down, left, up) = (Point(1, 0), Point(0, 1), Point(-1, 0), Point(0, -1))
5+
6+
enum Tile:
7+
case Open, Solid, Wrap
8+
import Tile._
59

610
case class Point(x: Int, y: Int):
711
def +(other: Point): Point = Point(x + other.x, y + other.y)
812
def clockwise: Point = Point(-y, x)
913
def counterClockwise: Point = Point(y, -x)
1014

11-
case class State(position: Point, direction: Point):
12-
def password: Int = 1000 * (position.y + 1) + 4 * (position.x + 1) + facing.indexOf(direction)
13-
14-
def parse(input: Seq[String]): (Seq[Int], Seq[Int], Seq[Int], Seq[Int], Set[Point], Seq[(Int, String)]) =
15-
val prefix = input.dropRight(2)
15+
case class State(position: Point, direction: Point)
1616

17+
def parse(input: Seq[String]): Map[Point, Tile] =
1718
val points = for
18-
(row, y) <- prefix.zipWithIndex
19-
(col, x) <- row.zipWithIndex
20-
if col == '#'
21-
yield Point(x, y)
22-
23-
val maxSize = prefix.map(_.length).max
24-
val padded = prefix.map(_.padTo(maxSize, ' '))
25-
val minX = padded.map(_.segmentLength(_ == ' '))
26-
val maxX = padded.map(row => row.size - row.reverse.segmentLength(_ == ' '))
27-
28-
val tranposed = padded.transpose
29-
val minY = tranposed.map(_.segmentLength(_ == ' '))
30-
val maxY = tranposed.map(col => col.size - col.reverse.segmentLength(_ == ' '))
31-
32-
val numbers = input.last.split("\\D+").map(_.toInt).toSeq
33-
val letters = input.last.split("\\d+").tail.toSeq
34-
val moves = numbers.zipAll(letters, 0, "X")
35-
36-
(minX, maxX, minY, maxY, points.toSet, moves)
37-
end parse
38-
39-
def part1(input: Seq[String]): Int =
40-
val (minX, maxX, minY, maxY, points, moves) = parse(input)
41-
val initial = State(Point(minX.head, 0), Point(1, 0))
42-
43-
val result = moves.foldLeft(initial) { case (State(position, direction), (number, letter)) =>
44-
val nextPosition = (1 to number).foldLeft(position) { (position, _) =>
45-
val next = position + direction
46-
val candidate = direction match
47-
case Point(-1, 0) => if next.x >= minX(next.y) then next else next.copy(x = maxX(next.y) - 1)
48-
case Point(1, 0) => if next.x < maxX(next.y) then next else next.copy(x = minX(next.y))
49-
case Point(0, -1) => if next.y >= minY(next.x) then next else next.copy(y = maxY(next.x) - 1)
50-
case Point(0, 1) => if next.y < maxY(next.x) then next else next.copy(y = minY(next.x))
51-
if points.contains(candidate) then position else candidate
52-
}
19+
(row, y) <- input.dropRight(2).zipWithIndex
20+
(cell, x) <- row.zipWithIndex
21+
if cell != ' '
22+
yield Point(x, y) -> (if cell == '.' then Open else Solid)
23+
points.toMap.withDefaultValue(Wrap)
24+
25+
def follow(points: Map[Point, Tile], path: String, handleWrap: State => State): Int =
26+
val numbers = path.split("\\D+").map(_.toInt).toSeq
27+
val letters = path.split("\\d+").toSeq
28+
val moves = numbers.zip(letters)
29+
30+
val initial = State(Point(50, 0), Point(1, 0))
31+
val result = moves.foldLeft(initial) { case (state, (number, letter)) =>
5332
val nextDirection = letter match
54-
case "L" => direction.counterClockwise
55-
case "R" => direction.clockwise
56-
case _ => direction
57-
State(nextPosition, nextDirection)
58-
}
59-
60-
result.password
61-
end part1
33+
case "L" => state.direction.counterClockwise
34+
case "R" => state.direction.clockwise
35+
case _ => state.direction
6236

63-
def part2(input: Seq[String]): Int =
64-
val (minX, _, _, _, points, moves) = parse(input)
65-
val initial = State(Point(minX.head, 0), Point(1, 0))
66-
67-
val result = moves.foldLeft(initial) { case (state, (number, letter)) =>
68-
val nextState = (1 to number).foldLeft(state) { case (state, _) =>
37+
(1 to number).foldLeft(State(state.position, nextDirection)) { (state, _) =>
6938
val State(position, direction) = state
70-
val cubeX = position.x / 50
71-
val cubeY = position.y / 50
72-
val modX = position.x % 50
73-
val modY = position.y % 50
7439
val next = position + direction
75-
val nextX = if next.x >= 0 then next.x / 50 else next.x
76-
val nextY = if next.y >= 0 then next.y / 50 else next.y
77-
78-
val (candidatePosition, candidateDirection) =
79-
if cubeX == nextX && cubeY == nextY then
80-
(next, direction)
81-
else
82-
(cubeX, cubeY, nextX, nextY) match
83-
// A to F
84-
case (1, 0, 1, -1) => Point(0, 150 + modX) -> Point(1, 0)
85-
// A to C
86-
case (1, 0, 1, 1) => next -> direction
87-
// A to E
88-
case (1, 0, 0, 0) => Point(0, 149 - modY) -> Point(1, 0)
89-
// A to B
90-
case (1, 0, 2, 0) => next -> direction
91-
92-
// B to F
93-
case (2, 0, 2, -1) => Point(0 + modX, 199) -> direction
94-
// B to C
95-
case (2, 0, 2, 1) => Point(99, 50 + modX) -> Point(-1, 0)
96-
// B to A
97-
case (2, 0, 1, 0) => next -> direction
98-
// B to D
99-
case (2, 0, 3, 0) => Point(99, 149 - modY) -> Point(-1, 0)
100-
101-
// C to A
102-
case (1, 1, 1, 0) => next -> direction
103-
// C to D
104-
case (1, 1, 1, 2) => next -> direction
105-
// C to E
106-
case (1, 1, 0, 1) => Point(modY, 100) -> Point(0, 1)
107-
// C to B
108-
case (1, 1, 2, 1) => Point(100 + modY, 49) -> Point(0, -1)
109-
110-
// D to C
111-
case (1, 2, 1, 1) => next -> direction
112-
// D to F
113-
case (1, 2, 1, 3) => Point(49, 150 + modX) -> Point(-1, 0)
114-
// D to E
115-
case (1, 2, 0, 2) => next -> direction
116-
// D to B
117-
case (1, 2, 2, 2) => Point(149, 49 - modY) -> Point(-1, 0)
118-
119-
// E to C
120-
case (0, 2, 0, 1) => Point(50, 50 + modX) -> Point(1, 0)
121-
// E to F
122-
case (0, 2, 0, 3) => next -> direction
123-
// E to A
124-
case (0, 2, -1, 2) => Point(50, 49 - modY) -> Point(1, 0)
125-
// E to D
126-
case (0, 2, 1, 2) => next -> direction
127-
128-
// F to E
129-
case (0, 3, 0, 2) => next -> direction
130-
// F to B
131-
case (0, 3, 0, 4) => Point(100 + modX, 0) -> direction
132-
// F to A
133-
case (0, 3, -1, 3) => Point(50 + modY, 0) -> Point(0, 1)
134-
// F to D
135-
case (0, 3, 1, 3) => Point(50 + modY, 149) -> Point(0, -1)
136-
end match
137-
end if
138-
139-
if points.contains(candidatePosition) then state else State(candidatePosition, candidateDirection)
40+
points(next) match
41+
case Open => State(next, direction)
42+
case Solid => state
43+
case Wrap => handleWrap(state)
14044
}
45+
}
14146

142-
val nextDirection = letter match
143-
case "L" => nextState.direction.counterClockwise
144-
case "R" => nextState.direction.clockwise
145-
case _ => nextState.direction
47+
val facing = Seq(right, down, left, up)
48+
1000 * (result.position.y + 1) + 4 * (result.position.x + 1) + facing.indexOf(result.direction)
49+
end follow
14650

147-
State(nextState.position, nextDirection)
148-
}
51+
def part1(input: Seq[String]): Int =
52+
val tiles = parse(input)
53+
54+
val valid = tiles.filterNot((k, v) => v == Wrap).keySet
55+
val minX = valid.groupMapReduce(_.y)(_.x)(_ min _)
56+
val maxX = valid.groupMapReduce(_.y)(_.x)(_ max _)
57+
val minY = valid.groupMapReduce(_.x)(_.y)(_ min _)
58+
val maxY = valid.groupMapReduce(_.x)(_.y)(_ max _)
59+
60+
def handleWrap(state: State): State =
61+
val State(position, direction) = state
62+
val nextPosition = direction match
63+
case `right` => Point(minX(position.y), position.y)
64+
case `left` => Point(maxX(position.y), position.y)
65+
case `down` => Point(position.x, minY(position.x))
66+
case `up` => Point(position.x, maxY(position.x))
67+
if tiles(nextPosition) == Open then State(nextPosition, direction) else state
68+
69+
follow(tiles, input.last, handleWrap)
70+
end part1
14971

150-
result.password
72+
def part2(input: Seq[String]): Int =
73+
val tiles = parse(input)
74+
75+
def handleWrap(state: State): State =
76+
val State(position, direction) = state
77+
val (cubeX, cubeY) = (position.x / 50, position.y / 50)
78+
val (modX, modY) = (position.x % 50, position.y % 50)
79+
80+
// Cube faces:
81+
// BA
82+
// C
83+
// ED
84+
// F
85+
val (nextPosition, nextDirection) = (cubeX, cubeY, direction) match
86+
case (2, 0, `up`) => Point(modX, 199) -> up // A to F
87+
case (2, 0, `down`) => Point(99, 50 + modX) -> left // A to C
88+
case (2, 0, `right`) => Point(99, 149 - modY) -> left // A to D
89+
case (1, 0, `up`) => Point(0, 150 + modX) -> right // B to F
90+
case (1, 0, `left`) => Point(0, 149 - modY) -> right // B to E
91+
case (1, 1, `left`) => Point(modY, 100) -> down // C to E
92+
case (1, 1, `right`) => Point(100 + modY, 49) -> up // C to A
93+
case (1, 2, `down`) => Point(49, 150 + modX) -> left // D to F
94+
case (1, 2, `right`) => Point(149, 49 - modY) -> left // D to A
95+
case (0, 2, `up`) => Point(50, 50 + modX) -> right // E to C
96+
case (0, 2, `left`) => Point(50, 49 - modY) -> right // E to B
97+
case (0, 3, `down`) => Point(100 + modX, 0) -> down // F to A
98+
case (0, 3, `left`) => Point(50 + modY, 0) -> down // F to B
99+
case (0, 3, `right`) => Point(50 + modY, 149) -> up // F to D
100+
101+
if tiles(nextPosition) == Open then State(nextPosition, nextDirection) else state
102+
end handleWrap
103+
104+
follow(tiles, input.last, handleWrap)
151105
end part2
152106

153107
def main(args: Array[String]): Unit =

0 commit comments

Comments
 (0)