Skip to content

Commit 4029763

Browse files
committed
Solution Day 25, part one (Code Chronicle)
1 parent ff06462 commit 4029763

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package de.ronny_h.aoc.year24.day25
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.extensions.combinationsOf
5+
import de.ronny_h.aoc.extensions.split
6+
7+
fun main() = CodeChronicle().run(2993, 0)
8+
9+
class CodeChronicle : AdventOfCode<Int>(2024, 25) {
10+
companion object {
11+
private val filled = '#'
12+
private val empty = '.'
13+
14+
fun List<String>.convertToLocksAndKeys() = split()
15+
.map { block ->
16+
if (block.first().all { it == filled } && block.last().all { it == empty }) {
17+
Lock(block.countHeights())
18+
} else if (block.first().all { it == empty } && block.last().all { it == filled }) {
19+
Key(block.countHeights())
20+
} else {
21+
error("The current block is neither a lock nor a key: '$block'")
22+
}
23+
}
24+
25+
fun Key.fitsIntoLockWithoutOverlapping(lock: Lock) = (0..4).all { column ->
26+
this.heights[column] + lock.pins[column] <= 5
27+
}
28+
29+
private fun List<String>.countHeights(): List<Int> = (0..4).map { column ->
30+
count { it[column] == filled } - 1
31+
}
32+
}
33+
34+
@Suppress("UNCHECKED_CAST")
35+
override fun part1(input: List<String>): Int {
36+
val (keys, locks) = input
37+
.convertToLocksAndKeys()
38+
.partition { it is Key } as Pair<List<Key>, List<Lock>>
39+
return combinationsOf(keys, locks).count { (key, lock) ->
40+
key.fitsIntoLockWithoutOverlapping(lock)
41+
}
42+
}
43+
44+
override fun part2(input: List<String>): Int {
45+
TODO("Not yet implemented")
46+
}
47+
}
48+
49+
sealed interface VirtualFivePinTumbler
50+
51+
data class Lock(val pins: List<Int>) : VirtualFivePinTumbler {
52+
init {
53+
require(pins.size == 5) { "Number of pins is not 5: $pins" }
54+
}
55+
}
56+
57+
data class Key(val heights: List<Int>) : VirtualFivePinTumbler {
58+
init {
59+
require(heights.size == 5) { "Number of heights is not 5: $heights" }
60+
}
61+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package de.ronny_h.aoc.year24.day25
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import de.ronny_h.aoc.year24.day25.CodeChronicle.Companion.convertToLocksAndKeys
5+
import de.ronny_h.aoc.year24.day25.CodeChronicle.Companion.fitsIntoLockWithoutOverlapping
6+
import io.kotest.core.spec.style.StringSpec
7+
import io.kotest.matchers.shouldBe
8+
9+
class CodeChronicleTest : StringSpec({
10+
11+
val smallInput = """
12+
#####
13+
.####
14+
.####
15+
.####
16+
.#.#.
17+
.#...
18+
.....
19+
20+
.....
21+
#....
22+
#....
23+
#...#
24+
#.#.#
25+
#.###
26+
#####
27+
""".asList()
28+
29+
val input = """
30+
#####
31+
.####
32+
.####
33+
.####
34+
.#.#.
35+
.#...
36+
.....
37+
38+
#####
39+
##.##
40+
.#.##
41+
...##
42+
...#.
43+
...#.
44+
.....
45+
46+
.....
47+
#....
48+
#....
49+
#...#
50+
#.#.#
51+
#.###
52+
#####
53+
54+
.....
55+
.....
56+
#.#..
57+
###..
58+
###.#
59+
###.#
60+
#####
61+
62+
.....
63+
.....
64+
.....
65+
#....
66+
#.#..
67+
#.#.#
68+
#####
69+
""".asList()
70+
71+
"convert smallInput to locks and keys" {
72+
smallInput.convertToLocksAndKeys() shouldBe listOf(Lock(listOf(0,5,3,4,3)), Key(listOf(5,0,2,1,3)))
73+
}
74+
75+
"a key fits into a lock without overlapping" {
76+
Key(listOf(3,3,3,3,3)).fitsIntoLockWithoutOverlapping(Lock(listOf(2,2,2,2,2))) shouldBe true
77+
}
78+
79+
"a key with a too high height does not fit into a lock without overlapping" {
80+
Key(listOf(3,3,4,3,3)).fitsIntoLockWithoutOverlapping(Lock(listOf(2,2,2,2,2))) shouldBe false
81+
}
82+
83+
"part1: In the input should be 3 unique lock/key pairs that fit together without overlapping in any column" {
84+
CodeChronicle().part1(input) shouldBe 3
85+
}
86+
})

0 commit comments

Comments
 (0)