-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Day 22 2020 * PR cleanup
- Loading branch information
Showing
7 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
src/main/kotlin/me/peckb/aoc/_2020/calendar/day22/CombatPlayer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package me.peckb.aoc._2020.calendar.day22 | ||
|
||
import java.util.LinkedList | ||
import java.util.Queue | ||
|
||
data class CombatPlayer( | ||
val id: Int, | ||
val cardValues: Queue<Int> = LinkedList() | ||
) { | ||
fun addCard(toInt: Int) { | ||
cardValues.add(toInt) | ||
} | ||
|
||
fun hasCards(): Boolean = cardValues.peek() != null | ||
|
||
fun turnCard(): Int = cardValues.remove() | ||
|
||
fun addCards(higherCard: Int, lowerCard: Int) { | ||
addCard(higherCard) | ||
addCard(lowerCard) | ||
} | ||
|
||
fun generateScore(): Long { | ||
return cardValues.reversed().foldIndexed(0L) { index, total, card -> | ||
val multiplier = index + 1 | ||
total + (multiplier * card) | ||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
src/main/kotlin/me/peckb/aoc/_2020/calendar/day22/Day22.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package me.peckb.aoc._2020.calendar.day22 | ||
|
||
import javax.inject.Inject | ||
|
||
import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory | ||
import kotlin.math.max | ||
|
||
class Day22 @Inject constructor( | ||
private val generatorFactory: InputGeneratorFactory, | ||
) { | ||
fun partOne(filename: String) = generatorFactory.forFile(filename).read { input -> | ||
val players = mutableListOf<CombatPlayer>() | ||
|
||
val iterator = input.iterator() | ||
while (iterator.hasNext()) { | ||
val next = iterator.next() | ||
if (next.startsWith("Player ")) { | ||
// new player | ||
players.add(CombatPlayer(id = next.dropLast(1).takeLast(1).toInt())) | ||
} else if (next.isEmpty()) { | ||
// end of current player | ||
} else { | ||
players.last().addCard(next.toInt()) | ||
} | ||
} | ||
|
||
val playerOne = players.first() | ||
val playerTwo = players.last() | ||
|
||
while (playerOne.hasCards() && playerTwo.hasCards()) { | ||
val playerOneCard = playerOne.turnCard() | ||
val playerTwoCard = playerTwo.turnCard() | ||
|
||
if (playerOneCard > playerTwoCard) { | ||
playerOne.addCards(playerOneCard, playerTwoCard) | ||
} else if (playerTwoCard > playerOneCard) { | ||
playerTwo.addCards(playerTwoCard, playerOneCard) | ||
} else { // cards are equal | ||
playerOne.addCard(playerOneCard) | ||
playerTwo.addCard(playerTwoCard) | ||
} | ||
} | ||
|
||
val p1Score = playerOne.generateScore() | ||
val p2Score = playerTwo.generateScore() | ||
|
||
max(p1Score, p2Score) | ||
} | ||
|
||
fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> | ||
val players = mutableListOf<RecursiveCombatPlayer>() | ||
|
||
val iterator = input.iterator() | ||
while (iterator.hasNext()) { | ||
val next = iterator.next() | ||
if (next.startsWith("Player ")) { | ||
// new player | ||
players.add(RecursiveCombatPlayer(id = next.dropLast(1).takeLast(1).toInt())) | ||
} else if (next.isEmpty()) { | ||
// end of current player | ||
} else { | ||
players.last().addCard(next.toInt()) | ||
} | ||
} | ||
|
||
val playerOne = players.first() | ||
val playerTwo = players.last() | ||
|
||
RecursiveCombat(playerOne, playerTwo).playGame() | ||
|
||
val p1Score = playerOne.generateScore() | ||
val p2Score = playerTwo.generateScore() | ||
|
||
max(p1Score, p2Score) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
## [Day 22: Crab Combat](https://adventofcode.com/2020/day/22) |
67 changes: 67 additions & 0 deletions
67
src/main/kotlin/me/peckb/aoc/_2020/calendar/day22/RecursiveCombat.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package me.peckb.aoc._2020.calendar.day22 | ||
|
||
import me.peckb.aoc._2020.calendar.day22.RecursiveCombatWinner.* | ||
import org.apache.commons.lang3.ObjectUtils.clone | ||
|
||
class RecursiveCombat( | ||
private val playerOne: RecursiveCombatPlayer, | ||
private val playerTwo: RecursiveCombatPlayer | ||
) { | ||
private val previouslySeenPlayerOneConfigurations = mutableSetOf<String>() | ||
private val previouslySeenPlayerTwoConfigurations = mutableSetOf<String>() | ||
|
||
fun playGame(): RecursiveCombatWinner { | ||
while (playerOne.hasCards() && playerTwo.hasCards()) { | ||
val (winner, p1Card, p2Card) = deal() | ||
if (p1Card == null || p2Card == null) return PLAYER_ONE | ||
|
||
when (winner) { | ||
PLAYER_ONE -> playerOne.addCards(p1Card, p2Card) | ||
PLAYER_TWO -> playerTwo.addCards(p2Card, p1Card) | ||
NO_WINNER -> throw IllegalStateException("There must always be a winner!") | ||
} | ||
} | ||
|
||
if (playerOne.hasCards()) return PLAYER_ONE | ||
return PLAYER_TWO | ||
} | ||
|
||
private fun deal(): Triple<RecursiveCombatWinner, Int?, Int?> { | ||
val playerOneConfiguration = playerOne.deckConfiguration() | ||
val playerTwoConfiguration = playerTwo.deckConfiguration() | ||
|
||
if ( | ||
previouslySeenPlayerOneConfigurations.contains(playerOneConfiguration) && | ||
previouslySeenPlayerTwoConfigurations.contains(playerTwoConfiguration) | ||
) { | ||
return Triple(PLAYER_ONE, null, null) | ||
} else { | ||
previouslySeenPlayerOneConfigurations.add(playerOneConfiguration) | ||
previouslySeenPlayerTwoConfigurations.add(playerTwoConfiguration) | ||
} | ||
|
||
val playerOneCard = playerOne.turnCard() | ||
val playerTwoCard = playerTwo.turnCard() | ||
|
||
val winner = if (playerOne.haveAtLeast(playerOneCard) && playerTwo.haveAtLeast(playerTwoCard)) { | ||
RecursiveCombat( | ||
playerOne.copy(cardValues = clone(playerOne.cardValues)).dropUntil(playerOneCard), | ||
playerTwo.copy(cardValues = clone(playerTwo.cardValues)).dropUntil(playerTwoCard) | ||
).playGame() | ||
} else { | ||
if (playerOneCard > playerTwoCard) { | ||
PLAYER_ONE | ||
} else if (playerTwoCard > playerOneCard) { | ||
PLAYER_TWO | ||
} else { // cards are equal | ||
NO_WINNER | ||
} | ||
} | ||
|
||
return Triple(winner, playerOneCard, playerTwoCard) | ||
} | ||
} | ||
|
||
enum class RecursiveCombatWinner { | ||
PLAYER_ONE, PLAYER_TWO, NO_WINNER | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/kotlin/me/peckb/aoc/_2020/calendar/day22/RecursiveCombatPlayer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package me.peckb.aoc._2020.calendar.day22 | ||
|
||
import java.util.LinkedList | ||
import java.util.Queue | ||
|
||
data class RecursiveCombatPlayer( | ||
val id: Int, | ||
val cardValues: Queue<Int> = LinkedList() | ||
) { | ||
fun addCard(toInt: Int) { | ||
cardValues.add(toInt) | ||
} | ||
|
||
fun hasCards(): Boolean = cardValues.peek() != null | ||
|
||
fun turnCard(): Int = cardValues.remove() | ||
|
||
fun addCards(higherCard: Int, lowerCard: Int) { | ||
addCard(higherCard) | ||
addCard(lowerCard) | ||
} | ||
|
||
fun generateScore(): Long { | ||
return cardValues.reversed().foldIndexed(0L) { index, total, card -> | ||
val multiplier = index + 1 | ||
total + (multiplier * card) | ||
} | ||
} | ||
|
||
fun deckConfiguration(): String { | ||
return cardValues.toList().joinToString(",") | ||
} | ||
|
||
fun haveAtLeast(deckSize: Int): Boolean { | ||
return cardValues.toList().size >= deckSize | ||
} | ||
|
||
fun dropUntil(deckSize: Int): RecursiveCombatPlayer = apply { | ||
val topCards = cardValues.take(deckSize) | ||
while(cardValues.poll() != null) { /* clear current queue */ } | ||
topCards.forEach(::addCard) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/test/kotlin/me/peckb/aoc/_2020/calendar/day22/Day22Test.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package me.peckb.aoc._2020.calendar.day22 | ||
|
||
import javax.inject.Inject | ||
|
||
|
||
import me.peckb.aoc._2020.DaggerTestDayComponent | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
|
||
internal class Day22Test { | ||
@Inject | ||
lateinit var day22: Day22 | ||
|
||
@BeforeEach | ||
fun setup() { | ||
DaggerTestDayComponent.create().inject(this) | ||
} | ||
|
||
@Test | ||
fun testDay22PartOne() { | ||
assertEquals(32102, day22.partOne(DAY_22)) | ||
} | ||
|
||
@Test | ||
fun testDay22PartTwo() { | ||
assertEquals(34173, day22.partTwo(DAY_22)) | ||
} | ||
|
||
companion object { | ||
private const val DAY_22: String = "advent-of-code-input/2020/day22.input" | ||
} | ||
} |