feat(game): add GameWritebackEventDto and update related services for game state management
Build & Test (NowChessSystems) TeamCity build failed

This commit is contained in:
2026-04-29 20:52:50 +02:00
parent 91efed1370
commit 75be096c5a
23 changed files with 349 additions and 298 deletions
@@ -9,7 +9,7 @@ quarkus:
nowchess:
internal:
secret: ${INTERNAL_SECRET}
secret: 123abc
smallrye-openapi:
info-title: NowChess IO Service
info-version: 1.0.0
@@ -3,61 +3,42 @@ package de.nowchess.io.grpc
import de.nowchess.api.board.*
import de.nowchess.api.board.CastlingRights as DomainCastlingRights
import de.nowchess.api.game.{DrawReason, GameContext, GameResult, WinReason}
import de.nowchess.api.move.{Move as DomainMove, MoveType, PromotionPiece}
import de.nowchess.api.grpc.ProtoMapperBase
import de.nowchess.api.move.{Move as DomainMove, MoveType}
import de.nowchess.io.proto.*
import scala.jdk.CollectionConverters.*
object IoProtoMapper:
object IoProtoMapper extends ProtoMapperBase[ProtoColor, ProtoPieceType, ProtoMoveKind, ProtoMove, ProtoSquarePiece, java.util.List[ProtoSquarePiece], ProtoCastlingRights, ProtoGameResultKind, ProtoGameContext]:
private val (colorTo, colorFrom) = ProtoMapperBase.colorConversions(ProtoColor.WHITE, ProtoColor.BLACK)
private val (pieceTypeTo, pieceTypeFrom) = ProtoMapperBase.pieceTypeConversions(
ProtoPieceType.PAWN,
ProtoPieceType.KNIGHT,
ProtoPieceType.BISHOP,
ProtoPieceType.ROOK,
ProtoPieceType.QUEEN,
ProtoPieceType.KING,
)
private val (moveKindTo, moveKindFrom) = ProtoMapperBase.moveKindConversions(
ProtoMoveKind.QUIET,
ProtoMoveKind.CAPTURE,
ProtoMoveKind.CASTLE_KINGSIDE,
ProtoMoveKind.CASTLE_QUEENSIDE,
ProtoMoveKind.EN_PASSANT,
ProtoMoveKind.PROMO_QUEEN,
ProtoMoveKind.PROMO_ROOK,
ProtoMoveKind.PROMO_BISHOP,
ProtoMoveKind.PROMO_KNIGHT,
)
def toProtoColor(c: Color): ProtoColor = c match
case Color.White => ProtoColor.WHITE
case Color.Black => ProtoColor.BLACK
override def toProtoColor(c: Color): ProtoColor = colorTo(c)
override def fromProtoColor(c: ProtoColor): Color = colorFrom(c)
override def toProtoPieceType(pt: PieceType): ProtoPieceType = pieceTypeTo(pt)
override def fromProtoPieceType(pt: ProtoPieceType): PieceType = pieceTypeFrom(pt)
override def toProtoMoveKind(mt: MoveType): ProtoMoveKind = moveKindTo(mt)
override def fromProtoMoveKind(k: ProtoMoveKind): MoveType = moveKindFrom(k)
def fromProtoColor(c: ProtoColor): Color = c match
case ProtoColor.WHITE => Color.White
case _ => Color.Black
def toProtoPieceType(pt: PieceType): ProtoPieceType = pt match
case PieceType.Pawn => ProtoPieceType.PAWN
case PieceType.Knight => ProtoPieceType.KNIGHT
case PieceType.Bishop => ProtoPieceType.BISHOP
case PieceType.Rook => ProtoPieceType.ROOK
case PieceType.Queen => ProtoPieceType.QUEEN
case PieceType.King => ProtoPieceType.KING
def fromProtoPieceType(pt: ProtoPieceType): PieceType = pt match
case ProtoPieceType.PAWN => PieceType.Pawn
case ProtoPieceType.KNIGHT => PieceType.Knight
case ProtoPieceType.BISHOP => PieceType.Bishop
case ProtoPieceType.ROOK => PieceType.Rook
case ProtoPieceType.QUEEN => PieceType.Queen
case _ => PieceType.King
def toProtoMoveKind(mt: MoveType): ProtoMoveKind = mt match
case MoveType.Normal(false) => ProtoMoveKind.QUIET
case MoveType.Normal(true) => ProtoMoveKind.CAPTURE
case MoveType.CastleKingside => ProtoMoveKind.CASTLE_KINGSIDE
case MoveType.CastleQueenside => ProtoMoveKind.CASTLE_QUEENSIDE
case MoveType.EnPassant => ProtoMoveKind.EN_PASSANT
case MoveType.Promotion(PromotionPiece.Queen) => ProtoMoveKind.PROMO_QUEEN
case MoveType.Promotion(PromotionPiece.Rook) => ProtoMoveKind.PROMO_ROOK
case MoveType.Promotion(PromotionPiece.Bishop) => ProtoMoveKind.PROMO_BISHOP
case MoveType.Promotion(PromotionPiece.Knight) => ProtoMoveKind.PROMO_KNIGHT
def fromProtoMoveKind(k: ProtoMoveKind): MoveType = k match
case ProtoMoveKind.QUIET => MoveType.Normal(false)
case ProtoMoveKind.CAPTURE => MoveType.Normal(true)
case ProtoMoveKind.CASTLE_KINGSIDE => MoveType.CastleKingside
case ProtoMoveKind.CASTLE_QUEENSIDE => MoveType.CastleQueenside
case ProtoMoveKind.EN_PASSANT => MoveType.EnPassant
case ProtoMoveKind.PROMO_QUEEN => MoveType.Promotion(PromotionPiece.Queen)
case ProtoMoveKind.PROMO_ROOK => MoveType.Promotion(PromotionPiece.Rook)
case ProtoMoveKind.PROMO_BISHOP => MoveType.Promotion(PromotionPiece.Bishop)
case ProtoMoveKind.PROMO_KNIGHT => MoveType.Promotion(PromotionPiece.Knight)
case _ => MoveType.Normal(false)
def toProtoMove(m: DomainMove): ProtoMove =
override def toProtoMove(m: DomainMove): ProtoMove =
ProtoMove
.newBuilder()
.setFrom(m.from.toString)
@@ -65,42 +46,44 @@ object IoProtoMapper:
.setMoveKind(toProtoMoveKind(m.moveType))
.build()
def fromProtoMove(m: ProtoMove): Option[DomainMove] =
override def fromProtoMove(m: ProtoMove): Option[DomainMove] =
for
from <- Square.fromAlgebraic(m.getFrom)
to <- Square.fromAlgebraic(m.getTo)
yield DomainMove(from, to, fromProtoMoveKind(m.getMoveKind))
def toProtoBoard(board: Board): java.util.List[ProtoSquarePiece] =
board.pieces
.map { (sq, piece) =>
ProtoSquarePiece
override def toProtoSquarePiece(sq: Square, piece: Piece): ProtoSquarePiece =
ProtoSquarePiece
.newBuilder()
.setSquare(sq.toString)
.setPiece(
ProtoPiece
.newBuilder()
.setSquare(sq.toString)
.setPiece(
ProtoPiece
.newBuilder()
.setColor(toProtoColor(piece.color))
.setPieceType(toProtoPieceType(piece.pieceType))
.build(),
)
.build()
}
.setColor(toProtoColor(piece.color))
.setPieceType(toProtoPieceType(piece.pieceType))
.build(),
)
.build()
override def fromProtoSquarePiece(sp: ProtoSquarePiece): Option[(Square, Piece)] =
Square
.fromAlgebraic(sp.getSquare)
.map(_ -> Piece(fromProtoColor(sp.getPiece.getColor), fromProtoPieceType(sp.getPiece.getPieceType)))
override def toProtoBoard(board: Board): java.util.List[ProtoSquarePiece] =
board.pieces
.map { (sq, piece) => toProtoSquarePiece(sq, piece) }
.toSeq
.asJava
def fromProtoBoard(pieces: java.util.List[ProtoSquarePiece]): Board =
override def fromProtoBoard(pieces: java.util.List[ProtoSquarePiece]): Board =
Board(
pieces.asScala
.flatMap(sp =>
Square
.fromAlgebraic(sp.getSquare)
.map(_ -> Piece(fromProtoColor(sp.getPiece.getColor), fromProtoPieceType(sp.getPiece.getPieceType))),
)
.flatMap(fromProtoSquarePiece)
.toMap,
)
def toProtoResultKind(r: Option[GameResult]): ProtoGameResultKind = r match
override def toProtoResultKind(r: Option[GameResult]): ProtoGameResultKind = r match
case None => ProtoGameResultKind.ONGOING
case Some(GameResult.Win(Color.White, WinReason.Checkmate)) => ProtoGameResultKind.WIN_CHECKMATE_W
case Some(GameResult.Win(Color.Black, WinReason.Checkmate)) => ProtoGameResultKind.WIN_CHECKMATE_B
@@ -114,7 +97,7 @@ object IoProtoMapper:
case Some(GameResult.Draw(DrawReason.ThreefoldRepetition)) => ProtoGameResultKind.DRAW_THREEFOLD
case Some(GameResult.Draw(DrawReason.Agreement)) => ProtoGameResultKind.DRAW_AGREEMENT
def fromProtoResultKind(k: ProtoGameResultKind): Option[GameResult] = k match
override def fromProtoResultKind(k: ProtoGameResultKind): Option[GameResult] = k match
case ProtoGameResultKind.ONGOING => None
case ProtoGameResultKind.WIN_CHECKMATE_W => Some(GameResult.Win(Color.White, WinReason.Checkmate))
case ProtoGameResultKind.WIN_CHECKMATE_B => Some(GameResult.Win(Color.Black, WinReason.Checkmate))
@@ -129,20 +112,24 @@ object IoProtoMapper:
case ProtoGameResultKind.DRAW_AGREEMENT => Some(GameResult.Draw(DrawReason.Agreement))
case _ => None
def toProtoGameContext(ctx: GameContext): ProtoGameContext =
override def toProtoCastlingRights(cr: DomainCastlingRights): ProtoCastlingRights =
ProtoCastlingRights
.newBuilder()
.setWhiteKingSide(cr.whiteKingSide)
.setWhiteQueenSide(cr.whiteQueenSide)
.setBlackKingSide(cr.blackKingSide)
.setBlackQueenSide(cr.blackQueenSide)
.build()
override def fromProtoCastlingRights(pcr: ProtoCastlingRights): DomainCastlingRights =
DomainCastlingRights(pcr.getWhiteKingSide, pcr.getWhiteQueenSide, pcr.getBlackKingSide, pcr.getBlackQueenSide)
override def toProtoGameContext(ctx: GameContext): ProtoGameContext =
ProtoGameContext
.newBuilder()
.addAllBoard(toProtoBoard(ctx.board))
.setTurn(toProtoColor(ctx.turn))
.setCastlingRights(
ProtoCastlingRights
.newBuilder()
.setWhiteKingSide(ctx.castlingRights.whiteKingSide)
.setWhiteQueenSide(ctx.castlingRights.whiteQueenSide)
.setBlackKingSide(ctx.castlingRights.blackKingSide)
.setBlackQueenSide(ctx.castlingRights.blackQueenSide)
.build(),
)
.setCastlingRights(toProtoCastlingRights(ctx.castlingRights))
.setEnPassantSquare(ctx.enPassantSquare.map(_.toString).getOrElse(""))
.setHalfMoveClock(ctx.halfMoveClock)
.addAllMoves(ctx.moves.map(toProtoMove).asJava)
@@ -150,13 +137,11 @@ object IoProtoMapper:
.addAllInitialBoard(toProtoBoard(ctx.initialBoard))
.build()
def fromProtoGameContext(p: ProtoGameContext): GameContext =
val cr = p.getCastlingRights
override def fromProtoGameContext(p: ProtoGameContext): GameContext =
GameContext(
board = fromProtoBoard(p.getBoardList),
turn = fromProtoColor(p.getTurn),
castlingRights =
DomainCastlingRights(cr.getWhiteKingSide, cr.getWhiteQueenSide, cr.getBlackKingSide, cr.getBlackQueenSide),
castlingRights = fromProtoCastlingRights(p.getCastlingRights),
enPassantSquare = Option(p.getEnPassantSquare).filter(_.nonEmpty).flatMap(Square.fromAlgebraic),
halfMoveClock = p.getHalfMoveClock,
moves = p.getMovesList.asScala.flatMap(fromProtoMove).toList,