Create test for complete game

create notifications for reply the players
implement notifications on GameEventPlayerNotificationListener
add priority to the eventbus.subscribe()
improve JWT creation
update libs koin + ktor
remove output of GameCommandStream
improve logs
create a function disableShuffleDeck to disable the shuffle of the deck (for tests)
This commit is contained in:
2025-03-08 01:07:45 +01:00
parent 99f0760d3c
commit 51d857513c
55 changed files with 659 additions and 235 deletions

View File

@@ -10,13 +10,17 @@ import eventDemo.app.event.event.NewPlayerEvent
import eventDemo.app.event.event.PlayerChoseColorEvent
import eventDemo.app.event.event.PlayerHavePassEvent
import eventDemo.app.event.event.PlayerReadyEvent
import eventDemo.libs.event.EventStream
fun GameId.buildStateFromEventStream(eventStream: EventStream<GameEvent, GameId>): GameState =
fun GameId.buildStateFromEventStream(eventStream: GameEventStream): GameState =
buildStateFromEvents(
eventStream.readAll(this),
)
fun GameEvent.buildStateFromEventStreamTo(eventStream: GameEventStream): GameState =
gameId.buildStateFromEvents(
eventStream.readAll(gameId).takeWhile { it != this } + this,
)
private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
events.fold(GameState(this)) { state: GameState, event: GameEvent ->
when (event) {
@@ -37,12 +41,13 @@ private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
lastPlayer = event.player,
direction = direction,
lastColor = color,
lastCard = GameState.LastCard(event.card, event.player),
deck = state.deck.putOneCardFromHand(event.player, event.card),
)
}
is NewPlayerEvent -> {
if (state.isReady) error("The game is already started")
if (state.isStarted) error("The game is already started")
state.copy(
players = state.players + event.player,
@@ -56,6 +61,7 @@ private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
}
is PlayerHavePassEvent -> {
if (event.takenCard != state.deck.stack.first()) error("taken card is not ot top of the stack")
state.copy(
lastPlayer = event.player,
deck = state.deck.takeOneCardFromStackTo(event.player),
@@ -70,7 +76,7 @@ private fun GameId.buildStateFromEvents(events: List<GameEvent>): GameState =
is GameStartedEvent -> {
state.copy(
lastColor = (event.deck.discard.first() as? Card.ColorCard)?.color,
lastColor = (event.deck.discard.first() as? Card.ColorCard)?.color ?: state.lastColor,
lastCard = GameState.LastCard(event.deck.discard.first(), event.firstPlayer),
lastPlayer = event.firstPlayer,
deck = event.deck,

View File

@@ -8,7 +8,7 @@ import eventDemo.app.entity.Player
* An [GameEvent] to represent a played card.
*/
data class CardIsPlayedEvent(
override val id: GameId,
override val gameId: GameId,
val card: Card,
val player: Player,
) : GameEvent

View File

@@ -9,5 +9,5 @@ import kotlinx.serialization.Serializable
*/
@Serializable
sealed interface GameEvent : Event<GameId> {
override val id: GameId
override val gameId: GameId
}

View File

@@ -9,7 +9,7 @@ import eventDemo.app.entity.initHands
* This [GameEvent] is sent when all players are ready.
*/
data class GameStartedEvent(
override val id: GameId,
override val gameId: GameId,
val firstPlayer: Player,
val deck: Deck,
) : GameEvent {
@@ -19,9 +19,20 @@ data class GameStartedEvent(
players: Set<Player>,
): GameStartedEvent =
GameStartedEvent(
id = id,
firstPlayer = players.random(),
deck = Deck.newWithoutPlayers().initHands(players).placeFirstCardOnDiscard(),
gameId = id,
firstPlayer = if (isDisabled) players.first() else players.random(),
deck =
Deck
.newWithoutPlayers()
.let { if (isDisabled) it else it.shuffle() }
.initHands(players)
.placeFirstCardOnDiscard(),
)
}
}
private var isDisabled = false
internal fun disableShuffleDeck() {
isDisabled = true
}

View File

@@ -7,6 +7,6 @@ import eventDemo.app.entity.Player
* An [GameEvent] to represent a new player joining the game.
*/
data class NewPlayerEvent(
override val id: GameId,
override val gameId: GameId,
val player: Player,
) : GameEvent

View File

@@ -8,7 +8,7 @@ import eventDemo.app.entity.Player
* This [GameEvent] is sent when a player chose a color.
*/
data class PlayerChoseColorEvent(
override val id: GameId,
override val gameId: GameId,
val player: Player,
val color: Card.Color,
) : GameEvent

View File

@@ -1,5 +1,6 @@
package eventDemo.app.event.event
import eventDemo.app.entity.Card
import eventDemo.app.entity.GameId
import eventDemo.app.entity.Player
@@ -7,6 +8,7 @@ import eventDemo.app.entity.Player
* This [GameEvent] is sent when a player can play.
*/
data class PlayerHavePassEvent(
override val id: GameId,
override val gameId: GameId,
val player: Player,
val takenCard: Card,
) : GameEvent

View File

@@ -7,6 +7,6 @@ import eventDemo.app.entity.Player
* This [GameEvent] is sent when a player is ready.
*/
data class PlayerReadyEvent(
override val id: GameId,
override val gameId: GameId,
val player: Player,
) : GameEvent