|
| 1 | +package de.ronny_h.aoc.year2017.day20 |
| 2 | + |
| 3 | +import de.ronny_h.aoc.AdventOfCode |
| 4 | +import de.ronny_h.aoc.extensions.combinations |
| 5 | +import de.ronny_h.aoc.extensions.numbers.sumOfFirstNaturalNumbers |
| 6 | +import de.ronny_h.aoc.extensions.threedim.Vector |
| 7 | +import de.ronny_h.aoc.extensions.threedim.Vector.Companion.ZERO |
| 8 | +import de.ronny_h.aoc.extensions.threedim.times |
| 9 | + |
| 10 | +fun main() = ParticleSwarm().run(344, 404) |
| 11 | + |
| 12 | +class ParticleSwarm : AdventOfCode<Int>(2017, 20) { |
| 13 | + override fun part1(input: List<String>): Int { |
| 14 | + val allParticles = input.parseParticles() |
| 15 | + |
| 16 | + // in the long term, the particles with minimal absolute acceleration stay closest to ZERO |
| 17 | + val sorted = allParticles.withIndex().sortedBy { it.value.acceleration.abs() } |
| 18 | + val minAcceleration = sorted.first().value.acceleration.abs() |
| 19 | + var particles = sorted.takeWhile { it.value.acceleration.abs() == minAcceleration } |
| 20 | + |
| 21 | + particles.forEach { println("${it.index}: ${it.value.acceleration.abs()} - ${it.value}") } |
| 22 | + |
| 23 | + // for equal absolute acceleration, their direction and initial velocity are relevant |
| 24 | + // -> simulate a significant amount of ticks |
| 25 | + var closest = 0 |
| 26 | + var oldClosest: Int |
| 27 | + var closestIsTheSameCount = 0 |
| 28 | + var iterations = 0 |
| 29 | + |
| 30 | + while (closestIsTheSameCount < 10000) { |
| 31 | + iterations++ |
| 32 | + oldClosest = closest |
| 33 | + particles = particles.map { IndexedValue(it.index, it.value.update()) } |
| 34 | + closest = particles.map { IndexedValue(it.index, it.value.position taxiDistanceTo ZERO) } |
| 35 | + .minBy { it.value } |
| 36 | + .index |
| 37 | + closestIsTheSameCount += if (oldClosest == closest) 1 else 0 |
| 38 | + } |
| 39 | + |
| 40 | + println("found closest in $iterations iterations") |
| 41 | + return closest |
| 42 | + } |
| 43 | + |
| 44 | + override fun part2(input: List<String>): Int { |
| 45 | + // for one particle: |
| 46 | + // p_i = p_0 + i*v_0 + (i*(i+1)/2)*a // i*(i+1)/2 = sumOfFirstNaturalNumbers(i) |
| 47 | + // |
| 48 | + // particles q, r collide in iteration i when: |
| 49 | + // p_q_i == p_r_i |
| 50 | + // <=> p_q_0 + i*v_q_0 + (i*(i+1)/2)*a_q == p_r_0 + i*v_r_0 + (i*(i+1)/2)*a_r |
| 51 | + // <=> 0 == p_q_0 - p_r_0 + i*(v_q_0 - v_r_0) + (i*(i+1)/2)*(a_q - a_r) |
| 52 | + |
| 53 | + val particles = input.parseParticles().withIndex().toList() |
| 54 | + val toRemove = mutableSetOf<Int>() |
| 55 | + |
| 56 | + for (i in 0..100) { |
| 57 | + particles |
| 58 | + .filterNot { it.index in toRemove } |
| 59 | + .combinations() |
| 60 | + .forEach { |
| 61 | + if (ZERO == differenceOf(it.first.value, it.second.value, i)) { |
| 62 | + toRemove.add(it.first.index) |
| 63 | + toRemove.add(it.second.index) |
| 64 | + } |
| 65 | + } |
| 66 | + if (i % 10 == 0) { |
| 67 | + println("$i: ${particles.size - toRemove.size}") |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + return particles.size - toRemove.size |
| 72 | + } |
| 73 | + |
| 74 | + private fun differenceOf(q: Particle, r: Particle, i: Int): Vector { |
| 75 | + return q.position - r.position + i * (q.velocity - r.velocity) + sumOfFirstNaturalNumbers(i) * (q.acceleration - r.acceleration) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +data class Particle(val position: Vector, val velocity: Vector, val acceleration: Vector) { |
| 80 | + fun update(): Particle { |
| 81 | + val newVelocity = velocity + acceleration |
| 82 | + return Particle(position + newVelocity, newVelocity, acceleration) |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +fun List<String>.parseParticles() = map { |
| 87 | + val (p, v, a) = it.split(", ") |
| 88 | + Particle( |
| 89 | + position = p.toVector("p"), |
| 90 | + velocity = v.toVector("v"), |
| 91 | + acceleration = a.toVector("a"), |
| 92 | + ) |
| 93 | +} |
| 94 | + |
| 95 | +private fun String.toVector(name: String): Vector { |
| 96 | + val (x, y, z) = substringAfter("$name=<") |
| 97 | + .substringBefore(">") |
| 98 | + .split(",") |
| 99 | + .map(String::trim) |
| 100 | + .map(String::toLong) |
| 101 | + return Vector(x, y, z) |
| 102 | +} |
0 commit comments