/Cuboid.kt Secret
Last active
December 22, 2021 23:01
Advent of Code Day 22
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.example.e3fxgaming.adventOfCode2021.day22 | |
/** | |
* Data class for a cuboid record | |
*/ | |
data class Cuboid(val onOff: Boolean, val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) { | |
override fun toString(): String { | |
return "${xRange.count()}x${yRange.count()}x${zRange.count()}: ${if(onOff) "on" else "off"} x=$xRange,y=$yRange,z=$zRange" | |
} | |
/** | |
* Calculates the cubes contained in this cuboid | |
*/ | |
fun calculateElements(): Long { | |
return xRange.count().toLong() * yRange.count().toLong() * zRange.count().toLong() | |
} | |
/** | |
* Returns a shrunken version of this cuboid if it exists in the -50..50 range. | |
* Returns null if this cuboid doesn't have any elements in the -50..50 range. | |
*/ | |
fun shrinkCube(): Cuboid? { | |
val newXRange = xRange.part1ShrinkRange() | |
if(newXRange.isEmpty()) return null | |
val newYRange = yRange.part1ShrinkRange() | |
if(newYRange.isEmpty()) return null | |
val newZRange = zRange.part1ShrinkRange() | |
if(newZRange.isEmpty()) return null | |
return Cuboid(onOff, newXRange, newYRange, newZRange) | |
} | |
/** | |
* Helper method for shrinking an [IntRange] to the desired -50..50 range | |
*/ | |
private fun IntRange.part1ShrinkRange(): IntRange { | |
val newFrom = if(first < -50) -50 else first | |
val newTo = if(last > 50) 50 else last | |
return newFrom..newTo | |
} | |
/** | |
* Subtracts the [other] Cuboid from this cuboid. | |
* The result of that subtractions are 0 - 5 new Cuboids. | |
*/ | |
fun subtractCuboid(other: Cuboid): List<Cuboid> { | |
val xResults = xRange.subtractRange(other.xRange) | |
val yResults = yRange.subtractRange(other.yRange) | |
val zResults = zRange.subtractRange(other.zRange) | |
if(xResults.size == 1 && xResults.first() == xRange) return listOf(this) | |
if(yResults.size == 1 && yResults.first() == yRange) return listOf(this) | |
if(zResults.size == 1 && zResults.first() == zRange) return listOf(this) | |
val zCuboids = zResults.map { | |
Cuboid(true, xRange, yRange, it) | |
} | |
var negativeZResults = zRange | |
for(zResult in zResults) { | |
negativeZResults = negativeZResults.subtractRange(zResult).first() | |
} | |
val xCuboids = xResults.map { | |
Cuboid(true, it, yRange, negativeZResults) | |
} | |
var negativeXResults = xRange | |
for(xResult in xResults) { | |
negativeXResults = negativeXResults.subtractRange(xResult).first() | |
} | |
val yCuboids = yResults.map { | |
Cuboid(true, negativeXResults, it, negativeZResults) | |
} | |
return listOf(zCuboids, xCuboids, yCuboids).flatten() | |
} | |
/** | |
* Helper method for subtracting an [IntRange] from the [other] IntRange. Returns 0 - 2 new IntRages. | |
*/ | |
private fun IntRange.subtractRange(other: IntRange): List<IntRange> { | |
return if(last < other.first || first > other.last) { | |
return listOf(this) | |
} else if(other.first <= first && last <= other.last) { | |
listOf() | |
} else if(first < other.first && last <= other.last) { | |
listOf(first until other.first) | |
} else if(other.first <= first) { | |
listOf((other.last + 1)..last) | |
} else { | |
listOf(first until other.first, (other.last + 1)..last) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.example.e3fxgaming.adventOfCode2021.day22 | |
import org.example.e3fxgaming.adventOfCode2021.common.CommonHelper | |
fun main() { | |
val day22Solver = Day22Solver() | |
val part1Result = day22Solver.solve(true) | |
println("Part 1: turned on cubes count $part1Result") | |
val part2Result = day22Solver.solve(false) | |
println("Part 2: turned on cubes count $part2Result") | |
} | |
/** | |
* Solver for Advent of Code Day 22 | |
*/ | |
class Day22Solver { | |
private val inputCuboids = inputParsing() | |
/** | |
* Parses the input into a List of [Cuboid]. | |
*/ | |
private fun inputParsing(): List<Cuboid> { | |
val fileName = "/day22Input.txt" | |
val inputLines: List<String> = CommonHelper.getResourceContent(fileName) | |
val parsingRegex = Regex("(on|off) x=(-*\\d+)..(-*\\d+),y=(-*\\d+)..(-*\\d+),z=(-*\\d+)..(-*\\d+)") | |
return inputLines.map { inputLine -> | |
val searchResult = parsingRegex.find(inputLine) | |
if(searchResult != null) { | |
val captureGroups = searchResult.groups.mapNotNull { it?.value } | |
val onOff = captureGroups[1] == "on" | |
val numbers = captureGroups.subList(2, 8).map { it.toInt() } | |
val (xRange, yRange, zRange) = numbers.windowed(2, 2).map { it.component1()..it.component2() } | |
Cuboid(onOff, xRange, yRange, zRange) | |
} else { | |
throw IllegalStateException("Line parse unsuccessful for line \"$inputLine\"") | |
} | |
} | |
} | |
/** | |
* Solves the cuboid task by ignoring parts of cuboids that are handled later. | |
*/ | |
fun solve(shrinkCuboids: Boolean): Long { | |
var result: Long = 0 | |
val workingCuboids = if(shrinkCuboids) inputCuboids.mapNotNull { it.shrinkCuboid() } else inputCuboids | |
workingCuboids.forEachIndexed { index, cube -> | |
if(cube.onOff) { //only ON cuboids can add to the result | |
val remainingCubes = if (index + 1 <= workingCuboids.lastIndex) { | |
workingCuboids.subList(index + 1, workingCuboids.size) | |
} else { | |
listOf() | |
} | |
result += onCuboid(cube, remainingCubes) | |
} | |
} | |
return result | |
} | |
/** | |
* Shatters a cuboid into its non-ignorable parts that aren't handled later. | |
*/ | |
private fun onCuboid(newCuboid: Cuboid, remainingCuboids: List<Cuboid>): Long { | |
if(!newCuboid.onOff) throw IllegalStateException("Treating cube \"$newCuboid\" as ON cuboid") | |
var newCuboids: List<Cuboid> = listOf(newCuboid) | |
for(remainingCube in remainingCuboids) { | |
newCuboids = newCuboids.map { it.subtractCuboid(remainingCube) }.flatten() | |
} | |
return newCuboids.sumOf { it.calculateElements() } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment