Skip to content

Commit ebf3db3

Browse files
committed
Solution 2018-04 (Repose Record)
1 parent 703001a commit ebf3db3

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package de.ronny_h.aoc.year2018.day04
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.year2018.day04.GuardEvent.*
5+
6+
fun main() = ReposeRecord().run(140932, 51232)
7+
8+
class ReposeRecord : AdventOfCode<Int>(2018, 4) {
9+
override fun part1(input: List<String>): Int {
10+
val guardDutyRecords = input.parseRecords()
11+
12+
val guardWithMaxSleep = guardDutyRecords.findGuardWithMostSleep()
13+
println("the guard with max sleep minutes: $guardWithMaxSleep")
14+
15+
val maxSleepMinute = guardDutyRecords
16+
.filter { it.guardId == guardWithMaxSleep.guardId }
17+
.findMinuteAsleepMostFrequently()
18+
println("the guard was asleep most during minute: $maxSleepMinute}")
19+
return guardWithMaxSleep.guardId * maxSleepMinute.minute
20+
}
21+
22+
override fun part2(input: List<String>): Int {
23+
input.parseRecords()
24+
.groupBy { it.guardId }
25+
.filter {
26+
// there are actually guards that don't fall asleep at all
27+
it.value.any { record -> record.event == FALL_ASLEEP }
28+
}
29+
.map { it.key to it.value.findMinuteAsleepMostFrequently() }
30+
.maxBy { it.second.sleepCount }
31+
.let { return it.first * it.second.minute }
32+
}
33+
34+
private fun List<GuardDutyRecord>.findGuardWithMostSleep(): GuardSleepMinutes {
35+
val sleepMinutes = mutableMapOf<Int, Int>().withDefault { 0 }
36+
var guardId: Int = -1
37+
var fallAsleepAt: Int = -1
38+
forEach {
39+
when (it.event) {
40+
BEGIN_SHIFT -> guardId = it.guardId
41+
FALL_ASLEEP -> fallAsleepAt = it.minute()
42+
WAKE_UP -> sleepMinutes[guardId] = sleepMinutes.getValue(guardId) + it.minute() - fallAsleepAt
43+
}
44+
}
45+
return sleepMinutes
46+
.maxBy { it.value }
47+
.let { GuardSleepMinutes(it.key, it.value) }
48+
}
49+
50+
private fun List<GuardDutyRecord>.findMinuteAsleepMostFrequently(): SleepPerMinute {
51+
val asleepPerMinute = mutableMapOf<Int, Int>().withDefault { 0 }
52+
var fallAsleepAt: Int = -1
53+
forEach {
54+
when (it.event) {
55+
FALL_ASLEEP -> fallAsleepAt = it.minute()
56+
WAKE_UP -> {
57+
for (minute in fallAsleepAt..<it.minute()) {
58+
asleepPerMinute[minute] = asleepPerMinute.getValue(minute) + 1
59+
}
60+
}
61+
62+
else -> {} // noting to do
63+
}
64+
}
65+
66+
return asleepPerMinute.maxBy { it.value }.let { SleepPerMinute(it.key, it.value) }
67+
}
68+
}
69+
70+
// records have the form:
71+
// [1518-11-01 00:00] Guard #10 begins shift
72+
// [1518-11-01 00:05] falls asleep
73+
// [1518-11-01 00:25] wakes up
74+
fun List<String>.parseRecords(): List<GuardDutyRecord> {
75+
var guardId = -1
76+
77+
return map {
78+
val (time, event) = it.substringAfter("[").split("] ")
79+
time to event
80+
}
81+
.sortedBy { it.first }
82+
.map {
83+
when (it.second) {
84+
"falls asleep" -> GuardDutyRecord(it.first, guardId, FALL_ASLEEP)
85+
"wakes up" -> GuardDutyRecord(it.first, guardId, WAKE_UP)
86+
else -> {
87+
guardId = it.second.substringAfter("Guard #").substringBefore(" begins shift").toInt()
88+
GuardDutyRecord(it.first, guardId, BEGIN_SHIFT)
89+
}
90+
}
91+
}
92+
}
93+
94+
data class GuardDutyRecord(private val time: String, val guardId: Int, val event: GuardEvent) {
95+
fun minute(): Int = time.split(":")[1].toInt()
96+
}
97+
98+
enum class GuardEvent { BEGIN_SHIFT, FALL_ASLEEP, WAKE_UP }
99+
100+
data class GuardSleepMinutes(val guardId: Int, val minutes: Int)
101+
102+
data class SleepPerMinute(val minute: Int, val sleepCount: Int)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package de.ronny_h.aoc.year2018.day04
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import de.ronny_h.aoc.year2018.day04.GuardEvent.*
5+
import io.kotest.core.spec.style.StringSpec
6+
import io.kotest.matchers.shouldBe
7+
8+
class ReposeRecordTest : StringSpec({
9+
10+
val input = """
11+
[1518-11-01 00:00] Guard #10 begins shift
12+
[1518-11-01 00:05] falls asleep
13+
[1518-11-01 00:25] wakes up
14+
[1518-11-01 00:30] falls asleep
15+
[1518-11-01 00:55] wakes up
16+
[1518-11-01 23:58] Guard #99 begins shift
17+
[1518-11-02 00:40] falls asleep
18+
[1518-11-02 00:50] wakes up
19+
[1518-11-03 00:05] Guard #10 begins shift
20+
[1518-11-03 00:24] falls asleep
21+
[1518-11-03 00:29] wakes up
22+
[1518-11-04 00:02] Guard #99 begins shift
23+
[1518-11-04 00:36] falls asleep
24+
[1518-11-04 00:46] wakes up
25+
[1518-11-05 00:03] Guard #99 begins shift
26+
[1518-11-05 00:45] falls asleep
27+
[1518-11-05 00:55] wakes up
28+
""".asList()
29+
30+
"input can be parsed" {
31+
input.parseRecords() shouldBe listOf(
32+
GuardDutyRecord("1518-11-01 00:00", 10, BEGIN_SHIFT),
33+
GuardDutyRecord("1518-11-01 00:05", 10, FALL_ASLEEP),
34+
GuardDutyRecord("1518-11-01 00:25", 10, WAKE_UP),
35+
GuardDutyRecord("1518-11-01 00:30", 10, FALL_ASLEEP),
36+
GuardDutyRecord("1518-11-01 00:55", 10, WAKE_UP),
37+
GuardDutyRecord("1518-11-01 23:58", 99, BEGIN_SHIFT),
38+
GuardDutyRecord("1518-11-02 00:40", 99, FALL_ASLEEP),
39+
GuardDutyRecord("1518-11-02 00:50", 99, WAKE_UP),
40+
GuardDutyRecord("1518-11-03 00:05", 10, BEGIN_SHIFT),
41+
GuardDutyRecord("1518-11-03 00:24", 10, FALL_ASLEEP),
42+
GuardDutyRecord("1518-11-03 00:29", 10, WAKE_UP),
43+
GuardDutyRecord("1518-11-04 00:02", 99, BEGIN_SHIFT),
44+
GuardDutyRecord("1518-11-04 00:36", 99, FALL_ASLEEP),
45+
GuardDutyRecord("1518-11-04 00:46", 99, WAKE_UP),
46+
GuardDutyRecord("1518-11-05 00:03", 99, BEGIN_SHIFT),
47+
GuardDutyRecord("1518-11-05 00:45", 99, FALL_ASLEEP),
48+
GuardDutyRecord("1518-11-05 00:55", 99, WAKE_UP),
49+
)
50+
}
51+
52+
"input gets sorted by timestamp" {
53+
"""
54+
[1518-11-01 00:25] wakes up
55+
[1518-11-01 00:05] falls asleep
56+
[1518-11-01 00:00] Guard #10 begins shift
57+
[1518-11-01 00:30] falls asleep
58+
""".asList()
59+
.parseRecords() shouldBe listOf(
60+
GuardDutyRecord("1518-11-01 00:00", 10, BEGIN_SHIFT),
61+
GuardDutyRecord("1518-11-01 00:05", 10, FALL_ASLEEP),
62+
GuardDutyRecord("1518-11-01 00:25", 10, WAKE_UP),
63+
GuardDutyRecord("1518-11-01 00:30", 10, FALL_ASLEEP),
64+
)
65+
}
66+
67+
"part 1: the guard's id who slept most, multiplied by the minute he slept most" {
68+
ReposeRecord().part1(input) shouldBe 240
69+
}
70+
71+
"part 2: the guard's id who sleeps most frequently in the same minute, multiplied by that minute" {
72+
ReposeRecord().part2(input) shouldBe 4455
73+
}
74+
})

0 commit comments

Comments
 (0)