Skip to content

Commit 1599386

Browse files
committed
Solution 2017-16 (Permutation Promenade)
1 parent b60d24d commit 1599386

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package de.ronny_h.aoc.year2017.day16
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.year2017.day16.DanceMove.*
5+
import de.ronny_h.aoc.year2017.day16.MutableRingList.Companion.mutableRingListOf
6+
7+
fun main() = PermutationPromenade().run("kgdchlfniambejop", "fjpmholcibdgeakn")
8+
9+
class PermutationPromenade : AdventOfCode<String>(2017, 16) {
10+
override fun part1(input: List<String>): String = doTheDance("abcdefghijklmnop", input)
11+
override fun part2(input: List<String>): String = doTheDance("abcdefghijklmnop", input, 1_000_000_000)
12+
13+
fun doTheDance(programsLine: String, input: List<String>, times: Int = 1): String {
14+
val programs = mutableRingListOf(programsLine)
15+
val danceMoves = input.parseDanceMoves()
16+
17+
var cycleTime = 0
18+
while (cycleTime < times) {
19+
danceMoves.forEach { it.doTheMove(programs) }
20+
cycleTime++
21+
if (programs.toJoinedString() == programsLine) {
22+
break
23+
}
24+
}
25+
26+
val remaining = times % cycleTime
27+
repeat(remaining) {
28+
danceMoves.forEach { it.doTheMove(programs) }
29+
}
30+
return programs.toJoinedString()
31+
}
32+
}
33+
34+
sealed interface DanceMove {
35+
fun doTheMove(programs: MutableRingList<Char>)
36+
37+
data class Spin(val x: Int) : DanceMove {
38+
override fun doTheMove(programs: MutableRingList<Char>) {
39+
programs.shiftRight(x)
40+
}
41+
}
42+
43+
data class Exchange(val posA: Int, val posB: Int) : DanceMove {
44+
override fun doTheMove(programs: MutableRingList<Char>) {
45+
programs.swap(posA, posB)
46+
}
47+
}
48+
49+
data class Partner(val programA: Char, val programB: Char) : DanceMove {
50+
override fun doTheMove(programs: MutableRingList<Char>) {
51+
programs.swap(programA, programB)
52+
}
53+
}
54+
}
55+
56+
fun List<String>.parseDanceMoves() = single().split(",").map {
57+
when (it.first()) {
58+
's' -> Spin(it.substringAfter("s").toInt())
59+
'x' -> {
60+
val (a, b) = it.substringAfter("x").split("/")
61+
Exchange(a.toInt(), b.toInt())
62+
}
63+
64+
'p' -> {
65+
val (a, b) = it.substringAfter("p").split("/")
66+
Partner(a.first(), b.first())
67+
}
68+
69+
else -> error("unknown move: $it")
70+
}
71+
}
72+
73+
class MutableRingList<T>(initialList: List<T>) {
74+
private val list: MutableList<T> = initialList.toMutableList()
75+
private var offset = 0
76+
77+
companion object {
78+
fun <T> mutableRingListOf(vararg elements: T): MutableRingList<T> = MutableRingList(elements.toList())
79+
fun mutableRingListOf(elements: String): MutableRingList<Char> = MutableRingList(elements.toList())
80+
}
81+
82+
operator fun get(index: Int) = list[(index + offset) % list.size]
83+
84+
operator fun set(index: Int, value: T) {
85+
list[(index + offset) % list.size] = value
86+
}
87+
88+
/**
89+
* Makes [n] elements move from the end to the front, but maintain their order otherwise.
90+
*/
91+
fun shiftRight(n: Int): MutableRingList<T> {
92+
check(n <= list.size) { "the number of items to spin must be smaller than or equal to the list's length" }
93+
offset = (offset + list.size - n) % list.size
94+
return this
95+
}
96+
97+
/**
98+
* Swaps the elements at [indexA] and [indexB].
99+
*/
100+
fun swap(indexA: Int, indexB: Int): MutableRingList<T> {
101+
val tmp = get(indexA)
102+
set(indexA, get(indexB))
103+
set(indexB, tmp)
104+
return this
105+
}
106+
107+
/**
108+
* Swaps the positions of the specified elements [elemA] and [elemB].
109+
* If not unique, only their first occurrences are swapped.
110+
*/
111+
fun swap(elemA: T, elemB: T): MutableRingList<T> {
112+
val indexA = list.indexOf(elemA)
113+
val indexB = list.indexOf(elemB)
114+
require(indexA >= 0) { "$elemA is not in the list" }
115+
require(indexB >= 0) { "$elemB is not in the list" }
116+
list[indexA] = elemB
117+
list[indexB] = elemA
118+
return this
119+
}
120+
121+
/**
122+
* @return A snapshot of the current state of this [MutableRingList]
123+
*/
124+
fun toList(): List<T> = list.subList(offset, list.size) + list.subList(0, offset)
125+
126+
override fun toString(): String = toList().toString()
127+
128+
fun toJoinedString(): String = toList().joinToString("")
129+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package de.ronny_h.aoc.year2017.day16
2+
3+
import de.ronny_h.aoc.year2017.day16.DanceMove.*
4+
import de.ronny_h.aoc.year2017.day16.MutableRingList.Companion.mutableRingListOf
5+
import io.kotest.core.spec.style.StringSpec
6+
import io.kotest.matchers.shouldBe
7+
8+
class PermutationPromenadeTest : StringSpec({
9+
10+
val input = listOf("s1,x3/4,pe/b")
11+
12+
"input can be parsed" {
13+
input.parseDanceMoves() shouldBe listOf(Spin(1), Exchange(3, 4), Partner('e', 'b'))
14+
}
15+
16+
"a MutableRingList<Char> can be created from a variable amounts of Chars and toString() generates a nice representation" {
17+
mutableRingListOf('a', 'b', 'c', 'd', 'e').toString() shouldBe "[a, b, c, d, e]"
18+
}
19+
20+
"a MutableRingList<Char> can be created from a String and toJoinedString() recreates that String" {
21+
mutableRingListOf("abcde").toJoinedString() shouldBe "abcde"
22+
}
23+
24+
"MutableRingList: get" {
25+
mutableRingListOf("abcde").get(2) shouldBe 'c'
26+
mutableRingListOf("abcde")[2] shouldBe 'c'
27+
}
28+
29+
"MutableRingList: set" {
30+
val list = mutableRingListOf("abcde")
31+
32+
list.set(2, 'z')
33+
list[2] shouldBe 'z'
34+
35+
list[0] = 'y'
36+
list[0] shouldBe 'y'
37+
}
38+
39+
"MutableRingList: programs can be spun" {
40+
mutableRingListOf("abcde").shiftRight(1).toJoinedString() shouldBe "eabcd"
41+
42+
mutableRingListOf("abcde").shiftRight(2).toJoinedString() shouldBe "deabc"
43+
mutableRingListOf("abcde").shiftRight(1).shiftRight(1).toJoinedString() shouldBe "deabc"
44+
45+
mutableRingListOf("abcde").shiftRight(3).toJoinedString() shouldBe "cdeab"
46+
mutableRingListOf("abcde").shiftRight(1).shiftRight(2).toJoinedString() shouldBe "cdeab"
47+
48+
mutableRingListOf("abcde").shiftRight(5).toJoinedString() shouldBe "abcde"
49+
}
50+
51+
"MutableRingList: get after spin" {
52+
val list = mutableRingListOf("abcde").shiftRight(1)
53+
list[0] shouldBe 'e'
54+
list[4] shouldBe 'd'
55+
}
56+
57+
"MutableRingList: set after spin" {
58+
val list = mutableRingListOf("abcde").shiftRight(1)
59+
list[0] = 'x'
60+
list[0] shouldBe 'x'
61+
list[4] shouldBe 'd'
62+
}
63+
64+
"MutableRingList: toList returns the modified state" {
65+
val list = mutableRingListOf("abcde")
66+
.shiftRight(2)
67+
.swap(0, 1)
68+
list.toList() shouldBe listOf('e', 'd', 'a', 'b', 'c')
69+
}
70+
71+
"programs can be exchanged at positions" {
72+
mutableRingListOf("abcde").swap(3, 4).toJoinedString() shouldBe "abced"
73+
mutableRingListOf("abcde").swap(0, 4).toJoinedString() shouldBe "ebcda"
74+
mutableRingListOf("abcde").swap(3, 3).toJoinedString() shouldBe "abcde"
75+
}
76+
77+
"programs can be exchanged by their name" {
78+
mutableRingListOf("abcde").swap('d', 'e').toJoinedString() shouldBe "abced"
79+
mutableRingListOf("abcde").swap('a', 'e').toJoinedString() shouldBe "ebcda"
80+
mutableRingListOf("abcde").swap('c', 'c').toJoinedString() shouldBe "abcde"
81+
}
82+
83+
"part 1: The order of the programs after their dance" {
84+
PermutationPromenade().doTheDance("abcde", input) shouldBe "baedc"
85+
PermutationPromenade().part1(input) shouldBe "paedcbfghijklmno"
86+
}
87+
88+
"part 2: The order of the programs after one billion repetitions of their dance" {
89+
PermutationPromenade().doTheDance("abcde", input, 1_000_000_000) shouldBe "abcde"
90+
PermutationPromenade().part2(input) shouldBe "ghidjklmnopabcef"
91+
}
92+
})

0 commit comments

Comments
 (0)