Skip to content

Commit cb276a3

Browse files
committed
Solution 2017-11 (Hex Ed)
1 parent 0c38527 commit cb276a3

File tree

2 files changed

+187
-0
lines changed
  • src
    • main/kotlin/de/ronny_h/aoc/year2017/day11
    • test/kotlin/de/ronny_h/aoc/year2017/day11

2 files changed

+187
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package de.ronny_h.aoc.year2017.day11
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.year2017.day11.HexagonalGrid.Direction.*
5+
import kotlin.math.abs
6+
import kotlin.math.max
7+
8+
fun main() = HexEd().run(812, 1603)
9+
10+
class HexEd : AdventOfCode<Int>(2017, 11) {
11+
override fun part1(input: List<String>): Int {
12+
val hexgrid = HexagonalGrid()
13+
input.single()
14+
.parseDirections()
15+
.forEach { hexgrid.goInDirection(it) }
16+
return hexgrid.start.distanceTo(hexgrid.currentPosition)
17+
}
18+
19+
override fun part2(input: List<String>): Int {
20+
val hexgrid = HexagonalGrid()
21+
return input.single()
22+
.parseDirections()
23+
.maxOf {
24+
hexgrid.goInDirection(it)
25+
hexgrid.start distanceTo hexgrid.currentPosition
26+
}
27+
}
28+
}
29+
30+
fun String.parseDirections(): List<HexagonalGrid.Direction> = split(",").map {
31+
when (it) {
32+
"n" -> NORTH
33+
"ne" -> NORTH_EAST
34+
"se" -> SOUTH_EAST
35+
"s" -> SOUTH
36+
"sw" -> SOUTH_WEST
37+
"nw" -> NORTH_WEST
38+
else -> error("illegal direction: $it")
39+
}
40+
}
41+
42+
/**
43+
* We define coordinates for the hexagonal grid with columns and rows where columns that contain only even rows
44+
* alternate with columns containing only odd rows.
45+
*
46+
* *Example:* From a hextile in the middle with coordinates (0,0), the adjacent hextiles' coordinates are:
47+
* ```
48+
* +-+ -2,0 +-+
49+
* \ n /
50+
* -1,-1 +--+ -1,1
51+
* nw / \ ne
52+
* +-+ 0,0 +-+
53+
* sw \ / se
54+
* 1,-1 +--+ 1,1
55+
* / s \
56+
* +-+ 2,0 +-+
57+
* ```
58+
* */
59+
class HexagonalGrid {
60+
61+
data class Hextile(val row: Int, val col: Int) {
62+
63+
init {
64+
check(if (abs(col) % 2 == 0) abs(row) % 2 == 0 else true) { "Invalid Hextile($row, $col): In even columns only even row numbers are allowed." }
65+
check(if (abs(col) % 2 == 1) abs(row) % 2 == 1 else true) { "Invalid Hextile($row, $col): In odd columns only odd row numbers are allowed." }
66+
}
67+
68+
operator fun plus(direction: Direction) = Hextile(row + direction.row, col + direction.col)
69+
70+
infix fun distanceTo(other: Hextile): Int {
71+
// if hextiles would be in the same col:
72+
val verticalDistance = abs((row - other.row) / 2)
73+
// if hextiles would be in the "same" row:
74+
val horizontalDistance = abs(col - other.col)
75+
// in different rows/columns, per column, a distance of 1 in vertical direction can be made up
76+
return horizontalDistance + max(verticalDistance - max(horizontalDistance - verticalDistance, 0), 0)
77+
}
78+
}
79+
80+
enum class Direction(val row: Int, val col: Int) {
81+
NORTH(-2, 0),
82+
NORTH_EAST(-1, +1),
83+
SOUTH_EAST(+1, +1),
84+
SOUTH(+2, 0),
85+
SOUTH_WEST(+1, -1),
86+
NORTH_WEST(-1, -1),
87+
}
88+
89+
val start = Hextile(0, 0)
90+
var currentPosition = start
91+
92+
fun goInDirection(direction: Direction) {
93+
currentPosition += direction
94+
}
95+
}
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.day11
2+
3+
import de.ronny_h.aoc.year2017.day11.HexagonalGrid.Direction.*
4+
import de.ronny_h.aoc.year2017.day11.HexagonalGrid.Hextile
5+
import io.kotest.assertions.throwables.shouldThrow
6+
import io.kotest.core.spec.style.StringSpec
7+
import io.kotest.data.forAll
8+
import io.kotest.data.row
9+
import io.kotest.matchers.shouldBe
10+
11+
class HexEdTest : StringSpec({
12+
13+
"input can be parsed" {
14+
forAll(
15+
row("n", listOf(NORTH)),
16+
row("ne", listOf(NORTH_EAST)),
17+
row("se", listOf(SOUTH_EAST)),
18+
row("s", listOf(SOUTH)),
19+
row("sw", listOf(SOUTH_WEST)),
20+
row("nw", listOf(NORTH_WEST)),
21+
row("nw,s,sw", listOf(NORTH_WEST, SOUTH, SOUTH_WEST)),
22+
) { input, expected ->
23+
input.parseDirections() shouldBe expected
24+
}
25+
}
26+
27+
"go in directions leads to target hextile" {
28+
forAll(
29+
row("ne,ne,ne", Hextile(-3, 3)),
30+
row("ne,ne,sw,sw", Hextile(0, 0)),
31+
row("ne,ne,s,s", Hextile(2, 2)),
32+
row("se,sw,se,sw,sw", Hextile(5, -1)),
33+
) { directions, hextile ->
34+
val hexGrid = HexagonalGrid()
35+
directions.parseDirections().forEach { hexGrid.goInDirection(it) }
36+
hexGrid.currentPosition shouldBe hextile
37+
}
38+
}
39+
40+
"only valid hextiles can be created" {
41+
forAll(
42+
row(1, 0),
43+
row(0, 1),
44+
row(11, 10),
45+
) { row, col ->
46+
shouldThrow<IllegalStateException> {
47+
Hextile(row, col)
48+
}
49+
}
50+
}
51+
52+
"hextile distances" {
53+
forAll(
54+
row(Hextile(0, 0), Hextile(0, 0), 0),
55+
row(Hextile(0, 0), Hextile(2, 0), 1),
56+
row(Hextile(0, 0), Hextile(10, 0), 5),
57+
row(Hextile(-10, 0), Hextile(0, 0), 5),
58+
row(Hextile(0, 0), Hextile(1, 1), 1),
59+
row(Hextile(0, 0), Hextile(0, 2), 2),
60+
row(Hextile(0, 0), Hextile(1, 3), 3),
61+
row(Hextile(0, 0), Hextile(3, 3), 3),
62+
row(Hextile(-3, -3), Hextile(0, 0), 3),
63+
row(Hextile(-3, -3), Hextile(3, 3), 6),
64+
) { first, second, distance ->
65+
first distanceTo second shouldBe distance
66+
}
67+
}
68+
69+
"part 1: the fewest number of steps to reach the target hextile" {
70+
forAll(
71+
row("ne,ne,ne", 3),
72+
row("ne,ne,sw,sw", 0),
73+
row("ne,ne,s,s", 2),
74+
row("se,sw,se,sw,sw", 3),
75+
) { directions, distance ->
76+
val input = listOf(directions)
77+
HexEd().part1(input) shouldBe distance
78+
}
79+
}
80+
81+
"part 2: the furthest steps away on the path" {
82+
forAll(
83+
row("ne,ne,ne", 3),
84+
row("ne,ne,sw,sw", 2),
85+
row("ne,ne,s,s", 2),
86+
row("se,sw,se,sw,sw", 3),
87+
) { directions, distance ->
88+
val input = listOf(directions)
89+
HexEd().part2(input) shouldBe distance
90+
}
91+
}
92+
})

0 commit comments

Comments
 (0)