Skip to content

Instantly share code, notes, and snippets.

@E3FxGaming
Last active December 22, 2021 23:01
Advent of Code Day 22
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)
}
}
}
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