|
1 | 1 | package AdventOfCode2022
|
2 | 2 |
|
3 | 3 | object Day24:
|
4 |
| - case class Point(x: Int, y: Int, z: Int): |
5 |
| - def +(other: Point): Point = Point(x + other.x, y + other.y, z + other.z) |
6 |
| - |
7 |
| - def part1(input: Seq[String]): Int = 123 |
8 |
| - |
9 |
| - def part2(input: Seq[String]): Int = 456 |
10 |
| - |
11 |
| - def parse(input: Seq[String]): (Set[Point], Set[Point], Set[Point], Set[Point], Set[Point]) = |
12 |
| - val right = for y <- input.indices; x <- input.head.indices if input(y)(x) == '>' yield Point(x, y, 0) |
13 |
| - val down = for y <- input.indices; x <- input.head.indices if input(y)(x) == 'v' yield Point(x, y, 0) |
14 |
| - val left = for y <- input.indices; x <- input.head.indices if input(y)(x) == '<' yield Point(x, y, 0) |
15 |
| - val up = for y <- input.indices; x <- input.head.indices if input(y)(x) == '^' yield Point(x, y, 0) |
16 |
| - val wall = for y <- input.indices; x <- input.head.indices if input(y)(x) == '#' yield Point(x, y, 0) |
17 |
| - (right.toSet, down.toSet, left.toSet, up.toSet, wall.toSet) |
| 4 | + val moves = Seq(Point(0, 0), Point(1, 0), Point(-1, 0), Point(0, 1), Point(0, -1)) |
| 5 | + |
| 6 | + case class Point(x: Int, y: Int): |
| 7 | + def +(other: Point): Point = Point(x + other.x, y + other.y) |
| 8 | + def neighbours: Seq[Point] = moves.map(_ + this) |
| 9 | + |
| 10 | + def parse(input: Seq[String]): (Point, Point, (Set[Point], Point, Int) => Int) = |
| 11 | + val width = input.head.size - 2 |
| 12 | + val height = input.size - 2 |
| 13 | + |
| 14 | + def mod(a: Int, m: Int): Int = |
| 15 | + val remainder = (a - 1) % m |
| 16 | + if remainder < 0 then remainder + m + 1 else remainder + 1 |
| 17 | + |
| 18 | + def valid(time: Int)(point: Point): Boolean = |
| 19 | + val Point(x, y) = point |
| 20 | + input.indices.contains(y) |
| 21 | + && input(y)(x) != '#' |
| 22 | + && input(y)(mod(x + time, width)) != '<' |
| 23 | + && input(y)(mod(x - time, width)) != '>' |
| 24 | + && input(mod(y + time, height))(x) != '^' |
| 25 | + && input(mod(y - time, height))(x) != 'v' |
| 26 | + |
| 27 | + def simulate(points: Set[Point], end: Point, time: Int): Int = |
| 28 | + if points.contains(end) then time else |
| 29 | + val next = points.flatMap(_.neighbours).filter(valid(time + 1)) |
| 30 | + simulate(next, end, time + 1) |
| 31 | + |
| 32 | + (Point(1, 0), Point(width, height + 1), simulate) |
| 33 | + end parse |
| 34 | + |
| 35 | + def part1(input: Seq[String]): Int = |
| 36 | + val (start, end, simulate) = parse(input) |
| 37 | + simulate(Set(start), end, 0) |
| 38 | + |
| 39 | + def part2(input: Seq[String]): Int = |
| 40 | + val (start, end, simulate) = parse(input) |
| 41 | + val time1 = simulate(Set(start), end, 0) |
| 42 | + val time2 = simulate(Set(end), start, time1) |
| 43 | + simulate(Set(start), end, time2) |
18 | 44 |
|
19 | 45 | def main(args: Array[String]): Unit =
|
20 |
| - // val data = sample |
21 |
| - // val minX = 1 |
22 |
| - // val maxX = 6 |
23 |
| - // val minY = 1 |
24 |
| - // val maxY = 4 |
25 |
| - |
26 | 46 | val data = io.Source.fromResource("AdventOfCode2022/Day24.txt").getLines().toSeq
|
27 |
| - val minX = 1 |
28 |
| - val maxX = 120 |
29 |
| - val minY = 1 |
30 |
| - val maxY = 25 |
31 |
| - |
32 |
| - val points = collection.mutable.Set[Point]() |
33 |
| - var (right, down, left, up, wall) = parse(data) |
34 |
| - wall = wall + Point(1, -1, 0) |
35 |
| - points ++= right |
36 |
| - points ++= down |
37 |
| - points ++= left |
38 |
| - points ++= down |
39 |
| - points ++= wall |
40 |
| - |
41 |
| - for _ <- 1 to 1000 do // Guess max z value |
42 |
| - right = right.map { cur => |
43 |
| - if cur.x == maxX then Point(minX, cur.y, cur.z + 1) else Point(cur.x + 1, cur.y, cur.z + 1) |
44 |
| - } |
45 |
| - left = left.map { cur => |
46 |
| - if cur.x == minX then Point(maxX, cur.y, cur.z + 1) else Point(cur.x - 1, cur.y, cur.z + 1) |
47 |
| - } |
48 |
| - up = up.map { cur => |
49 |
| - if cur.y == minY then Point(cur.x, maxY, cur.z + 1) else Point(cur.x, cur.y - 1, cur.z + 1) |
50 |
| - } |
51 |
| - down = down.map { cur => |
52 |
| - if cur.y == maxY then Point(cur.x, minY, cur.z + 1) else Point(cur.x, cur.y + 1, cur.z + 1) |
53 |
| - } |
54 |
| - wall = wall.map { cur => |
55 |
| - Point(cur.x, cur.y, cur.z + 1) |
56 |
| - } |
57 |
| - points ++= right |
58 |
| - points ++= down |
59 |
| - points ++= left |
60 |
| - points ++= up |
61 |
| - points ++= wall |
62 |
| - end for |
63 |
| - |
64 |
| - def bfs(start: Point, end: Point): Int = |
65 |
| - val todo = collection.mutable.Queue(start) |
66 |
| - val cost = collection.mutable.Map(start -> 0) |
67 |
| - |
68 |
| - while todo.nonEmpty do |
69 |
| - val cur = todo.dequeue() |
70 |
| - |
71 |
| - if cur.x == end.x && cur.y == end.y then return cost(cur) |
72 |
| - |
73 |
| - val neighbours = Seq( |
74 |
| - Point(cur.x, cur.y, cur.z + 1), |
75 |
| - Point(cur.x - 1, cur.y, cur.z + 1), |
76 |
| - Point(cur.x + 1, cur.y, cur.z + 1), |
77 |
| - Point(cur.x, cur.y - 1, cur.z + 1), |
78 |
| - Point(cur.x, cur.y + 1, cur.z + 1), |
79 |
| - ) |
80 |
| - |
81 |
| - neighbours.filterNot(cost.contains).filterNot(points.contains).foreach { next => |
82 |
| - todo += next |
83 |
| - cost(next) = cost(cur) + 1 |
84 |
| - } |
85 |
| - end while |
86 |
| - |
87 |
| - -1 |
88 |
| - end bfs |
89 |
| - |
90 |
| - val time1 = bfs(Point(1, 0, 0), Point(maxX, maxY + 1, 0)) |
91 |
| - println(time1) |
92 |
| - val time2 = bfs(Point(maxX, maxY + 1, time1), Point(1, 0, 0)) |
93 |
| - println(time2) |
94 |
| - val time3 = bfs(Point(1, 0, time1 + time2), Point(maxX, maxY + 1, 0)) |
95 |
| - println(time3) |
96 |
| - println(time1 + time2 + time3) |
97 |
| - end main |
| 47 | + println(part1(data)) |
| 48 | + println(part2(data)) |
0 commit comments