@@ -2,7 +2,6 @@ package de.ronny_h.aoc.year2018.day15
22
33import de.ronny_h.aoc.AdventOfCode
44import de.ronny_h.aoc.extensions.collections.filterMinBy
5- import de.ronny_h.aoc.extensions.graphs.shortestpath.ShortestPath
65import de.ronny_h.aoc.extensions.grids.Coordinates
76import de.ronny_h.aoc.extensions.grids.SimpleCharGrid
87import de.ronny_h.aoc.year2018.day15.CombatArea.PlayerType.Elf
@@ -11,54 +10,42 @@ import io.github.oshai.kotlinlogging.KotlinLogging
1110
1211private val logger = KotlinLogging .logger {}
1312
14- fun main () = BeverageBandits ().run (0 , 0 )
13+ fun main () = BeverageBandits ().run (218272 , 0 )
1514
16- class BeverageBandits : AdventOfCode <Long >(2018 , 15 ) {
17- override fun part1 (input : List <String >): Long {
15+ class BeverageBandits : AdventOfCode <Int >(2018 , 15 ) {
16+ override fun part1 (input : List <String >): Int {
1817 val combatArea = CombatArea (input)
1918 while (combatArea.takeOneRound()) {
2019 // the action takes place in the condition
2120 }
2221 return combatArea.outcome()
2322 }
2423
25- override fun part2 (input : List <String >): Long {
24+ override fun part2 (input : List <String >): Int {
2625 return 0
2726 }
2827}
2928
3029class CombatArea (input : List <String >) : SimpleCharGrid(input) {
3130 private val cavern = ' .'
32-
33- private data class Player (
34- val type : PlayerType ,
35- var position : Coordinates ,
36- val attackPower : Int = 3 ,
37- var hitPoints : Int = 200
38- )
39-
40- private enum class PlayerType (val char : Char ) {
41- Elf (' E' ), Goblin (' G' )
42- }
43-
44- private val units = buildList {
45- forEachCoordinates { position, square ->
46- when (square) {
47- Elf .char -> add(Player (Elf , position))
48- Goblin .char -> add(Player (Goblin , position))
49- }
50- }.last()
51- }.toMutableList()
52-
53- private var fullRounds = 0L
31+ private val attackPower = 3
32+
33+ private var fullRounds = 0
34+ private val units = forEachCoordinates { position, square ->
35+ when (square) {
36+ Elf .char -> Player (Elf , position)
37+ Goblin .char -> Player (Goblin , position)
38+ else -> null
39+ }
40+ }.filterNotNull().toMutableList()
5441
5542 /* *
5643 * @return if combat continues
5744 */
5845 fun takeOneRound (): Boolean {
59- if (fullRounds % 10 == 0L ) logStats()
46+ if (fullRounds % 50 == 0 ) logStats()
6047 units.sortedBy { it.position }.forEach { unit ->
61- logger.debug { " unit $unit " }
48+ logger.trace { " unit $unit " }
6249 val enemyType = if (unit.type == Elf ) Goblin else Elf
6350 val targets = units.filter { it.type == enemyType }
6451 if (targets.isEmpty()) {
@@ -72,14 +59,30 @@ class CombatArea(input: List<String>) : SimpleCharGrid(input) {
7259 return true
7360 }
7461
75- fun outcome (): Long = fullRounds * units.sumOf { it.hitPoints }
62+ fun outcome (): Int {
63+ logStats()
64+ val hitPointsLeft = units.sumOf { it.hitPoints }
65+ logger.info { " rounds: $fullRounds , hit points left: $hitPointsLeft " }
66+ return fullRounds * hitPointsLeft
67+ }
68+
69+ private data class Player (
70+ val type : PlayerType ,
71+ var position : Coordinates ,
72+ var hitPoints : Int = 200
73+ ) {
74+ fun isDead () = hitPoints <= 0
75+ override fun toString (): String = " ${type.char}${position} _$hitPoints "
76+ }
7677
77- private data class Target (val position : Coordinates , val path : ShortestPath <Coordinates >)
78+ private enum class PlayerType (val char : Char ) {
79+ Elf (' E' ), Goblin (' G' )
80+ }
7881
7982 private fun Player.move (
8083 targets : List <Player >
8184 ) {
82- logger.debug { " moving $this " }
85+ logger.trace { " moving $this " }
8386 if (targets.any { it.position in position.neighbours() }) {
8487 // already in range of a target
8588 return
@@ -88,51 +91,51 @@ class CombatArea(input: List<String>) : SimpleCharGrid(input) {
8891 val targetsInRange = targets.flatMap { target ->
8992 target.position.neighbours().filter { getAt(it) == cavern }
9093 }
91- // TODO make path precedence in reading order configurable
92- val targetsWithPaths = shortestPaths(
94+ val shortestPaths = shortestPaths(
9395 start = position,
9496 goals = targetsInRange,
9597 isObstacle = { it != cavern })
96- .map { Target (it.path.last(), it) }
9798
98- if (targetsWithPaths .isEmpty()) {
99+ if (shortestPaths .isEmpty()) {
99100 // no path to any target found
100101 return
101102 }
102- logger.debug { " ${targetsWithPaths .size} paths found" }
103- val nearestTargetPath = targetsWithPaths
104- .filterMinBy { it.path. distance }
105- .filterMinBy { it.position }
106- .filterMinBy { it.path.path [1 ] }
103+ logger.trace { " ${shortestPaths .size} paths found" }
104+ val nearestTargetPath = shortestPaths
105+ .filterMinBy { it.distance }
106+ .filterMinBy { it.path.last() }
107+ .filterMinBy { it.path[1 ] }
107108 .first()
108- moveTo(nearestTargetPath.path.path [1 ])
109+ moveTo(nearestTargetPath.path[1 ])
109110 }
110111
111112 private fun Player.attack (targets : List <Player >) {
112- logger.debug { " attacking with $this " }
113+ if (this .isDead()) return
114+ logger.trace { " attacking with $this " }
113115 val opponent = position
114116 .neighbours()
115117 .mapNotNull { neighbour -> targets.firstOrNull { it.position == neighbour } }
116118 .filterMinBy { it.hitPoints }
117119 .minByOrNull { it.position }
118120 if (opponent == null ) return
119121
120- logger.debug { " hitting $opponent " }
121122 opponent.hitPoints - = attackPower
123+ logger.debug { " $this hit $opponent " }
122124
123125 if (opponent.hitPoints <= 0 ) {
124126 opponent.die()
125127 }
126128 }
127129
128130 private fun Player.moveTo (newPosition : Coordinates ) {
129- logger.debug { " moving to $newPosition " }
131+ logger.debug { " $this moves to $newPosition " }
130132 setAt(position, cavern)
131133 setAt(newPosition, type.char)
132134 position = newPosition
133135 }
134136
135137 private fun Player.die () {
138+ logger.debug { " $this died" }
136139 setAt(position, cavern)
137140 units.remove(this )
138141 }
0 commit comments