Skip to content

Commit 0c38527

Browse files
committed
Solution 2017-10 (Knot Hash)
1 parent 9c40ecc commit 0c38527

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package de.ronny_h.aoc.year2017.day10
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
5+
fun main() = KnotHash().run("3770", "a9d0e68649d0174c8756a59ba21d4dc6")
6+
7+
class KnotHash : AdventOfCode<String>(2017, 10) {
8+
override fun part1(input: List<String>): String {
9+
val lengths = input.single().split(",").map(String::toInt)
10+
val size = if (lengths.size < 5) 5 else 256 // just for testing
11+
return knotHash(size, lengths).toString()
12+
}
13+
14+
override fun part2(input: List<String>): String {
15+
val lengths = input.single().toASCII() + listOf(17, 31, 73, 47, 23)
16+
val denseHash = sparseHash(256, lengths, 64).reduceToDenseHash()
17+
check(denseHash.size == 16)
18+
return denseHash.joinToString("") { it.toString(16).padStart(2, '0') }
19+
}
20+
}
21+
22+
fun knotHash(size: Int, lengths: List<Int>): Int {
23+
val circularList = sparseHash(size, lengths)
24+
return circularList[0] * circularList[1]
25+
}
26+
27+
private fun sparseHash(
28+
size: Int,
29+
lengths: List<Int>,
30+
rounds: Int = 1,
31+
): List<Int> {
32+
val circularList = MutableList(size) { it }
33+
var position = 0
34+
var skipSize = 0
35+
36+
fun reverseSubList(start: Int, length: Int) {
37+
if (start + length < size) {
38+
circularList.subList(start, start + length).toList()
39+
} else {
40+
circularList.subList(start, size) + circularList.subList(0, length - size + start)
41+
}
42+
.reversed()
43+
.forEachIndexed { i, item ->
44+
circularList[(start + i) % size] = item
45+
}
46+
}
47+
48+
repeat(rounds) {
49+
lengths.forEach { length ->
50+
reverseSubList(position, length)
51+
position = (position + length + skipSize) % size
52+
skipSize++
53+
}
54+
}
55+
return circularList
56+
}
57+
58+
fun List<Int>.reduceToDenseHash() = chunked(16) {
59+
it.reduce { a, b -> a xor b }
60+
}
61+
62+
fun String.toASCII() = map(Char::code)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package de.ronny_h.aoc.year2017.day10
2+
3+
import io.kotest.core.spec.style.StringSpec
4+
import io.kotest.data.forAll
5+
import io.kotest.data.row
6+
import io.kotest.matchers.shouldBe
7+
8+
class KnotHashTest : StringSpec({
9+
10+
"the knot hash of a small example" {
11+
knotHash(5, listOf(3, 4, 1, 5)) shouldBe 12
12+
}
13+
14+
"part 1: the knot hash of a small example" {
15+
val input = listOf("3,4,1,5")
16+
KnotHash().part1(input) shouldBe "12"
17+
}
18+
19+
"ASCII Codes of the input" {
20+
"1,2,3".toASCII() shouldBe listOf(49, 44, 50, 44, 51)
21+
}
22+
23+
"reduce to dense hash" {
24+
listOf(65, 27, 9, 1, 4, 3, 40, 50, 91, 7, 6, 0, 2, 5, 68, 22).reduceToDenseHash() shouldBe listOf(64)
25+
}
26+
27+
"part 2: the real knot hash" {
28+
forAll(
29+
row(listOf(""), "a2582a3a0e66e6e86e3812dcb672a272"),
30+
row(listOf("AoC 2017"), "33efeb34ea91902bb2f59c9920caa6cd"),
31+
row(listOf("1,2,3"), "3efbe78a8d82f29979031a4aa0b16a9d"),
32+
row(listOf("1,2,4"), "63960835bcdc130f0b66d7ff4f6a5a8e"),
33+
) { input, hash ->
34+
KnotHash().part2(input) shouldBe hash
35+
}
36+
}
37+
})

0 commit comments

Comments
 (0)