Advent of Code 2021 day 21
12/21/2021, 5:00 AMDavid Whittaker
12/21/2021, 5:38 AMelizarov
12/21/2021, 5:39 AMDavid Whittaker
12/21/2021, 5:42 AMJintin
12/21/2021, 5:49 AMDavid Whittaker
12/21/2021, 6:01 AMephemient
12/21/2021, 6:30 AMMichael Böiers
12/21/2021, 6:48 AMMichael Böiers
12/21/2021, 6:49 AMgroupingBy(…).fold
…elizarov
12/21/2021, 7:08 AMEndre Deak
12/21/2021, 8:27 AMephemient
12/21/2021, 8:34 AMfun score(player1: Int, player2: Int, score1: Int, score2: Int)
returns (x, y)
where x
is the number of universes in which player 1 wins and y
is the number of universes in which player 2 wins, starting from the given state. object Score
is exactly that, but with caching of recursive calls; because player1 in 1..10
, player2 in 1..10
, score1 in 0..20
, and score2 in 0..20
, a flat array of 44100 can store everythingMichael Böiers
12/21/2021, 8:54 AMelizarov
12/21/2021, 8:54 AMMichael Böiers
12/21/2021, 8:55 AMMichael Böiers
12/21/2021, 8:56 AMJintin
12/21/2021, 9:03 AMMichael Böiers
12/21/2021, 9:07 AMephemient
12/21/2021, 9:10 AMephemient
12/21/2021, 9:11 AMJintin
12/21/2021, 9:13 AMJintin
12/21/2021, 9:14 AMMichael Böiers
12/21/2021, 9:19 AMDan Fingal-Surma
12/21/2021, 9:25 AMMichael de Kaste
12/21/2021, 9:26 AMDan Fingal-Surma
12/21/2021, 9:28 AMDan Fingal-Surma
12/21/2021, 9:28 AMMichael de Kaste
12/21/2021, 3:32 PMMichael de Kaste
12/21/2021, 3:32 PMDavid Whittaker
12/21/2021, 5:56 PMdata class gameOutcome(val player: Int, val score0: Long, val score1: Long, val pos0: Int, val pos1: Int)
private val cache = mutableMapOf<gameOutcome, Pair<Long, Long>>()
Dan Fingal-Surma
12/21/2021, 6:06 PMMarcin Wisniowski
12/21/2021, 6:43 PMMichael Böiers
12/21/2021, 7:13 PMNir
12/21/2021, 7:22 PMNir
12/21/2021, 7:23 PMMichael Böiers
12/21/2021, 7:30 PMNir
12/21/2021, 7:30 PMNir
12/21/2021, 7:30 PMNir
12/21/2021, 7:30 PMNir
12/21/2021, 7:31 PMclass DeterministicDice {
private var value = 1
fun roll() = value.also { ++value; if (value > 100) value = 1 }
}
data class Player(val position: Int, val score: Int = 0)
fun Player.forward(steps: Int): Player {
val newPosition = (position + steps - 1) % 10 + 1
return Player(newPosition, score + newPosition)
}
data class GameState(val players: List<Player>, val curPlayer: Int = 0)
fun GameState.forward(steps: Int): GameState {
val newPlayers = players.toMutableList()
newPlayers[curPlayer] = players[curPlayer].forward(steps)
return GameState(newPlayers, (curPlayer + 1) % 2)
}
defining nice classes for the abstractions in the problem probably costs more lines than it saves, but IMHO it breaks apart the complexity better and makes the whole thing take less time to readNir
12/21/2021, 7:33 PMMichael Böiers
12/21/2021, 7:44 PMMichael Böiers
12/21/2021, 7:47 PMMichael Böiers
12/21/2021, 7:49 PMfun main(vararg args: String) = listOf(args[0].toInt(), args[1].toInt()).map { it - 1 }.let { (start1, start2) ->
println(part1(start1, start2, die = generateSequence(1) { if (it == 100) 1 else it + 1 }.iterator()))
println(part2(start1, start2))
}
fun part1(p1: Int, p2: Int, s1: Int = 0, s2: Int = 0, turn: Int = 0, die: Iterator<Int>): Int =
if (s1 >= 1000 || s2 >= 1000) (if (s1 > s2) s2 else s1) * turn * 3
else ((p1 + die.next() + die.next() + die.next()) % 10)
.let { newP1 -> part1(p1 = p2, p2 = newP1, s1 = s2, s2 = s1 + newP1 + 1, turn + 1, die) }
fun part2(start1: Int, start2: Int): Long {
val die = listOf(1, 2, 3).run { flatMap { a -> flatMap { b -> map { c -> a + b + c } } } }
val cache = mutableMapOf<List<Int>, Pair<Long, Long>>()
fun play(p1: Int, p2: Int, s1: Int, s2: Int, turn: Int): Pair<Long, Long> =
listOf(p1, p2, s1, s2, turn % 2).let { k ->
cache[k]?.let { return it }
if (s1 >= 21 || s2 >= 21) {
val won = turn % 2 == if (s1 >= 21) 1 else 0
if (won) (1L to 0L) else (0L to 1L)
} else die.map { ((p1 + it) % 10) }
.map {play(p1 = p2, p2 = it, s1 = s2, s2 = s1 + it + 1, turn + 1)}
.fold(0L to 0L) { (allWon, allLost), (won, lost) ->allWon + won to allLost + lost}
.also { cache[k] = it }
}
return play(start1, start2, 0, 0, turn = 0).toList().maxOf { it }
}
Michael Böiers
12/21/2021, 7:50 PMNir
12/21/2021, 7:59 PMNir
12/21/2021, 7:59 PMMarcin Wisniowski
12/21/2021, 8:03 PMphldavies
12/21/2021, 8:33 PMMichael Böiers
12/21/2021, 8:46 PMMichael Böiers
12/21/2021, 8:47 PMNir
12/21/2021, 8:51 PMNir
12/21/2021, 8:51 PMNir
12/21/2021, 8:52 PMMichael Böiers
12/21/2021, 8:52 PMMichael Böiers
12/21/2021, 8:52 PMNir
12/21/2021, 8:54 PMMichael Böiers
12/21/2021, 8:55 PMNir
12/21/2021, 8:55 PMNir
12/21/2021, 8:56 PMfor i, j, k in product(range(1,3), repeat=3))
Nir
12/21/2021, 8:56 PMNir
12/21/2021, 8:56 PMMichael Böiers
12/21/2021, 9:01 PMfun State.play(): Score = cache[this] ?: when { /* compute the result */ }.also { cache[this] = it }
Nir
12/21/2021, 9:06 PMwhen
thereNir
12/21/2021, 9:07 PMNir
12/21/2021, 9:07 PMNir
12/21/2021, 9:08 PMNir
12/21/2021, 9:08 PM?:
Nir
12/21/2021, 9:08 PMcache.getOrPut(this) { when { ... } }
Nir
12/21/2021, 9:08 PMDan Fingal-Surma
12/21/2021, 9:10 PMgetOrPut
is def what you wantMichael Böiers
12/21/2021, 9:12 PMMichael Böiers
12/21/2021, 9:30 PMfun State.play(): Score = when {
isGameOver(minScore = 21) -> if (doesPlayerLead) Score(playerWon = 1L) else Score(playerLost = 1L)
else -> cache.getOrPut(this) { dieCasts.map(::withCast).map(State::play).reduce(Score::plus) }
}
todd.ginsberg
12/21/2021, 9:36 PMMichael Böiers
12/21/2021, 9:44 PMfun main() {
val input = listOf(2, 5)
val positions = input.map { it - 1 }.toMutableList()
val scores = mutableListOf(0, 0)
var player = 0
val die = generateSequence(1) { if (it == 100) 1 else it + 1 }.iterator()
var turn = 1
while (true) {
val nextPosition = (positions[player] + (die.next() + die.next() + die.next())) % 10
positions[player] = nextPosition
scores[player] += nextPosition + 1
if (scores[player] >= 1000) break
player = (player + 1) % 2
turn++
}
println(scores[(player + 1) % 2] * turn * 3)
}
todd.ginsberg
12/21/2021, 9:48 PMMichael Böiers
12/21/2021, 9:51 PMephemient
12/21/2021, 10:16 PMtodd.ginsberg
12/21/2021, 10:32 PMDan Fingal-Surma
12/21/2021, 10:36 PMDan Fingal-Surma
12/21/2021, 10:39 PM#!/usr/bin/env kotlin
val lines = java.io.File(args[0]).readLines()
fun Iterator<Int>.roll() = (0..2).sumOf { next() }
val positions = lines.map { it.split(": ").mapNotNull { it.toIntOrNull() }.single() - 1 }.toMutableList()
val scores = mutableListOf(0, 0)
val dice = generateSequence(1) { 1 + (it % 100) }.iterator()
var turnCount = 0
while (true) {
turnCount++
val roll0 = dice.roll()
positions[0] = (positions[0] + roll0) % 10
scores[0] += positions[0] + 1
if (scores[0] >= 1000) break
turnCount++
val roll1 = dice.roll()
positions[1] = (positions[1] + roll1) % 10
scores[1] += positions[1] + 1
if (scores[1] >= 1000) break
}
println(scores.minOf { it} * (turnCount * 3))