Advertisement
Guest User

AoC 2021, day 19

a guest
Dec 19th, 2021
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 10.94 KB | None | 0 0
  1. package day19
  2.  
  3. import common.Point3
  4. import kotlin.math.abs
  5.  
  6. import kotlin.math.abs
  7.  
  8. data class Point3(
  9.     val x: Int,
  10.     val y: Int,
  11.     val z: Int
  12. ) {
  13.  
  14.     operator fun minus(point3: Point3): Point3 {
  15.         return Point3(
  16.             x - point3.x,
  17.             y - point3.y,
  18.             z - point3.z
  19.         )
  20.     }
  21.  
  22.     operator fun plus(point3: Point3): Point3 {
  23.         return Point3(
  24.             x + point3.x,
  25.             y + point3.y,
  26.             z + point3.z
  27.         )
  28.     }
  29.  
  30.     fun deltaAbs(point3: Point3): Point3 {
  31.         return Point3(
  32.             abs(x - point3.x),
  33.             abs(y - point3.y),
  34.             abs(z - point3.z)
  35.         )
  36.  
  37.     }
  38. }
  39.  
  40. enum class Axis(
  41.     val originalName: String
  42. ) {
  43.     X("x"),
  44.     Y("y"),
  45.     Z("z"),
  46.     X_NEGATIVE("x"),
  47.     Y_NEGATIVE("y"),
  48.     Z_NEGATIVE("z")
  49. }
  50.  
  51. private val defaultConfiguration = RotationConfiguration(
  52.     facing = Axis.X,
  53.     up = Axis.Z
  54. )
  55.  
  56. private val allConfigurations = Axis.values().flatMap { axis ->
  57.     (Axis.values().toList() - axis).mapNotNull { upAxis ->
  58.  
  59.         if (upAxis.originalName == axis.originalName) {
  60.             return@mapNotNull null
  61.         }
  62.  
  63.         RotationConfiguration(
  64.             facing = axis,
  65.             up = upAxis
  66.         )
  67.     }
  68. }
  69.  
  70.  
  71. data class RotationConfiguration(
  72.     val facing: Axis,
  73.     val up: Axis
  74. ) {
  75.  
  76.     fun apply(point: Point3): Point3 {
  77.         val x = when (facing) {
  78.             Axis.X -> point.x
  79.             Axis.Y -> point.y
  80.             Axis.Z -> point.z
  81.             Axis.X_NEGATIVE -> -point.x
  82.             Axis.Y_NEGATIVE -> -point.y
  83.             Axis.Z_NEGATIVE -> -point.z
  84.         }
  85.  
  86.         val y = when (facing) {
  87.             Axis.X -> when (up) {
  88.                 Axis.X -> throw IllegalStateException()
  89.                 Axis.Y -> -point.z
  90.                 Axis.Z -> point.y
  91.                 Axis.X_NEGATIVE -> throw IllegalStateException()
  92.                 Axis.Y_NEGATIVE -> point.z
  93.                 Axis.Z_NEGATIVE -> -point.y
  94.             }
  95.  
  96.             Axis.Y -> when (up) {
  97.                 Axis.X -> point.z
  98.                 Axis.Y -> throw IllegalStateException()
  99.                 Axis.Z -> -point.x
  100.                 Axis.X_NEGATIVE -> -point.z
  101.                 Axis.Y_NEGATIVE -> throw IllegalStateException()
  102.                 Axis.Z_NEGATIVE -> point.x
  103.             }
  104.  
  105.             Axis.Z -> when (up) {
  106.                 Axis.X -> -point.y
  107.                 Axis.Y -> point.x
  108.                 Axis.Z -> throw IllegalStateException()
  109.                 Axis.X_NEGATIVE -> point.y
  110.                 Axis.Y_NEGATIVE -> -point.x
  111.                 Axis.Z_NEGATIVE -> throw IllegalStateException()
  112.             }
  113.  
  114.             Axis.X_NEGATIVE -> when (up) {
  115.                 Axis.X -> throw IllegalStateException()
  116.                 Axis.Y -> point.z
  117.                 Axis.Z -> -point.y
  118.                 Axis.X_NEGATIVE -> throw IllegalStateException()
  119.                 Axis.Y_NEGATIVE -> -point.z
  120.                 Axis.Z_NEGATIVE -> point.y
  121.             }
  122.  
  123.             Axis.Y_NEGATIVE -> when (up) {
  124.                 Axis.X -> -point.z
  125.                 Axis.Y -> throw IllegalStateException()
  126.                 Axis.Z -> point.x
  127.                 Axis.X_NEGATIVE -> point.z
  128.                 Axis.Y_NEGATIVE -> throw IllegalStateException()
  129.                 Axis.Z_NEGATIVE -> -point.x
  130.             }
  131.  
  132.             Axis.Z_NEGATIVE -> when (up) {
  133.                 Axis.X -> point.y
  134.                 Axis.Y -> -point.x
  135.                 Axis.Z -> throw IllegalStateException()
  136.                 Axis.X_NEGATIVE -> -point.y
  137.                 Axis.Y_NEGATIVE -> point.x
  138.                 Axis.Z_NEGATIVE -> throw IllegalStateException()
  139.             }
  140.         }
  141.  
  142.         val z = when (up) {
  143.             Axis.X -> point.x
  144.             Axis.Y -> point.y
  145.             Axis.Z -> point.z
  146.             Axis.X_NEGATIVE -> -point.x
  147.             Axis.Y_NEGATIVE -> -point.y
  148.             Axis.Z_NEGATIVE -> -point.z
  149.         }
  150.  
  151.         return Point3(x, y, z)
  152.     }
  153. }
  154.  
  155.  
  156. private data class Deltas(
  157.     private val deltas: Map<RotationConfiguration, Map<Pair<Point3, Point3>, Point3>>
  158. ) {
  159.  
  160.     operator fun get(rotation: RotationConfiguration): Map<Pair<Point3, Point3>, Point3> {
  161.         return deltas[rotation]!!
  162.     }
  163.  
  164.     fun points(): Points {
  165.         return Points(
  166.             deltas.mapValues { (_, value) -> getPointsMap(value) }
  167.         )
  168.     }
  169.  
  170.     private fun getPointsMap(map: Map<Pair<Point3, Point3>, Point3>): Map<Point3, List<Pair<Point3, Point3>>> {
  171.         return map.entries.groupBy { (_, delta) ->
  172.             delta
  173.         }.mapValues { (_, value) ->
  174.             value.map { (pair, _) ->
  175.                 pair
  176.             }
  177.         }
  178.     }
  179.  
  180. }
  181.  
  182. private data class Points(
  183.     val points: Map<RotationConfiguration, Map<Point3, List<Pair<Point3, Point3>>>>
  184. ) {
  185.     operator fun get(rotation: RotationConfiguration): Map<Point3, List<Pair<Point3, Point3>>> {
  186.         return points[rotation]!!
  187.     }
  188. }
  189.  
  190. private data class Scanner(
  191.     val name: String,
  192.     val beacons: List<Point3>
  193. ) {
  194.  
  195.     private fun delta(rotation: RotationConfiguration): Map<Pair<Point3, Point3>, Point3> {
  196.         val appliedBeacons = beacons.map {
  197.             rotation.apply(it)
  198.         }
  199.  
  200.         val mutableMap = mutableMapOf<Pair<Point3, Point3>, Point3>()
  201.  
  202.         appliedBeacons.forEach { beaconA ->
  203.             appliedBeacons.forEach innerForEach@{ beaconB ->
  204.                 if (beaconA == beaconB) {
  205.                     return@innerForEach
  206.                 }
  207.  
  208.                 val delta = beaconA - beaconB
  209.  
  210.                 mutableMap[beaconA to beaconB] = delta
  211.             }
  212.         }
  213.  
  214.         return mutableMap
  215.     }
  216.  
  217.     fun deltas(): Deltas {
  218.         return Deltas(
  219.             allConfigurations.map { configuration ->
  220.                 configuration to delta(configuration)
  221.             }.toMap()
  222.         )
  223.     }
  224. }
  225.  
  226.  
  227. private fun String.toScanner(): Scanner {
  228.     val lines = split("\n")
  229.  
  230.     val name = lines[0].removeSuffix(" ---").removePrefix("--- ")
  231.  
  232.     val beacons = lines.drop(1).map {
  233.         val parts = it.split(",")
  234.         Point3(
  235.             parts[0].toInt(),
  236.             parts[1].toInt(),
  237.             parts[2].toInt()
  238.         )
  239.     }
  240.  
  241.     return Scanner(name, beacons)
  242. }
  243.  
  244. fun main() {
  245.     val scanners = input.split("\n\n").map {
  246.         it.toScanner()
  247.     }
  248.  
  249.     val scannerOrigins = mutableSetOf<Point3>()
  250.  
  251.     val scannerDeltas = scanners.map {
  252.         it to it.deltas()
  253.     }.toMap().toMutableMap()
  254.  
  255.     val deltasToPoints = scannerDeltas.map { (scanner, deltas) ->
  256.         scanner to deltas.points()
  257.     }.toMap().toMutableMap()
  258.  
  259.     val knownScanners = mutableListOf(
  260.         scanners.first()
  261.     ).toMutableList()
  262.  
  263.     val unknownScanners = (scanners.toMutableList() - knownScanners).toMutableList()
  264.  
  265.     while (unknownScanners.isNotEmpty()) {
  266.         var knownScannerToAdd: Scanner? = null
  267.         var unknownScannerToRemove: Scanner? = null
  268.  
  269.         println("\nStarting new iteration...")
  270.         var foundSomething = false
  271.  
  272.         for (knownScanner in knownScanners) {
  273.             if (foundSomething) {
  274.                 break
  275.             }
  276.  
  277.             unknownScanners.forEach { unknownScanner ->
  278.                 val knownDelta = scannerDeltas[knownScanner]!![defaultConfiguration]
  279.                 val unknownDeltaAll = scannerDeltas[unknownScanner]!!
  280.  
  281.                 val pointsForDeltaKnown = deltasToPoints[knownScanner]!![defaultConfiguration]
  282.                 val pointsForDeltaUnknownAll = deltasToPoints[unknownScanner]!!
  283.  
  284.                 // figure out whether they're looking at the same drones
  285.                 // and at which orientation they are
  286.                 val rotations = allConfigurations
  287.                 val rotationAndIntersectingValues = rotations.firstNotNullOfOrNull { rotation ->
  288.                     unknownDeltaAll[rotation].values.intersect(knownDelta.values).takeIf {
  289.                         it.size >= REQUIRED_INTERSECTIONS
  290.                     }?.let {
  291.                         rotation to it
  292.                     }
  293.                 }
  294.  
  295.                 val intersectingValues = rotationAndIntersectingValues?.second
  296.                 val unknownRotation = rotationAndIntersectingValues?.first
  297.  
  298.                 val pointsForDeltaUnknown = unknownRotation?.let {
  299.                     pointsForDeltaUnknownAll[it]
  300.                 }
  301.  
  302.                 if (pointsForDeltaUnknown != null && intersectingValues != null && intersectingValues.size >= REQUIRED_INTERSECTIONS) {
  303.                     // we already know that their axises are aligned at this point
  304.                     val commonDelta = intersectingValues.first()
  305.  
  306.                     val (knownPoint1, _) = pointsForDeltaKnown[commonDelta]!!.single()
  307.                     val (unknownPoint1, _) = pointsForDeltaUnknown[commonDelta]!!.single()
  308.  
  309.                     val (dx, dy, dz) = knownPoint1 - unknownPoint1
  310.                     println("Figured out that ${unknownScanner.name} is at ($dx, $dy, $dz)")
  311.  
  312.                     scannerOrigins.add(Point3(dx, dy, dz))
  313.  
  314.                     println("${unknownScanners.size - 1} scanners left...")
  315.                     println("Marking scanner ${unknownScanner.name} as known, removing it from unknown scanners list")
  316.                     unknownScannerToRemove = unknownScanner
  317.  
  318.                     val transform: (Point3) -> Point3 = { point ->
  319.                         val transformed = unknownRotation.apply(point)
  320.  
  321.                         with (transformed) {
  322.                             Point3(
  323.                                 x + dx,
  324.                                 y + dy,
  325.                                 z + dz
  326.                             )
  327.                         }
  328.                     }
  329.  
  330.                     knownScannerToAdd = Scanner(
  331.                         name = unknownScanner.name,
  332.                         beacons = unknownScanner.beacons.map(transform)
  333.                     )
  334.  
  335.                     foundSomething = true
  336.                 }
  337.             }
  338.         }
  339.  
  340.         knownScannerToAdd?.also { scanner ->
  341.             knownScanners.add(scanner)
  342.  
  343.             val deltas = scanner.deltas()
  344.             scannerDeltas[scanner] = deltas
  345.             deltasToPoints[scanner] = deltas.points()
  346.  
  347.             println("Known scanners: ${knownScanners.size}")
  348.         }
  349.  
  350.         unknownScannerToRemove?.also {
  351.             unknownScanners.removeIf { scanner ->
  352.                 it.name == scanner.name
  353.             }
  354.         }
  355.     }
  356.  
  357.     val unique = knownScanners.flatMap {
  358.         it.beacons
  359.     }.toSet()
  360.     println("Drones: ${unique.size}")
  361.  
  362.     val maxDistance = scannerOrigins.flatMap { pointA ->
  363.         scannerOrigins.mapNotNull { pointB ->
  364.             if (pointA == pointB) {
  365.                 return@mapNotNull null
  366.             }
  367.  
  368.             val (dx, dy, dz) = pointA - pointB
  369.  
  370.             abs(dx) + abs(dy) + abs(dz)
  371.         }
  372.     }.maxOrNull()
  373.  
  374.     println(maxDistance)
  375. }
  376.  
  377. private const val REQUIRED_INTERSECTIONS = 132
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement