Skip to content

Commit 9c8e0b2

Browse files
authored
Daty 06 2024 (#265)
* Day 06 2024 - Initial Implementation
1 parent a0fd273 commit 9c8e0b2

File tree

4 files changed

+171
-0
lines changed

4 files changed

+171
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package me.peckb.aoc._2024.calendar.day06
2+
3+
import me.peckb.aoc._2024.calendar.day06.Direction.NORTH
4+
import javax.inject.Inject
5+
import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory
6+
7+
class Day06 @Inject constructor(
8+
private val generatorFactory: InputGeneratorFactory,
9+
) {
10+
companion object {
11+
const val MAX_STEPS = 8000 // minor optimization - 10k worked well, but this saved time
12+
}
13+
14+
fun partOne(filename: String) = generatorFactory.forFile(filename).read { lines ->
15+
val (area, guard) = setup(lines)
16+
17+
val steps = mutableSetOf<Location>()
18+
walk(area, guard, step = steps::add)
19+
20+
steps.size
21+
}
22+
23+
fun partTwo(filename: String) = generatorFactory.forFile(filename).read { lines ->
24+
val (area, guardStart) = setup(lines)
25+
26+
var guard = Guard(guardStart.direction, guardStart.location.copy())
27+
val steps = mutableSetOf<Location>()
28+
29+
walk(area, guard, step = steps::add)
30+
31+
steps.count { (y, x) ->
32+
if (guardStart.location.y == y && guardStart.location.x == x) {
33+
false
34+
} else {
35+
var stepCount = 0
36+
area[y][x] = Space.BLOCKED
37+
38+
walk(area, guard, totalStepHandler = { stepCount = it})
39+
40+
guard = Guard(guardStart.direction, guardStart.location.copy())
41+
area[y][x] = Space.EMPTY
42+
43+
stepCount == MAX_STEPS
44+
}
45+
}
46+
}
47+
48+
private fun setup(lines: Sequence<String>): Pair<MutableList<MutableList<Space>>, Guard> {
49+
val area = mutableListOf<MutableList<Space>>()
50+
var guard = Guard(NORTH, Location(-1, -1))
51+
52+
lines.forEachIndexed { yIndex, line ->
53+
val row = mutableListOf<Space>()
54+
line.forEachIndexed { xIndex, char ->
55+
when (char) {
56+
'.' -> row.add(Space.EMPTY)
57+
'#' -> row.add(Space.BLOCKED)
58+
'^' -> {
59+
row.add(Space.EMPTY)
60+
guard = Guard(NORTH, Location(yIndex, xIndex))
61+
}
62+
}
63+
}
64+
area.add(row)
65+
}
66+
67+
return area to guard
68+
}
69+
70+
private fun walk(
71+
area: List<List<Space>>,
72+
guardStart: Guard,
73+
step: (Location) -> Unit = { },
74+
totalStepHandler: (Int) -> Unit = { }
75+
) {
76+
val guard = Guard(guardStart.direction, guardStart.location.copy())
77+
78+
var stepCount = 0
79+
80+
while (stepCount < MAX_STEPS && area.containsLocation(guard.location.y, guard.location.x)) {
81+
val (nextY, nextX) = guard.direction.step(guard.location.y, guard.location.x)
82+
83+
step(guard.location.copy())
84+
85+
if (area.containsLocation(nextY, nextX) && area[nextY][nextX] == Space.BLOCKED) {
86+
guard.turnRight()
87+
} else {
88+
guard.stepForward()
89+
}
90+
91+
stepCount++
92+
}
93+
94+
totalStepHandler(stepCount)
95+
}
96+
}
97+
98+
99+
private fun List<List<*>>.containsLocation(y: Int, x: Int): Boolean {
100+
return y in indices && x in this[y].indices
101+
}
102+
103+
data class Guard(var direction: Direction, val location: Location) {
104+
fun stepForward() { location.step(direction) }
105+
106+
fun turnRight() { direction = direction.turnRight() }
107+
}
108+
109+
enum class Direction {
110+
NORTH { override fun turnRight(): Direction { return EAST } },
111+
EAST { override fun turnRight(): Direction { return SOUTH } },
112+
SOUTH { override fun turnRight(): Direction { return WEST } },
113+
WEST { override fun turnRight(): Direction { return NORTH } };
114+
115+
abstract fun turnRight(): Direction
116+
117+
fun step(y: Int, x: Int): Pair<Int, Int> {
118+
return when (this) {
119+
NORTH -> y - 1 to x
120+
EAST -> y to x + 1
121+
SOUTH -> y + 1 to x
122+
WEST -> y to x - 1
123+
}
124+
}
125+
}
126+
127+
data class Location(var y: Int, var x: Int) {
128+
fun step(direction: Direction) {
129+
direction.step(y, x).also { (newY, newX) ->
130+
y = newY
131+
x = newX
132+
}
133+
}
134+
}
135+
136+
enum class Space { EMPTY, BLOCKED }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## [Day 6: Guard Gallivant](https://adventofcode.com/2024/day/6)

src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import me.peckb.aoc._2024.calendar.day02.Day02Test
55
import me.peckb.aoc._2024.calendar.day03.Day03Test
66
import me.peckb.aoc._2024.calendar.day04.Day04Test
77
import me.peckb.aoc._2024.calendar.day05.Day05Test
8+
import me.peckb.aoc._2024.calendar.day06.Day06Test
89
import javax.inject.Singleton
910
import me.peckb.aoc.DayComponent
1011
import me.peckb.aoc.InputModule
@@ -18,4 +19,5 @@ internal interface TestDayComponent : DayComponent {
1819
fun inject(day03Test: Day03Test)
1920
fun inject(day04Test: Day04Test)
2021
fun inject(day05Test: Day05Test)
22+
fun inject(day06Test: Day06Test)
2123
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package me.peckb.aoc._2024.calendar.day06
2+
3+
import javax.inject.Inject
4+
5+
import me.peckb.aoc._2024.DaggerTestDayComponent
6+
import org.junit.jupiter.api.Assertions.assertEquals
7+
import org.junit.jupiter.api.BeforeEach
8+
import org.junit.jupiter.api.Test
9+
10+
internal class Day06Test {
11+
@Inject
12+
lateinit var day06: Day06
13+
14+
@BeforeEach
15+
fun setup() {
16+
DaggerTestDayComponent.create().inject(this)
17+
}
18+
19+
@Test
20+
fun testDay06PartOne() {
21+
assertEquals(5145, day06.partOne(DAY_06))
22+
}
23+
24+
@Test
25+
fun testDay06PartTwo() {
26+
assertEquals(1523, day06.partTwo(DAY_06))
27+
}
28+
29+
companion object {
30+
private const val DAY_06: String = "advent-of-code-input/2024/day06.input"
31+
}
32+
}

0 commit comments

Comments
 (0)