Skip to content

Commit ecfe90b

Browse files
committed
Year 2019 Day 12
1 parent fdc4611 commit ecfe90b

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,4 @@ Entries for the annual [Advent of Code](https://adventofcode.com/) challenge, wr
7979
| 9 | [Sensor Boost](https://adventofcode.com/2019/day/9) | [Source](src/main/scala/AdventOfCode2019/Day09.scala) |
8080
| 10 | [Monitoring Station](https://adventofcode.com/2019/day/10) | [Source](src/main/scala/AdventOfCode2019/Day10.scala) |
8181
| 11 | [Space Police](https://adventofcode.com/2019/day/11) | [Source](src/main/scala/AdventOfCode2019/Day11.scala) |
82+
| 12 | [The N-Body Problem](https://adventofcode.com/2019/day/12) | [Source](src/main/scala/AdventOfCode2019/Day12.scala) |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<x=17, y=-12, z=13>
2+
<x=2, y=1, z=1>
3+
<x=-1, y=-17, z=7>
4+
<x=12, y=-14, z=18>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package AdventOfCode2019
2+
3+
import scala.annotation.tailrec
4+
5+
object Day12:
6+
case class Point(x: Int, y: Int, z: Int):
7+
def +(other: Point): Point = Point(x + other.x, y + other.y, z + other.z)
8+
def -(other: Point): Point = Point(x - other.x, y - other.y, z - other.z)
9+
def sign: Point = Point(x.sign, y.sign, z.sign)
10+
def manhattan: Int = x.abs + y.abs + z.abs
11+
12+
case class Moon(position: Point, velocity: Point):
13+
def energy: Int = position.manhattan * velocity.manhattan
14+
15+
def parse(input: Seq[String]): Seq[Moon] =
16+
val regex = """<x=(-?\d+), y=(-?\d+), z=(-?\d+)>""".r
17+
input.map { case regex(x, y, z) => Moon(Point(x.toInt, y.toInt, z.toInt), Point(0, 0, 0)) }
18+
19+
def step(moons: Seq[Moon]): Seq[Moon] = moons.map { moon =>
20+
val velocity = moons.foldLeft(moon.velocity)((total, next) => total + (next.position - moon.position).sign)
21+
Moon(moon.position + velocity, velocity)
22+
}
23+
24+
def gcd(a: Long, b: Long): Long = if a % b == 0 then b else gcd(b, a % b)
25+
26+
def lcm(a: Long, b: Long): Long = a * b / gcd(a, b)
27+
28+
def part1(input: Seq[String]): Int = Iterator.iterate(parse(input))(step).drop(1000).next().map(_.energy).sum
29+
30+
def part2(input: Seq[String]): Long =
31+
@tailrec
32+
def halfPeriod(moons: Seq[Moon], dimension: Point => Int, count: Int = 1): Long =
33+
val next = step(moons)
34+
if next.map(_.velocity).map(dimension).forall(_ == 0) then count else halfPeriod(next, dimension, count + 1)
35+
36+
val initial = parse(input)
37+
val halfPeriodX = halfPeriod(initial, (p: Point) => p.x)
38+
val halfPeriodY = halfPeriod(initial, (p: Point) => p.y)
39+
val halfPeriodZ = halfPeriod(initial, (p: Point) => p.z)
40+
41+
2 * lcm(lcm(halfPeriodX, halfPeriodY), halfPeriodZ)
42+
end part2
43+
44+
def main(args: Array[String]): Unit =
45+
val data = io.Source.fromResource("AdventOfCode2019/Day12.txt").getLines().toSeq
46+
println(part1(data))
47+
println(part2(data))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package AdventOfCode2019
2+
3+
import org.scalatest.funsuite.AnyFunSuite
4+
5+
class Day12Suite extends AnyFunSuite:
6+
val sample = Seq(
7+
"<x=-1, y=0, z=2>",
8+
"<x=2, y=-10, z=-7>",
9+
"<x=4, y=-8, z=8>",
10+
"<x=3, y=5, z=-1>")
11+
12+
test("Part 1 should handle sample input correctly") {
13+
assert(Day12.part1(sample) == 183)
14+
}
15+
16+
test("Part 2 should handle sample input correctly") {
17+
assert(Day12.part2(sample) == 2772)
18+
}

0 commit comments

Comments
 (0)