Prepare for other years
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(1)
|
||||
class Day01(@Lines input: Input<IntArray>) {
|
||||
private val items = input.value
|
||||
|
||||
fun part1(): Int? {
|
||||
items.forEach { a ->
|
||||
items.forEach { b ->
|
||||
if (a + b == 2020) return a * b
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun part2(): Int? {
|
||||
items.forEach { a ->
|
||||
items.forEach { b ->
|
||||
items.forEach { c ->
|
||||
if (a + b + c == 2020) return a * b * c
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(2)
|
||||
class Day02(@Lines input: Input<List<String>>) {
|
||||
private data class PasswordEntry(val range: IntRange, val letter: Char, val password: String)
|
||||
|
||||
private val regex = "^(\\d+)-(\\d+) ([a-z]): (.*)$".toRegex()
|
||||
|
||||
private val passwords = input.value.map {
|
||||
val (_, min, max, letter, password) = regex.find(it)!!.groupValues
|
||||
PasswordEntry(min.toInt()..max.toInt(), letter[0], password)
|
||||
}
|
||||
|
||||
fun part1() = passwords.count { it.password.count { char -> char == it.letter } in it.range }
|
||||
|
||||
fun part2() = passwords.count { (range, letter, pwd) ->
|
||||
(pwd[range.first - 1] == letter) xor (pwd[range.last - 1] == letter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(3)
|
||||
class Day03(@Lines val input: Input<List<String>>) {
|
||||
private data class Slope(val x: Int, val y: Int)
|
||||
|
||||
private fun findSlope(slope: Slope): Int {
|
||||
val grid = input.value
|
||||
var trees = 0
|
||||
var x = 0
|
||||
var y = 0
|
||||
|
||||
val width = grid.first().length
|
||||
|
||||
while (y < grid.size - 1) {
|
||||
x += slope.x
|
||||
y += slope.y
|
||||
|
||||
if (grid[y][x % width] == '#') trees++
|
||||
}
|
||||
|
||||
return trees
|
||||
}
|
||||
|
||||
fun part1() = findSlope(Slope(x = 3, y = 1))
|
||||
|
||||
fun part2(): Long = listOf(
|
||||
Slope(x = 1, y = 1),
|
||||
Slope(x = 3, y = 1),
|
||||
Slope(x = 5, y = 1),
|
||||
Slope(x = 7, y = 1),
|
||||
Slope(x = 1, y = 2),
|
||||
)
|
||||
.map { findSlope(it).toLong() }
|
||||
.reduce { acc, trees -> acc * trees }
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Text
|
||||
|
||||
private typealias Entry = Pair<String, String>
|
||||
private typealias Entries = List<Entry>
|
||||
|
||||
@Day(4)
|
||||
class Day04(@Text val input: Input<String>) {
|
||||
|
||||
val entries = input.value.split("\n\n").map {
|
||||
it.split(" ", "\n").map { it.split(":").let { (k, v) -> k to v } }
|
||||
}
|
||||
|
||||
private fun Entries.hasRequiredKeys() = map { it.first }
|
||||
.containsAll(listOf("byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"))
|
||||
|
||||
fun part1() = entries.count { it.hasRequiredKeys() }
|
||||
|
||||
private val hclRegex = Regex("#[0-9a-f]{6}")
|
||||
private val pidRegex = Regex("[0-9]{9}")
|
||||
|
||||
private fun Entry.isValid() = let { (k, v) ->
|
||||
when (k) {
|
||||
"byr" -> v.toInt() in 1920..2002
|
||||
"iyr" -> v.toInt() in 2010..2020
|
||||
"eyr" -> v.toInt() in 2020..2030
|
||||
"hgt" -> when {
|
||||
v.endsWith("cm") -> v.removeSuffix("cm").toInt() in 150..193
|
||||
v.endsWith("in") -> v.removeSuffix("in").toInt() in 59..76
|
||||
else -> false
|
||||
}
|
||||
"hcl" -> v.matches(hclRegex)
|
||||
"ecl" -> v in arrayOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth")
|
||||
"pid" -> v.matches(pidRegex)
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
fun part2() = entries.count { it.hasRequiredKeys() && it.all { it.isValid() } }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(5)
|
||||
class Day05(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private val ids = input.value.map {
|
||||
it.replace("F", "0")
|
||||
.replace("B", "1")
|
||||
.replace("L", "0")
|
||||
.replace("R", "1")
|
||||
.toInt(2)
|
||||
}.sorted()
|
||||
|
||||
fun part1() = ids.last()
|
||||
|
||||
fun part2() = ids.windowed(size = 2, step = 1)
|
||||
.find { (a, b) -> b - a > 1 }!!
|
||||
.first() + 1
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Text
|
||||
import org.eclipse.collections.impl.factory.primitive.CharBags
|
||||
|
||||
@Day(6)
|
||||
class Day06(@Text val input: Input<String>) {
|
||||
|
||||
private val groups = input.value.split("\n\n")
|
||||
|
||||
fun part1() = groups.sumBy { it.replace("\n", "").toCharArray().toSet().size }
|
||||
|
||||
fun part2() = groups.sumBy {
|
||||
val group = it.split("\n")
|
||||
val bag = CharBags.mutable.empty()
|
||||
|
||||
group.forEach { chars: String -> chars.forEach { bag.add(it) } }
|
||||
|
||||
bag.selectByOccurrences { group.size == it }.toSet().size()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import org.eclipse.collections.api.factory.Stacks
|
||||
import org.eclipse.collections.api.multimap.list.ImmutableListMultimap
|
||||
import org.eclipse.collections.api.stack.MutableStack
|
||||
import org.eclipse.collections.impl.factory.Multimaps
|
||||
|
||||
@Day(7)
|
||||
class Day07(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private data class Bag(val count: Int, val color: String)
|
||||
|
||||
private val map: ImmutableListMultimap<String, Bag>
|
||||
|
||||
init {
|
||||
val mutableMap = Multimaps.mutable.list.empty<String, Bag>()
|
||||
|
||||
val colorRegex = "^(\\w+ \\w+)".toRegex()
|
||||
val requirementRegex = "(\\d+) (\\w+ \\w+) bag".toRegex()
|
||||
|
||||
for (line in input.value) {
|
||||
val outerColor = colorRegex.find(line)!!.groupValues[1]
|
||||
for (match in requirementRegex.findAll(line)) {
|
||||
val (_, count, color) = match.groupValues
|
||||
mutableMap.put(outerColor, Bag(count.toInt(), color))
|
||||
}
|
||||
}
|
||||
|
||||
map = mutableMap.toImmutable()
|
||||
}
|
||||
|
||||
private fun bagSequence(rootColor: String): Sequence<Bag> = sequence {
|
||||
val stack: MutableStack<Bag> = Stacks.mutable.ofAll(map.get(rootColor))
|
||||
while (stack.notEmpty()) {
|
||||
val current = stack.pop().also { yield(it) }
|
||||
map[current.color]?.let {
|
||||
it.forEach { (count, color) ->
|
||||
stack.push(Bag(current.count * count, color))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun part1() = map.keySet().count { bagSequence(it).any { it.color == "shiny gold" } }
|
||||
|
||||
fun part2() = bagSequence("shiny gold").sumBy { it.count }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import org.eclipse.collections.impl.factory.primitive.IntLists
|
||||
import org.eclipse.collections.impl.factory.primitive.IntSets
|
||||
|
||||
@Day(8)
|
||||
class Day08(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private val instructions = input.value.map {
|
||||
val words = it.split(" ")
|
||||
Instruction(Operation.valueOf(words[0].capitalize()), words[1].toInt())
|
||||
}.toTypedArray()
|
||||
|
||||
fun part1() = run(instructions).let {
|
||||
when (it) {
|
||||
is VmResult.Looped -> it.acc
|
||||
is VmResult.Terminated -> it.acc
|
||||
}
|
||||
}
|
||||
|
||||
private fun run(instructions: Array<Instruction>): VmResult {
|
||||
var acc = 0
|
||||
var ptr = 0
|
||||
|
||||
val visited = IntSets.mutable.empty()
|
||||
|
||||
while (visited.add(ptr)) {
|
||||
val instruction = instructions[ptr]
|
||||
when (instruction.operation) {
|
||||
Operation.Acc -> {
|
||||
acc += instruction.argument
|
||||
ptr++
|
||||
}
|
||||
Operation.Jmp -> ptr += instruction.argument
|
||||
Operation.Nop -> ptr++
|
||||
}
|
||||
if (ptr !in instructions.indices) {
|
||||
return VmResult.Terminated(acc)
|
||||
}
|
||||
}
|
||||
|
||||
return VmResult.Looped(acc)
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
val possibleMutations = IntLists.mutable.empty()
|
||||
instructions.forEachIndexed { i, e ->
|
||||
if (e.operation == Operation.Jmp || e.operation == Operation.Nop) possibleMutations.add(i)
|
||||
}
|
||||
|
||||
for (index in possibleMutations.toArray()) {
|
||||
val copy = instructions.copyOf().also {
|
||||
val modifiedOperation = if (it[index].operation == Operation.Nop) Operation.Jmp else Operation.Nop
|
||||
it[index] = it[index].copy(operation = modifiedOperation)
|
||||
}
|
||||
|
||||
val res = run(copy)
|
||||
if (res is VmResult.Terminated) return res.acc
|
||||
}
|
||||
|
||||
error("No result found")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum class Operation { Acc, Jmp, Nop }
|
||||
|
||||
private data class Instruction(val operation: Operation, val argument: Int)
|
||||
|
||||
private sealed class VmResult {
|
||||
data class Looped(val acc: Int) : VmResult()
|
||||
data class Terminated(val acc: Int) : VmResult()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(9)
|
||||
class Day09(@Lines val input: Input<LongArray>) {
|
||||
|
||||
private var part1Result = 0L
|
||||
|
||||
fun part1(): Long? {
|
||||
val longs = input.value
|
||||
|
||||
for (windowStart in 0 until longs.size - 26) {
|
||||
val last = longs[windowStart + 25]
|
||||
if (!isValid(windowStart, last)) {
|
||||
part1Result = last
|
||||
return last
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isValid(windowStart: Int, last: Long): Boolean {
|
||||
for (i in windowStart until windowStart + 25) {
|
||||
for (j in windowStart + 1 until windowStart + 25) {
|
||||
val f = input.value[i]
|
||||
val s = input.value[j]
|
||||
if (f + s == last && f != s) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun part2(): Long {
|
||||
var size = 2
|
||||
while (true) {
|
||||
for (startIndex in input.value.indices) {
|
||||
val lastIndex = input.value.size - 1 - size
|
||||
if (startIndex + size > lastIndex) break
|
||||
val slice = input.value.sliceArray(startIndex..startIndex + size)
|
||||
if (slice.sum() == part1Result) return slice.minOrNull()!! + slice.maxOrNull()!!
|
||||
}
|
||||
size++
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import org.eclipse.collections.api.list.primitive.MutableIntList
|
||||
import org.eclipse.collections.impl.factory.primitive.IntLists
|
||||
import org.eclipse.collections.impl.factory.primitive.IntLongMaps
|
||||
|
||||
@Day(10)
|
||||
class Day10(@Lines val input: Input<IntArray>) {
|
||||
|
||||
fun part1(): Int {
|
||||
val sorted = IntLists.mutable.of(0, *input.value).apply {
|
||||
sortThis()
|
||||
add(last + 3)
|
||||
}.toArray().toList()
|
||||
|
||||
var ones = 0
|
||||
var threes = 0
|
||||
|
||||
sorted.zipWithNext().forEach { (a, b) ->
|
||||
if (a + 1 == b) ones++
|
||||
else if (a + 3 == b) threes++
|
||||
}
|
||||
|
||||
return ones * threes
|
||||
}
|
||||
|
||||
fun part2(): Long {
|
||||
val sorted: MutableIntList = IntLists.mutable.of(*input.value).apply { sortThis() }
|
||||
|
||||
val map = IntLongMaps.mutable.empty().apply {
|
||||
put(0, 1L)
|
||||
}
|
||||
|
||||
sorted.forEach { i ->
|
||||
map.put(i, map.get(i - 1) + map.get(i - 2) + map.get(i - 3))
|
||||
}
|
||||
|
||||
return map.get(sorted.last)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
private typealias Seats = Array<CharArray>
|
||||
|
||||
private fun Seats.deepClone(): Seats = map { it.clone() }.toTypedArray()
|
||||
|
||||
private operator fun Seats.get(x: Int, y: Int): Char = this[y][x]
|
||||
private operator fun Seats.set(x: Int, y: Int, value: Char) {
|
||||
this[y][x] = value
|
||||
}
|
||||
|
||||
private operator fun Seats.contains(xy: Pair<Int, Int>): Boolean {
|
||||
val (x, y) = xy
|
||||
return x in 0 until width && y in 0 until height
|
||||
}
|
||||
|
||||
private val Seats.width get() = first().size
|
||||
private val Seats.height get() = size
|
||||
|
||||
private fun Seats.asGridString() = joinToString("\n") { it.joinToString("") }
|
||||
private fun Seats.countOccupied() = sumBy { it.count { it == '#' } }
|
||||
|
||||
@Day(11)
|
||||
class Day11(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private val seats: Seats = input.value.map { it.toCharArray() }.toTypedArray()
|
||||
|
||||
private val directions = listOf(
|
||||
-1 to -1,
|
||||
+0 to -1,
|
||||
+1 to -1,
|
||||
+1 to +0,
|
||||
+1 to +1,
|
||||
+0 to +1,
|
||||
-1 to +1,
|
||||
-1 to +0,
|
||||
)
|
||||
|
||||
private fun countAdjacentOccupiedSeats(x: Int, y: Int, seats: Array<CharArray>): Int =
|
||||
directions.mapNotNull { (dx, dy) ->
|
||||
val xx = x + dx
|
||||
val yy = y + dy
|
||||
|
||||
if (xx to yy in seats) seats[xx, yy]
|
||||
else null
|
||||
}.count { it == '#' }
|
||||
|
||||
private fun countVisibleOccupiedSeats(x: Int, y: Int, seats: Seats): Int {
|
||||
var occupied = 0
|
||||
|
||||
for ((dx, dy) in directions) {
|
||||
var xx = x
|
||||
var yy = y
|
||||
while (true) {
|
||||
xx += dx
|
||||
yy += dy
|
||||
if (xx to yy !in seats) break
|
||||
if (seats[xx, yy] == 'L') break
|
||||
if (seats[xx, yy] == '#') {
|
||||
occupied++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return occupied
|
||||
}
|
||||
|
||||
private fun findLastRepeating(seats: Seats, next: (Seats) -> Seats): Seats {
|
||||
var previous = seats
|
||||
val set = HashSet<String>() // use strings because Arrays don't implement equals ??
|
||||
|
||||
while (true) {
|
||||
val res = next(previous)
|
||||
if (!set.add(res.asGridString())) return res
|
||||
previous = res
|
||||
}
|
||||
}
|
||||
|
||||
private fun progress1(previous: Array<CharArray>): Array<CharArray> {
|
||||
val next = previous.deepClone()
|
||||
|
||||
for (x in 0 until seats.width) {
|
||||
for (y in 0 until seats.height) {
|
||||
when (previous[x, y]) {
|
||||
'L' -> if (countAdjacentOccupiedSeats(x, y, previous) == 0) next[x, y] = '#'
|
||||
'#' -> if (countAdjacentOccupiedSeats(x, y, previous) >= 4) next[x, y] = 'L'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
fun part1() = findLastRepeating(seats, ::progress1).countOccupied()
|
||||
|
||||
private fun progress2(previous: Seats): Array<CharArray> {
|
||||
val next = previous.deepClone()
|
||||
|
||||
for (x in 0 until previous.width) {
|
||||
for (y in 0 until previous.height) {
|
||||
when (previous[x, y]) {
|
||||
'L' -> if (countVisibleOccupiedSeats(x, y, previous) == 0) next[x, y] = '#'
|
||||
'#' -> if (countVisibleOccupiedSeats(x, y, previous) >= 5) next[x, y] = 'L'
|
||||
}
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
fun part2() = findLastRepeating(seats, ::progress2).countOccupied()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import kotlin.math.abs
|
||||
|
||||
@Day(12)
|
||||
class Day12(@Lines val input: Input<List<String>>) {
|
||||
|
||||
fun part1(): Int {
|
||||
var x = 0
|
||||
var y = 0
|
||||
var facing = "E"
|
||||
|
||||
val dirs = listOf("N", "E", "S", "W")
|
||||
|
||||
input.value.forEach {
|
||||
val dir = it.take(1)
|
||||
val steps = it.drop(1).toInt()
|
||||
|
||||
when (dir) {
|
||||
"L" -> repeat(steps / 90) {
|
||||
val i = (dirs.indexOf(facing) + 4 - 1) % 4
|
||||
facing = dirs[i]
|
||||
}
|
||||
"R" -> repeat(steps / 90) {
|
||||
val i = (dirs.indexOf(facing) + 1) % 4
|
||||
facing = dirs[i]
|
||||
}
|
||||
}
|
||||
|
||||
when (if (dir == "F") facing else dir) {
|
||||
"N" -> y -= steps
|
||||
"S" -> y += steps
|
||||
"W" -> x -= steps
|
||||
"E" -> x += steps
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return abs(x) + abs(y)
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
var x = 0
|
||||
var y = 0
|
||||
|
||||
var waypointX = 10
|
||||
var waypointY = -1
|
||||
|
||||
input.value.forEach {
|
||||
val dir = it.take(1)
|
||||
val steps = it.drop(1).toInt()
|
||||
|
||||
when (dir) {
|
||||
"L" -> repeat(steps / 90) {
|
||||
val oldY = waypointY
|
||||
waypointY = -waypointX
|
||||
waypointX = oldY
|
||||
}
|
||||
"R" -> repeat(steps / 90) {
|
||||
val oldY = waypointY
|
||||
waypointY = waypointX
|
||||
waypointX = -oldY
|
||||
}
|
||||
"F" -> {
|
||||
x += waypointX * steps
|
||||
y += waypointY * steps
|
||||
}
|
||||
"N" -> waypointY -= steps
|
||||
"S" -> waypointY += steps
|
||||
"W" -> waypointX -= steps
|
||||
"E" -> waypointX += steps
|
||||
}
|
||||
}
|
||||
|
||||
return abs(x) + abs(y)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import kotlin.math.abs
|
||||
|
||||
private data class Bus(val index: Int, val id: Long)
|
||||
|
||||
@Day(13)
|
||||
class Day13(@Lines val input: Input<List<String>>) {
|
||||
|
||||
fun part1(): Int {
|
||||
val id = input.value[0].toInt()
|
||||
|
||||
val (busId, min) = input.value[1]
|
||||
.splitToSequence(",")
|
||||
.filterNot { it == "x" }
|
||||
.map { it.toInt() }
|
||||
.map { bus -> bus to id / bus }
|
||||
.map { (bus, div) -> bus to bus * (div + 1) }
|
||||
.minByOrNull { it.second }!!
|
||||
|
||||
return busId * abs(min - id)
|
||||
}
|
||||
|
||||
private tailrec fun gcd(a: Long, b: Long): Long = if (b == 0L) a else gcd(b, a % b)
|
||||
private fun lcm(a: Long, b: Long): Long = a / gcd(a, b) * b
|
||||
|
||||
fun part2(): Long {
|
||||
val buses = input.value[1]
|
||||
.splitToSequence(",")
|
||||
.mapIndexedNotNull { index, bus ->
|
||||
if (bus == "x") null
|
||||
else Bus(index, bus.toLong())
|
||||
}
|
||||
.toList()
|
||||
|
||||
var step = 1L
|
||||
var t = 0L
|
||||
|
||||
for ((i, id) in buses) {
|
||||
while ((t + i) % id != 0L) t += step
|
||||
step = lcm(step, id)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import kotlin.math.pow
|
||||
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps
|
||||
import org.eclipse.collections.impl.factory.primitive.LongIntMaps
|
||||
|
||||
@Day(14)
|
||||
class Day14(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private val memRe = "mem\\[(\\d+)] = (.*)$".toRegex()
|
||||
|
||||
private fun Long.toBin36() = toString(2).padStart(length = 36, padChar = '0')
|
||||
|
||||
fun part1(): Long {
|
||||
val mem = IntObjectMaps.mutable.empty<String>()
|
||||
|
||||
var currentMask: String = ""
|
||||
|
||||
for (line in input.value) {
|
||||
if (line.startsWith("mask")) {
|
||||
currentMask = line.removePrefix("mask = ")
|
||||
} else {
|
||||
val (address, value) = memRe.find(line)!!.destructured
|
||||
val bin = value.toLong().toBin36()
|
||||
|
||||
val result = bin.zip(currentMask)
|
||||
.map { (bin, mask) -> if (mask != 'X') mask else bin }
|
||||
.joinToString("")
|
||||
|
||||
mem.put(address.toInt(), result)
|
||||
}
|
||||
}
|
||||
|
||||
return mem.values()
|
||||
.map { it.dropWhile { it == '0' } }
|
||||
.map { it.toLong(2) }
|
||||
.sum()
|
||||
}
|
||||
|
||||
fun part2(): Long {
|
||||
val mem = LongIntMaps.mutable.empty()
|
||||
|
||||
var currentMask = ""
|
||||
|
||||
for (line in input.value) {
|
||||
if (line[1] == 'a') {
|
||||
currentMask = line.substring(7)
|
||||
} else {
|
||||
|
||||
val (address, value) = memRe.find(line)!!.destructured.let { (add, value) ->
|
||||
add.toLong().toBin36() to value.toInt()
|
||||
}
|
||||
|
||||
val mutations = generateMutations(currentMask, address)
|
||||
|
||||
for (mutation in mutations) {
|
||||
mem.put(String(mutation).toLong(2), value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mem.values().sum()
|
||||
}
|
||||
|
||||
private fun generateMutations(mask: String, address: String): Array<CharArray> {
|
||||
val mutationCount = mask.count { it == 'X' }.let { 2.0.pow(it.toDouble()).toInt() }
|
||||
val mutations = Array(mutationCount) { CharArray(36) }
|
||||
|
||||
var groups = 1
|
||||
|
||||
for (i in mask.indices) {
|
||||
when (mask[i]) {
|
||||
'X' -> {
|
||||
groups *= 2
|
||||
val groupSize = mutationCount / groups
|
||||
var currentChar = '1'
|
||||
for (b in mutations.indices) {
|
||||
val builder = mutations[b]
|
||||
val flip = b % groupSize == 0
|
||||
if (flip) currentChar = if (currentChar == '0') '1' else '0'
|
||||
builder[i] = currentChar
|
||||
}
|
||||
}
|
||||
'1' -> mutations.forEach { it[i] = '1' }
|
||||
else -> mutations.forEach { it[i] = address[i] }
|
||||
}
|
||||
}
|
||||
return mutations
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Csv
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import kotlin.math.abs
|
||||
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps
|
||||
|
||||
@Day(15)
|
||||
class Day15(@Csv val input: Input<IntArray>) {
|
||||
|
||||
private fun run(until: Int): Int {
|
||||
val start = input.value
|
||||
|
||||
val map = IntObjectMaps.mutable.empty<IntArray>()
|
||||
|
||||
var last = -1
|
||||
|
||||
fun say(round: Int, number: Int) {
|
||||
val lastValues = map.get(number)
|
||||
val values = lastValues
|
||||
?.let { intArrayOf(round, it.maxOrNull()!!) }
|
||||
?: intArrayOf(round)
|
||||
|
||||
map.put(number, values)
|
||||
last = number
|
||||
}
|
||||
|
||||
for (i in 1..until) {
|
||||
if (i <= start.size) {
|
||||
say(i, start[i - 1])
|
||||
} else {
|
||||
val lastValues = map.get(last)
|
||||
if (lastValues?.size ?: 0 < 2) say(i, 0)
|
||||
else say(i, abs(lastValues[0] - lastValues[1]))
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
fun part1() = run(until = 2020)
|
||||
|
||||
fun part2() = run(until = 30000000)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Groups
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import org.eclipse.collections.api.multimap.list.ListMultimap
|
||||
import org.eclipse.collections.api.multimap.list.MutableListMultimap
|
||||
import org.eclipse.collections.api.multimap.set.MutableSetMultimap
|
||||
import org.eclipse.collections.impl.factory.Multimaps
|
||||
import org.eclipse.collections.impl.multimap.list.FastListMultimap
|
||||
|
||||
@Day(16)
|
||||
class Day16(@Groups val input: Input<List<List<String>>>) {
|
||||
|
||||
private val rangesGroup = input.value[0]
|
||||
private val myTicket = input.value[1][1]
|
||||
private val nearbyTicketsGroup = input.value[2].drop(1)
|
||||
|
||||
private val rangeRe = "(\\d+)-(\\d+)".toRegex()
|
||||
|
||||
private fun extractRanges(line: String): Sequence<IntRange> = rangeRe.findAll(line)
|
||||
.map {
|
||||
val (min, max) = it.destructured
|
||||
min.toInt()..max.toInt()
|
||||
}
|
||||
|
||||
fun part1(): Int {
|
||||
val minMax = mutableListOf<IntRange>()
|
||||
|
||||
for (line in rangesGroup) {
|
||||
minMax.addAll(extractRanges(line))
|
||||
}
|
||||
|
||||
val nearbyTickets = mutableListOf<Int>()
|
||||
for (line in nearbyTicketsGroup) {
|
||||
line.splitToSequence(",").forEach { nearbyTickets.add(it.toInt()) }
|
||||
}
|
||||
|
||||
val invalid = mutableListOf<Int>()
|
||||
nearbyTickets.forEach { number ->
|
||||
if (minMax.none { number in it }) invalid.add(number)
|
||||
}
|
||||
|
||||
return invalid.sum()
|
||||
}
|
||||
|
||||
private fun isTicketValid(ticket: List<Int>, ranges: Iterable<IntRange>): Boolean {
|
||||
for (number in ticket) {
|
||||
if (!ranges.any { number in it }) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun inRanges(value: Int, ranges: List<IntRange>) = ranges.any { value in it }
|
||||
|
||||
fun part2(): Long {
|
||||
val rangesByName = namedRanges()
|
||||
val validTickets = validTickets(rangesByName)
|
||||
val indexesByCategory = indexesByCategory(rangesByName, validTickets)
|
||||
|
||||
removeDuplicates(indexesByCategory)
|
||||
|
||||
val myTicketValues = myTicket.split(",").map { it.toInt() }
|
||||
|
||||
var mult = 1L
|
||||
indexesByCategory.forEachKeyValue { category, index ->
|
||||
if (category.startsWith("departure")) {
|
||||
mult *= myTicketValues[index]
|
||||
}
|
||||
}
|
||||
return mult
|
||||
}
|
||||
|
||||
private fun removeDuplicates(indexesByCategory: MutableSetMultimap<String, Int>) {
|
||||
val toBeRemoved = HashSet<Int>()
|
||||
val queue = ArrayDeque<Int>()
|
||||
|
||||
indexesByCategory.multiValuesView()
|
||||
.toSortedListBy { it.size() }
|
||||
.forEach { it.forEach { if (toBeRemoved.add(it)) queue.add(it) } }
|
||||
|
||||
queue.removeLast()
|
||||
|
||||
val categoriesToRemove = mutableListOf<String>()
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
val duplicate = queue.removeFirst()
|
||||
|
||||
categoriesToRemove.clear()
|
||||
|
||||
for (entry in indexesByCategory.keyMultiValuePairsView()) {
|
||||
if (entry.two.size() < 2) continue
|
||||
categoriesToRemove.add(entry.one)
|
||||
}
|
||||
|
||||
for (category in categoriesToRemove) {
|
||||
indexesByCategory.remove(category, duplicate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun indexesByCategory(
|
||||
rangesByName: ListMultimap<String, IntRange>,
|
||||
validTickets: List<List<Int>>,
|
||||
): MutableSetMultimap<String, Int> {
|
||||
val indexesByCategory = Multimaps.mutable.set.empty<String, Int>()
|
||||
|
||||
for (entry in rangesByName.keyMultiValuePairsView()) {
|
||||
val category = entry.one
|
||||
val ranges = entry.two.toList()
|
||||
|
||||
for (i in validTickets.first().indices) {
|
||||
var allInRange = true
|
||||
for(ticket in validTickets){
|
||||
val current = ticket[i]
|
||||
if(!inRanges(current, ranges)){
|
||||
allInRange = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allInRange) indexesByCategory.put(category, i)
|
||||
}
|
||||
}
|
||||
return indexesByCategory
|
||||
}
|
||||
|
||||
private fun validTickets(rangesByName: MutableListMultimap<String, IntRange>): List<List<Int>> {
|
||||
val validTickets = mutableListOf<List<Int>>()
|
||||
val ranges = rangesByName.valuesView().toList()
|
||||
for (line in nearbyTicketsGroup) {
|
||||
val ticket = line.split(",").map { it.toInt() }
|
||||
if (isTicketValid(ticket, ranges)) {
|
||||
validTickets.add(ticket)
|
||||
}
|
||||
}
|
||||
return validTickets
|
||||
}
|
||||
|
||||
private fun namedRanges(): MutableListMultimap<String, IntRange> {
|
||||
val rangesByName = FastListMultimap<String, IntRange>()
|
||||
for (line in rangesGroup) {
|
||||
val name = line.substringBefore(":")
|
||||
extractRanges(line).forEach {
|
||||
rangesByName.put(name, it)
|
||||
}
|
||||
}
|
||||
return rangesByName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
private data class Point(val x: Int, val y: Int, val z: Int)
|
||||
private data class Point4(val x: Int, val y: Int, val z: Int, val blah: Int)
|
||||
|
||||
private enum class State { Active, Inactive }
|
||||
|
||||
@Day(17)
|
||||
class Day17(@Lines val input: Input<List<String>>) {
|
||||
|
||||
fun part1(): Int {
|
||||
val grid = parseGrid { x, y -> Point(x, y, 0) }
|
||||
repeat(6) { step(grid, ::neighbours3) }
|
||||
return grid.values.count { it == State.Active }
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
val grid = parseGrid { x, y -> Point4(x, y, 0, 0) }
|
||||
repeat(6) { step(grid, ::neighbours4) }
|
||||
return grid.values.count { it == State.Active }
|
||||
}
|
||||
|
||||
private fun <T> parseGrid(pointFactory: (x: Int, y: Int) -> T): MutableMap<T, State> {
|
||||
val grid = mutableMapOf<T, State>()
|
||||
input.value.forEachIndexed { index, row ->
|
||||
row.forEachIndexed { col, char ->
|
||||
val state = if (char == '#') State.Active else State.Inactive
|
||||
grid[pointFactory(index, col)] = state
|
||||
}
|
||||
}
|
||||
return grid
|
||||
}
|
||||
|
||||
private fun <T> step(grid: MutableMap<T, State>, neighbours: (T) -> List<T>) {
|
||||
val modifications = mutableMapOf<T, State>()
|
||||
val pointsToConsider = grid.keys.flatMap { neighbours(it) }.toSet()
|
||||
for (point in pointsToConsider) {
|
||||
val neighbours = neighbours(point)
|
||||
val activeNeighboursCount = neighbours.count { grid[it] ?: State.Inactive == State.Active }
|
||||
val state = grid[point] ?: State.Inactive
|
||||
if (state == State.Active && activeNeighboursCount !in 2..3) {
|
||||
modifications[point] = State.Inactive
|
||||
} else if (activeNeighboursCount == 3) {
|
||||
modifications[point] = State.Active
|
||||
}
|
||||
}
|
||||
for ((point, state) in modifications) {
|
||||
grid[point] = state
|
||||
}
|
||||
}
|
||||
|
||||
private fun neighbours3(point: Point): List<Point> {
|
||||
val points = mutableListOf<Point>()
|
||||
for (x in point.x - 1..point.x + 1) {
|
||||
for (y in point.y - 1..point.y + 1) {
|
||||
for (z in point.z - 1..point.z + 1) {
|
||||
val generatedPoint = Point(x, y, z)
|
||||
if (generatedPoint != point)
|
||||
points.add(generatedPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
check(points.size == 26) { "Points size was ${points.size}" }
|
||||
return points
|
||||
}
|
||||
|
||||
private fun neighbours4(point: Point4): List<Point4> {
|
||||
val points = mutableListOf<Point4>()
|
||||
for (x in point.x - 1..point.x + 1) {
|
||||
for (y in point.y - 1..point.y + 1) {
|
||||
for (z in point.z - 1..point.z + 1) {
|
||||
for (blah in point.blah - 1..point.blah + 1) {
|
||||
val generatedPoint = Point4(x, y, z, blah)
|
||||
if (generatedPoint != point)
|
||||
points.add(generatedPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
check(points.size == 80) { "Points size was ${points.size}" }
|
||||
return points
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import java.util.*
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
private enum class Operator { Add, Multiply }
|
||||
|
||||
private operator fun Operator.invoke(a: Long, b: Long) = when (this) {
|
||||
Operator.Add -> a + b
|
||||
Operator.Multiply -> a * b
|
||||
}
|
||||
|
||||
private inline fun Logger.debug(msg: () -> Any) {
|
||||
if (isDebugEnabled) debug(msg().toString())
|
||||
}
|
||||
|
||||
@Day(18)
|
||||
class Day18(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger("Day18")
|
||||
private val lines = input.value.map { it.replace(" ", "") }
|
||||
|
||||
private fun parseGroups(line: String): Map<Int, List<IntRange>> {
|
||||
var depth = 0
|
||||
val groups = mutableMapOf<Int, MutableList<IntRange>>()
|
||||
val openingPars = Stack<Int>()
|
||||
|
||||
for ((index, char) in line.withIndex()) {
|
||||
when (char) {
|
||||
'(' -> {
|
||||
openingPars.push(index)
|
||||
depth++
|
||||
}
|
||||
')' -> {
|
||||
depth--
|
||||
val group = groups.getOrPut(depth) { mutableListOf() }
|
||||
group.add(openingPars.pop()..index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups.also { logger.debug { it } }
|
||||
}
|
||||
|
||||
private fun solveGroup(group: String, precedence: Boolean) = solveGroup(parseGroup(group), precedence)
|
||||
|
||||
private fun solveGroup(group: Pair<LinkedList<Long>, LinkedList<Operator>>, precedence: Boolean): Long {
|
||||
val (operands, operators) = group
|
||||
logger.debug { operands }
|
||||
logger.debug { operators }
|
||||
|
||||
if (!precedence) {
|
||||
for (i in operators.indices) {
|
||||
val a = operands[i]
|
||||
val b = operands[i + 1]
|
||||
|
||||
val result = operators[i](a, b)
|
||||
|
||||
operands[i + 1] = result
|
||||
}
|
||||
|
||||
return operands.peekLast()
|
||||
} else {
|
||||
|
||||
var i = 0
|
||||
while (operators.any { it == Operator.Add }) {
|
||||
|
||||
val operator = operators[i]
|
||||
|
||||
if (operator == Operator.Add) {
|
||||
val a = operands[i]
|
||||
val b = operands[i + 1]
|
||||
val res = operator(a, b)
|
||||
operands.removeAt(i)
|
||||
operands[i] = res
|
||||
operators.removeAt(i)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return operands.reduce { a, b -> Operator.Multiply(a, b) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseGroup(group: String): Pair<LinkedList<Long>, LinkedList<Operator>> {
|
||||
val operands = LinkedList<Long>()
|
||||
val operators = LinkedList<Operator>()
|
||||
val operand = StringBuilder()
|
||||
|
||||
for (char in group) {
|
||||
if (char.isDigit()) {
|
||||
operand.append(char)
|
||||
} else {
|
||||
val operator = when (char) {
|
||||
'+' -> Operator.Add
|
||||
'*' -> Operator.Multiply
|
||||
else -> error("Unknown operator $char")
|
||||
}
|
||||
operators.add(operator)
|
||||
operands.add(operand.toString().toLong())
|
||||
operand.clear()
|
||||
}
|
||||
}
|
||||
|
||||
operands.add(operand.toString().toLong())
|
||||
|
||||
return Pair(operands, operators)
|
||||
}
|
||||
|
||||
private fun solveLine(line: String, precedence: Boolean = false): Long {
|
||||
var l = line
|
||||
|
||||
while (true) {
|
||||
val groups = parseGroups(l)
|
||||
if (groups.isEmpty()) break
|
||||
|
||||
val highestDepth = groups.keys.maxOrNull()!!
|
||||
val solvableRanges = groups[highestDepth]!!
|
||||
|
||||
val solved = ArrayDeque<Pair<IntRange, Long>>()
|
||||
|
||||
for (range in solvableRanges) {
|
||||
val rangeWithoutPars = range.first + 1 until range.last
|
||||
val answer = solveGroup(l.substring(rangeWithoutPars), precedence)
|
||||
solved.addLast(range to answer)
|
||||
}
|
||||
|
||||
var solution = solved.removeFirst()
|
||||
var inside = false
|
||||
|
||||
val builder = StringBuilder()
|
||||
|
||||
for (i in l.indices) {
|
||||
if (!inside && i in solution.first) {
|
||||
inside = true
|
||||
builder.append(solution.second)
|
||||
} else if (inside && i !in solution.first) {
|
||||
inside = false
|
||||
|
||||
solution = if (solved.isEmpty()) -1..-1 to -1 // avoid null checks..
|
||||
else solved.removeFirst()
|
||||
|
||||
builder.append(l[i])
|
||||
} else if (i !in solution.first) {
|
||||
builder.append(l[i])
|
||||
}
|
||||
}
|
||||
|
||||
l = builder.toString()
|
||||
}
|
||||
|
||||
return solveGroup(l, precedence)
|
||||
}
|
||||
|
||||
fun part1() = lines
|
||||
.parallelStream()
|
||||
.map { solveLine(it) }
|
||||
.reduce { t, u -> t + u }
|
||||
.get()
|
||||
|
||||
fun part2() = lines
|
||||
.parallelStream()
|
||||
.map { solveLine(it, precedence = true) }
|
||||
.reduce { t, u -> t + u }
|
||||
.get()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Groups
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@Day(19)
|
||||
class Day19(@Groups val input: Input<List<List<String>>>) {
|
||||
private val rules = input.value[0]
|
||||
private val messages = input.value[1]
|
||||
|
||||
sealed class Rule {
|
||||
data class CharRule(val value: Char) : Rule()
|
||||
data class OrRule(val a: List<Int>, val b: List<Int>?) : Rule() {
|
||||
val all get() = listOfNotNull(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseRules(): Map<Int, Rule> {
|
||||
val parsedRules = mutableMapOf<Int, Rule>()
|
||||
rules.forEach {
|
||||
val parts = it.split(":", limit = 2)
|
||||
val index = parts[0].toInt()
|
||||
val rule = if ("\"" !in parts[1]) {
|
||||
val p = parts[1].split('|', limit = 2).map { it.trim().split(" ").mapTo(ArrayList(2)) { it.toInt() } }
|
||||
Rule.OrRule(p[0], if (p.size == 2) p[1] else null)
|
||||
} else {
|
||||
Rule.CharRule(parts[1].trim().replace("\"", "")[0])
|
||||
}
|
||||
parsedRules[index] = rule
|
||||
}
|
||||
return parsedRules
|
||||
}
|
||||
|
||||
private fun matches(input: String, queue: ArrayDeque<Int>, rules: Map<Int, Rule>): Boolean =
|
||||
if (queue.isEmpty()) input.isEmpty()
|
||||
else if (input.isEmpty()) queue.isEmpty()
|
||||
else when (val rule = rules[queue.pop()]!!) {
|
||||
is Rule.CharRule -> input[0] == rule.value && matches(input.drop(1), queue, rules)
|
||||
is Rule.OrRule -> rule.all.any { matches(input, queue.clone().apply { it.asReversed().forEach { addFirst(it) } }, rules) }
|
||||
}
|
||||
|
||||
fun part1(): Int {
|
||||
val rules = parseRules()
|
||||
return messages.count { matches(it, ArrayDeque<Int>().apply { add(0) }, rules) }
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
val rules = parseRules().toMutableMap()
|
||||
rules[8] = Rule.OrRule(listOf(42), listOf(42, 8))
|
||||
rules[11] = Rule.OrRule(listOf(42, 31), listOf(42, 11, 31))
|
||||
return messages.count { matches(it, ArrayDeque<Int>().apply { add(0) }, rules) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.days.geometry.Grid
|
||||
import be.vandewalleh.aoc.days.geometry.gridOf
|
||||
import be.vandewalleh.aoc.days.geometry.transformations
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Groups
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.sqrt
|
||||
|
||||
private typealias Tile = Grid<Char>
|
||||
|
||||
@Day(20)
|
||||
class Day20(@Groups val input: Input<List<List<String>>>) {
|
||||
private val tiles: Map<Int, Tile> = input.value
|
||||
.map { it[0].let { it.substring(5 until it.indexOf(':')).toInt() } to it.drop(1) }
|
||||
.associate { (id, tile) -> id to gridOf(tile) }
|
||||
|
||||
private fun Tile.allEdges() = listOf(edges(), edges().map { it.reversed() }).flatten()
|
||||
|
||||
private fun edgesMatch(a: Tile, b: Tile): Boolean {
|
||||
val edges = b.allEdges()
|
||||
return a.allEdges().any { a -> edges.any { a == it } }
|
||||
}
|
||||
|
||||
private fun combine(tiles: List<Tile>) = sequence {
|
||||
for (i in 0 until tiles.lastIndex) {
|
||||
val a = tiles[i]
|
||||
for (j in i + 1 until tiles.size) {
|
||||
val b = tiles[j]
|
||||
yield(a to b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tilesByNeighbourCount(): MutableMap<Int, MutableList<Tile>> {
|
||||
val neighbours = combine(tiles.values.toList())
|
||||
.filter { (a, b) -> edgesMatch(a, b) }
|
||||
.map { it.toList() }
|
||||
.flatten()
|
||||
.groupBy { it }
|
||||
.values
|
||||
|
||||
val map = mutableMapOf<Int, MutableList<Tile>>()
|
||||
for (neighbour in neighbours) {
|
||||
map.computeIfAbsent(neighbour.size) { mutableListOf() }.add(neighbour.first())
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
fun part1() = tilesByNeighbourCount()[2]!!
|
||||
.map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
|
||||
.reduce { acc, id -> acc * id }
|
||||
|
||||
private fun neighboursCount(grid: Grid<*>, x: Int, y: Int): Int {
|
||||
var neighboursCount = 2
|
||||
if (x != 0 && x != grid.lastColumnIndex) neighboursCount++
|
||||
if (y != 0 && y != grid.lastRowIndex) neighboursCount++
|
||||
return neighboursCount
|
||||
}
|
||||
|
||||
private fun isLeftOk(grid: Grid<Tile?>, x: Int, y: Int, candidate: Tile) = grid[x - 1, y]
|
||||
?.let { left -> candidate.firstColumn() == left.lastColumn() }
|
||||
?: true
|
||||
|
||||
private fun isTopOk(grid: Grid<Tile?>, x: Int, y: Int, candidate: Tile) = grid[x, y - 1]
|
||||
?.let { top -> candidate.firstRow() == top.lastRow() }
|
||||
?: true
|
||||
|
||||
private fun isBottomOk(grid: Grid<*>, x: Int, y: Int, candidate: Tile, neighbours: Map<Int, List<Tile>>) =
|
||||
if (y == grid.lastRowIndex) true
|
||||
else neighbours[neighboursCount(grid, x, y + 1)]!!
|
||||
.filterNot { it == candidate }
|
||||
.count { it.transformations().any { candidate.lastColumn() == it.firstColumn() } } == 1
|
||||
|
||||
private fun isRightOk(grid: Grid<*>, x: Int, y: Int, candidate: Tile, neighbours: Map<Int, List<Tile>>) =
|
||||
if (x == grid.lastColumnIndex) true
|
||||
else neighbours[neighboursCount(grid, x + 1, y)]!!
|
||||
.filterNot { it == candidate }
|
||||
.count { it.transformations().any { candidate.lastRow() == it.firstRow() } } == 1
|
||||
|
||||
private fun assembleGrid(): Grid<Tile?> {
|
||||
val size = sqrt(tiles.size.toDouble()).toInt()
|
||||
val grid: Grid<Tile?> = Grid(Array(size) { Array(size) { null } })
|
||||
|
||||
val neighbours: MutableMap<Int, MutableList<Tile>> = tilesByNeighbourCount()
|
||||
|
||||
for (y in 0 until grid.height) {
|
||||
for (x in 0 until grid.width) {
|
||||
val neighboursCount = neighboursCount(grid, x, y)
|
||||
val candidates = neighbours[neighboursCount]!!
|
||||
|
||||
val conditions = mutableListOf<(Tile) -> Boolean>()
|
||||
conditions += { isLeftOk(grid, x, y, it) }
|
||||
conditions += { isTopOk(grid, x, y, it) }
|
||||
|
||||
// why is this condition needed ?
|
||||
if (x == 0 && y == 0) {
|
||||
conditions += { isBottomOk(grid, x, y, it, neighbours) }
|
||||
conditions += { isRightOk(grid, x, y, it, neighbours) }
|
||||
}
|
||||
|
||||
var found: Tile? = null
|
||||
|
||||
outer@ for (candidate in candidates) {
|
||||
for (transform in candidate.transformations()) {
|
||||
if (conditions.all { it(transform) }) {
|
||||
found = transform
|
||||
candidates.remove(candidate)
|
||||
break@outer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check(found != null)
|
||||
grid[x, y] = found
|
||||
}
|
||||
}
|
||||
|
||||
return grid
|
||||
}
|
||||
|
||||
private fun removeGaps(grid: Grid<Tile?>) {
|
||||
for (y in 0 until grid.height) {
|
||||
for (x in 0 until grid.width) {
|
||||
grid[x, y] = removeGaps(grid[x, y]!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeGaps(tile: Tile): Tile {
|
||||
val oldData: ArrayList<ArrayList<Char>> = tile.data
|
||||
val newData = ArrayList<ArrayList<Char>>(oldData.size - 2)
|
||||
oldData.subList(1, oldData.size - 1).forEach { d ->
|
||||
val l = ArrayList<Char>().apply {
|
||||
addAll(d.subList(1, d.size - 1))
|
||||
}
|
||||
newData.add(l)
|
||||
}
|
||||
return Tile(newData)
|
||||
}
|
||||
|
||||
private fun gridToTile(grid: Grid<Tile?>): Tile {
|
||||
val newData = ArrayList<ArrayList<Char>>()
|
||||
for (y in 0 until grid.height) {
|
||||
val row = grid.row(y)
|
||||
for (yy in 0 until row[0]!!.height) {
|
||||
val combinedRow = ArrayList<Char>().also { newData.add(it) }
|
||||
row.forEach { combinedRow.addAll(it!!.row(yy)) }
|
||||
}
|
||||
}
|
||||
return Tile(newData)
|
||||
}
|
||||
|
||||
private fun Tile.subGridData(startX: Int, startY: Int, width: Int, height: Int): List<List<Char>> {
|
||||
val newData = ArrayList<ArrayList<Char>>()
|
||||
for (y in startY until startY + height) {
|
||||
val row = ArrayList<Char>().also { newData.add(it) }
|
||||
for (x in startX until startX + width) {
|
||||
row.add(this[x, y]!!)
|
||||
}
|
||||
}
|
||||
return newData
|
||||
}
|
||||
|
||||
private fun List<List<Char>>.isMonster(monster: List<List<Char>>): Boolean {
|
||||
val monsterRe = monster.joinToString("") { it.joinToString("") }.replace(" ", ".").toRegex()
|
||||
val str = this.joinToString("") { it.joinToString("") }
|
||||
return monsterRe.matches(str)
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
val grid = assembleGrid()
|
||||
removeGaps(grid)
|
||||
val megaTile = gridToTile(grid)
|
||||
|
||||
val monster: List<List<Char>> = """
|
||||
| # |
|
||||
|# ## ## ###|
|
||||
| # # # # # # |
|
||||
""".trimMargin("|").lines().map { it.toCharArray().dropLast(1) }
|
||||
|
||||
val monsterWidth = monster[0].size
|
||||
val monsterHeight = 3
|
||||
val monsterSquares = monster.flatten().count { it == '#' }
|
||||
val squares = megaTile.data.flatten().count { it == '#' }
|
||||
|
||||
for (g in megaTile.transformations()) {
|
||||
var count = 0
|
||||
for (y in 0 until g.lastRowIndex - monsterHeight) {
|
||||
for (x in 0 until g.lastColumnIndex - monsterWidth) {
|
||||
val subgrid = g.subGridData(x, y, monsterWidth, monsterHeight)
|
||||
if (subgrid.isMonster(monster)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count != 0) return squares - (count * monsterSquares)
|
||||
}
|
||||
error("No monsters found")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import org.eclipse.collections.api.factory.Bags
|
||||
import org.eclipse.collections.api.multimap.set.MutableSetMultimap
|
||||
import org.eclipse.collections.impl.factory.Multimaps
|
||||
|
||||
@Day(21)
|
||||
class Day21(@Lines val input: Input<List<String>>) {
|
||||
private val foods = input.value.map { line ->
|
||||
val parOpen = line.indexOf('(')
|
||||
val ingredients = line.substring(0 until parOpen - 1).split(" ")
|
||||
val allergens = line.substring(parOpen + 1 until line.length - 1).removePrefix("contains ").split(", ")
|
||||
ingredients to allergens
|
||||
}
|
||||
|
||||
private val allIngredients = foods.flatMap { it.first }.toSet()
|
||||
private val allAllergens = foods.flatMap { it.second }.toSet()
|
||||
private val dangerousIngredients = dangerousIngredients()
|
||||
|
||||
fun part1(): Int {
|
||||
val occurrences = Bags.mutable.empty<String>()
|
||||
foods.forEach { (ingredients) -> occurrences.addAll(ingredients) }
|
||||
return allIngredients.filter { !dangerousIngredients.containsValue(it) }
|
||||
.map { ingredient -> occurrences.count { it == ingredient } }
|
||||
.sum()
|
||||
}
|
||||
|
||||
fun part2(): String {
|
||||
while (!dangerousIngredients.multiValuesView().all { it.size() == 1 }) {
|
||||
dangerousIngredients.multiValuesView()
|
||||
.filter { it.size() == 1 }
|
||||
.map { it.first() }
|
||||
.forEach { removeMe ->
|
||||
dangerousIngredients.keyMultiValuePairsView()
|
||||
.filter { it.two.size() != 1 && it.two.contains(removeMe) }
|
||||
.forEach { dangerousIngredients.remove(it.one, removeMe) }
|
||||
}
|
||||
}
|
||||
return dangerousIngredients.keySet().sorted().joinToString(",") { dangerousIngredients.get(it).first() }
|
||||
}
|
||||
|
||||
private fun dangerousIngredients(): MutableSetMultimap<String, String> {
|
||||
val map = Multimaps.mutable.set.empty<String, String>()
|
||||
allAllergens.forEach { map.putAll(it, allIngredients) }
|
||||
|
||||
foods.forEach { (ingredients, allergens) ->
|
||||
allergens.forEach { allergen ->
|
||||
allIngredients.forEach {
|
||||
if (!ingredients.contains(it)) map.remove(allergen, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Groups
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
|
||||
private data class PlayedGame(val a: List<Int>, val b: List<Int>)
|
||||
|
||||
@Day(22)
|
||||
class Day22(@Groups val input: Input<List<List<String>>>) {
|
||||
private val one = input.value[0].drop(1).map { it.toInt() }
|
||||
private val two = input.value[1].drop(1).map { it.toInt() }
|
||||
|
||||
fun part1(): Long {
|
||||
val oneDeque = ArrayDeque(one)
|
||||
val twoDeque = ArrayDeque(two)
|
||||
|
||||
while (oneDeque.isNotEmpty() && twoDeque.isNotEmpty()) {
|
||||
val a = oneDeque.removeFirst()
|
||||
val b = twoDeque.removeFirst()
|
||||
|
||||
if (a > b) {
|
||||
oneDeque.addLast(a)
|
||||
oneDeque.addLast(b)
|
||||
} else {
|
||||
twoDeque.addLast(b)
|
||||
twoDeque.addLast(a)
|
||||
}
|
||||
}
|
||||
|
||||
val deque = if (oneDeque.isEmpty()) twoDeque else oneDeque
|
||||
return deque.score()
|
||||
}
|
||||
|
||||
private fun ArrayDeque<Int>.score() =
|
||||
asReversed().mapIndexed { index, value -> (index + 1).toLong() * value.toLong() }.sum()
|
||||
|
||||
fun part2(): Long {
|
||||
val oneDeque = ArrayDeque(one)
|
||||
val twoDeque = ArrayDeque(two)
|
||||
|
||||
val winner = playGame(oneDeque, twoDeque)
|
||||
val deque = if (winner == 1) oneDeque else twoDeque
|
||||
return deque.score()
|
||||
}
|
||||
|
||||
private fun playGame(one: ArrayDeque<Int>, two: ArrayDeque<Int>): Int {
|
||||
val playedGames = mutableSetOf<PlayedGame>()
|
||||
|
||||
while (one.isNotEmpty() && two.isNotEmpty()) {
|
||||
if (!playedGames.add(PlayedGame(one.toList(), two.toList()))) return 1
|
||||
|
||||
val a = one.removeFirst()
|
||||
val b = two.removeFirst()
|
||||
|
||||
val winner = when {
|
||||
one.size >= a && two.size >= b -> playGame(
|
||||
ArrayDeque(one.take(a)),
|
||||
ArrayDeque(two.take(b))
|
||||
)
|
||||
a > b -> 1
|
||||
else -> 2
|
||||
}
|
||||
|
||||
if (winner == 1) {
|
||||
one.addLast(a)
|
||||
one.addLast(b)
|
||||
} else {
|
||||
two.addLast(b)
|
||||
two.addLast(a)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return if (one.isEmpty()) 2 else 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Text
|
||||
|
||||
@Day(23)
|
||||
class Day23(@Text val input: Input<String>) {
|
||||
private val cups = input.value.toCharArray().map { it.toString().toInt() }
|
||||
|
||||
private fun <T> ringSequence(head: Ring.Node<T>) = generateSequence(head) { it.next }
|
||||
|
||||
class Ring<T>(items: Iterable<T>) {
|
||||
|
||||
class Node<T>(var value: T) {
|
||||
lateinit var next: Node<T>
|
||||
override fun toString() = "Node($value -> ${next.value})"
|
||||
}
|
||||
|
||||
private var last: Node<T>? = null
|
||||
private var first: Node<T>? = null
|
||||
|
||||
init {
|
||||
items.forEach { item ->
|
||||
val node = Node(item)
|
||||
if (first == null) first = node
|
||||
last?.next = node
|
||||
last = node
|
||||
}
|
||||
check(first != null && last != null)
|
||||
last?.next = first!!
|
||||
}
|
||||
|
||||
fun first() = first!!
|
||||
}
|
||||
|
||||
private fun Ring.Node<Int>.cached(size: Int): Array<Ring.Node<Int>> =
|
||||
arrayOfNulls<Ring.Node<Int>>(size + 1).also { array ->
|
||||
array[0] = Ring.Node(-1)
|
||||
ringSequence(this).take(size).forEach { array[it.value] = it }
|
||||
} as Array<Ring.Node<Int>>
|
||||
|
||||
fun part1(): String {
|
||||
var currentNode = Ring(cups).first()
|
||||
val cache = currentNode.cached(cups.size)
|
||||
val max = cups.maxOrNull()!!
|
||||
|
||||
repeat(100) { currentNode = move(max, cache, currentNode) }
|
||||
|
||||
return ringSequence(currentNode)
|
||||
.dropWhile { it.value != 1 }
|
||||
.drop(1)
|
||||
.take(cups.size - 1)
|
||||
.map { it.value }
|
||||
.joinToString("")
|
||||
}
|
||||
|
||||
fun part2(): Long {
|
||||
fun fillCups(): List<Int> {
|
||||
val cups = ArrayList<Int>(1_000_000)
|
||||
cups.addAll(this.cups)
|
||||
val highest = this.cups.maxOrNull()!!
|
||||
|
||||
for (i in 1..1_000_000 - cups.size) {
|
||||
cups.add(highest + i)
|
||||
}
|
||||
|
||||
return cups
|
||||
}
|
||||
|
||||
val size = 1_000_000
|
||||
var currentNode = Ring(fillCups()).first()
|
||||
val cache = currentNode.cached(size)
|
||||
|
||||
repeat(10_000_000) { currentNode = move(max = size, cache, currentNode) }
|
||||
|
||||
val one = cache[1]
|
||||
val a = one.next
|
||||
val b = a.next
|
||||
return a.value.toLong() * b.value.toLong()
|
||||
}
|
||||
|
||||
private fun move(max: Int, cache: Array<Ring.Node<Int>>, current: Ring.Node<Int>): Ring.Node<Int> {
|
||||
val a = current.next
|
||||
val b = a.next
|
||||
val c = b.next
|
||||
|
||||
current.next = c.next
|
||||
|
||||
val aa = a.value
|
||||
val bb = b.value
|
||||
val cc = c.value
|
||||
|
||||
val destinationNode: Ring.Node<Int>
|
||||
var i = current.value - 1
|
||||
while (true) {
|
||||
if (i < 1) i = max
|
||||
val value = cache[i].value
|
||||
if (value == aa || value == bb || value == cc) {
|
||||
i--
|
||||
} else {
|
||||
destinationNode = cache[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
val destinationNextNode = destinationNode.next
|
||||
destinationNode.next = a
|
||||
c.next = destinationNextNode
|
||||
return current.next
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import org.eclipse.collections.api.bag.Bag
|
||||
import org.eclipse.collections.api.bag.MutableBag
|
||||
import org.eclipse.collections.api.factory.Bags
|
||||
|
||||
@Day(24)
|
||||
class Day24(@Lines val input: Input<List<String>>) {
|
||||
|
||||
private data class HexPoint(val x: Int, val y: Int, val z: Int) {
|
||||
fun translate(other: HexPoint) = HexPoint(this.x + other.x, this.y + other.y, this.z + other.z)
|
||||
}
|
||||
|
||||
private enum class Direction(vararg coordinates: Int) {
|
||||
E(1, -1, 0),
|
||||
SE(0, -1, 1),
|
||||
SW(-1, 0, 1),
|
||||
W(-1, 1, 0),
|
||||
NW(0, 1, -1),
|
||||
NE(1, 0, -1);
|
||||
|
||||
val coordinates = HexPoint(coordinates[0], coordinates[1], coordinates[2])
|
||||
}
|
||||
|
||||
private fun parseTile(line: String) = "e|se|sw|w|nw|ne".toRegex()
|
||||
.findAll(line)
|
||||
.flatMap { it.groupValues }
|
||||
.map { Direction.valueOf(it.toUpperCase()) }
|
||||
.toList()
|
||||
|
||||
private val tiles = input.value.map { parseTile(it) }.map {
|
||||
it.map { it.coordinates }.reduce { acc, ints -> acc.translate(ints) }
|
||||
}
|
||||
|
||||
fun part1() = Bags.immutable.ofAll(tiles).selectBlacks().size()
|
||||
|
||||
private fun Bag<HexPoint>.selectBlacks() = selectByOccurrences { it % 2 == 1 }
|
||||
|
||||
private fun HexPoint.adjacents() = Direction.values().map { this.translate(it.coordinates) }
|
||||
|
||||
// black -> odd
|
||||
// white -> even || not in bag -> !in black
|
||||
private fun day(bag: MutableBag<HexPoint>) {
|
||||
val blacks = bag.selectBlacks().toSet()
|
||||
|
||||
val all = (bag + bag.flatMap { it.adjacents() }).toSet()
|
||||
|
||||
for (tile in all) {
|
||||
val adjacents = tile.adjacents()
|
||||
val adjacentBlacks = adjacents.count { it in blacks }
|
||||
val isBlack = tile in blacks
|
||||
|
||||
if (isBlack && (adjacentBlacks == 0 || adjacentBlacks > 2)) bag.setOccurrences(tile, 2)
|
||||
else if (!isBlack && adjacentBlacks == 2) bag.setOccurrences(tile, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fun part2(): Int {
|
||||
val bag = Bags.mutable.ofAll(tiles)
|
||||
repeat(100) { day(bag) }
|
||||
return bag.selectBlacks().size()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
|
||||
@Day(25)
|
||||
class Day25(@Lines val input: Input<IntArray>) {
|
||||
private val doorPublicKey = input.value[0]
|
||||
private val cardPublicKey = input.value[1]
|
||||
|
||||
private fun encryptionKey(loopSize: Int, publicKey: Int) = transformSubjectNumber(loopSize, publicKey)
|
||||
|
||||
private fun transformSubjectNumber(loopSize: Int, subjectNumber: Int): Int {
|
||||
var number = 1L
|
||||
repeat(loopSize) {
|
||||
number *= subjectNumber
|
||||
number %= 20201227
|
||||
}
|
||||
return number.toInt()
|
||||
}
|
||||
|
||||
private fun transformSubjectNumberStartingWith(currentNumber: Long, subjectNumber: Int): Long {
|
||||
var number = currentNumber
|
||||
number *= subjectNumber
|
||||
number %= 20201227
|
||||
return number
|
||||
}
|
||||
|
||||
private fun findLoopSize(expectedPublicKey: Long): Int {
|
||||
var computedPublicKey = 1L
|
||||
var loopSize = 0
|
||||
do {
|
||||
computedPublicKey = transformSubjectNumberStartingWith(computedPublicKey, 7)
|
||||
loopSize++
|
||||
} while (computedPublicKey != expectedPublicKey)
|
||||
return loopSize
|
||||
}
|
||||
|
||||
fun part1(): Int {
|
||||
val cardLoopSize = findLoopSize(cardPublicKey.toLong())
|
||||
return encryptionKey(cardLoopSize, doorPublicKey)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.factory.createDay
|
||||
|
||||
fun main() = with(createDay<Day25>()) {
|
||||
println(part1())
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package be.vandewalleh.aoc.days.geometry
|
||||
|
||||
class Grid<T> {
|
||||
val data: ArrayList<ArrayList<T>> = ArrayList()
|
||||
|
||||
constructor(data: Iterable<Iterable<T>>) {
|
||||
data.forEach {
|
||||
this.data.add(ArrayList(it.toList()))
|
||||
}
|
||||
}
|
||||
|
||||
constructor(data: Array<Array<T>>) {
|
||||
data.forEach {
|
||||
this.data.add(ArrayList(it.toList()))
|
||||
}
|
||||
}
|
||||
|
||||
val width get() = data[0].size
|
||||
val lastColumnIndex get() = data[0].size - 1
|
||||
|
||||
val height get() = data.size
|
||||
val lastRowIndex get() = data.size - 1
|
||||
|
||||
operator fun get(x: Int, y: Int): T? {
|
||||
if (y !in 0..lastRowIndex) return null
|
||||
val row = data[y]
|
||||
return if (x !in 0..lastColumnIndex) null
|
||||
else row[x]
|
||||
}
|
||||
|
||||
operator fun set(x: Int, y: Int, value: T) {
|
||||
data[y][x] = value
|
||||
}
|
||||
|
||||
fun row(y: Int): List<T> = data[y]
|
||||
fun firstRow() = row(0)
|
||||
fun lastRow() = row(height - 1)
|
||||
|
||||
fun column(x: Int): List<T> = data.map { it[x] }
|
||||
fun firstColumn() = column(0)
|
||||
fun lastColumn() = column(width - 1)
|
||||
|
||||
fun edges() = listOf(row(0), column(0), row(lastRowIndex), column(lastColumnIndex))
|
||||
|
||||
override fun toString() = buildString {
|
||||
data.forEach { line ->
|
||||
append(line.joinToString(""))
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as Grid<*>
|
||||
if (data != other.data) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = data.hashCode()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package be.vandewalleh.aoc.days.geometry
|
||||
|
||||
fun gridOf(lines: List<String>): Grid<Char> =
|
||||
Grid(lines.map { it.toCharArray().toList() })
|
||||
|
||||
fun gridOf(lines: String): Grid<Char> =
|
||||
gridOf(lines.lines())
|
||||
@@ -0,0 +1,53 @@
|
||||
package be.vandewalleh.aoc.days.geometry
|
||||
|
||||
import java.util.*
|
||||
|
||||
private fun <T> ArrayList<T>.reversed(): ArrayList<T> {
|
||||
val out = ArrayList<T>(this.size)
|
||||
asReversed().forEach { out.add(it) }
|
||||
return out
|
||||
}
|
||||
|
||||
fun <T> Grid<T>.flipVertically(): Grid<T> = Grid(data.reversed())
|
||||
|
||||
fun <T> Grid<T>.flipHorizontally(): Grid<T> {
|
||||
val out = ArrayList<ArrayList<T>>(height)
|
||||
for (y in 0 until height) {
|
||||
out.add(data[y].reversed())
|
||||
}
|
||||
return Grid(out)
|
||||
}
|
||||
|
||||
fun <T> Grid<T>.rotateRight(): Grid<T> {
|
||||
val out = ArrayList<ArrayList<T>>(width)
|
||||
for (x in 0 until width) {
|
||||
out.add(ArrayList<T>(height))
|
||||
for (y in 0 until height) {
|
||||
out[x].add(data[y][x])
|
||||
}
|
||||
}
|
||||
return Grid(out).flipHorizontally()
|
||||
}
|
||||
|
||||
fun <T> Grid<T>.rotateLeft(): Grid<T> {
|
||||
val data = flipHorizontally().data
|
||||
val out = ArrayList<ArrayList<T>>(width)
|
||||
for (x in 0 until width) {
|
||||
out.add(ArrayList<T>(height))
|
||||
for (y in 0 until height) {
|
||||
out[x].add(data[y][x])
|
||||
}
|
||||
}
|
||||
return Grid(out).flipHorizontally()
|
||||
}
|
||||
|
||||
fun <T> Grid<T>.transformations(): Sequence<Grid<T>> = sequence {
|
||||
yield(this@transformations)
|
||||
yield(flipHorizontally())
|
||||
yield(flipVertically())
|
||||
yield(rotateLeft())
|
||||
yield(rotateRight())
|
||||
yield(flipHorizontally().flipVertically())
|
||||
yield(flipVertically().rotateRight())
|
||||
yield(flipVertically().rotateLeft())
|
||||
}
|
||||
Reference in New Issue
Block a user