feat(coordinator): add Redis integration and improve configuration for game state management
Build & Test (NowChessSystems) TeamCity build was removed from queue

This commit is contained in:
2026-04-26 18:25:03 +02:00
parent f327441089
commit 106b4d3b7e
56 changed files with 1072 additions and 1139 deletions
+1 -1
View File
@@ -116,7 +116,7 @@ tasks.jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks.withType(org.gradle.api.tasks.scala.ScalaCompile::class).configureEach {
tasks.withType(ScalaCompile::class).configureEach {
if (name == "compileScoverageScala") {
source = source.asFileTree.matching {
exclude("**/grpc/*.scala")
@@ -2,7 +2,7 @@ package de.nowchess.rules.grpc
import de.nowchess.api.board.{CastlingRights as DomainCastlingRights, *}
import de.nowchess.api.game.{DrawReason, GameContext, GameResult, WinReason}
import de.nowchess.api.move.{MoveType, PromotionPiece, Move as DomainMove}
import de.nowchess.api.move.{Move as DomainMove, MoveType, PromotionPiece}
import de.nowchess.rules.proto.*
import scala.jdk.CollectionConverters.*
@@ -57,7 +57,12 @@ object ProtoMapper:
case _ => MoveType.Normal(false)
def toProtoMove(m: DomainMove): ProtoMove =
ProtoMove.newBuilder().setFrom(m.from.toString).setTo(m.to.toString).setMoveKind(toProtoMoveKind(m.moveType)).build()
ProtoMove
.newBuilder()
.setFrom(m.from.toString)
.setTo(m.to.toString)
.setMoveKind(toProtoMoveKind(m.moveType))
.build()
def fromProtoMove(m: ProtoMove): Option[DomainMove] =
for
@@ -66,16 +71,33 @@ object ProtoMapper:
yield DomainMove(from, to, fromProtoMoveKind(m.getMoveKind))
def toProtoBoard(board: Board): java.util.List[ProtoSquarePiece] =
board.pieces.map { (sq, piece) =>
ProtoSquarePiece
.newBuilder()
.setSquare(sq.toString)
.setPiece(ProtoPiece.newBuilder().setColor(toProtoColor(piece.color)).setPieceType(toProtoPieceType(piece.pieceType)).build())
.build()
}.toSeq.asJava
board.pieces
.map { (sq, piece) =>
ProtoSquarePiece
.newBuilder()
.setSquare(sq.toString)
.setPiece(
ProtoPiece
.newBuilder()
.setColor(toProtoColor(piece.color))
.setPieceType(toProtoPieceType(piece.pieceType))
.build(),
)
.build()
}
.toSeq
.asJava
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)))).toMap)
Board(
pieces.asScala
.flatMap(sp =>
Square
.fromAlgebraic(sp.getSquare)
.map(_ -> Piece(fromProtoColor(sp.getPiece.getColor), fromProtoPieceType(sp.getPiece.getPieceType))),
)
.toMap,
)
def toProtoResultKind(r: Option[GameResult]): ProtoGameResultKind = r match
case None => ProtoGameResultKind.ONGOING
@@ -130,12 +152,13 @@ object ProtoMapper:
def fromProtoGameContext(p: ProtoGameContext): GameContext =
val cr = p.getCastlingRights
GameContext(
board = fromProtoBoard(p.getBoardList),
turn = fromProtoColor(p.getTurn),
castlingRights = DomainCastlingRights(cr.getWhiteKingSide, cr.getWhiteQueenSide, cr.getBlackKingSide, cr.getBlackQueenSide),
board = fromProtoBoard(p.getBoardList),
turn = fromProtoColor(p.getTurn),
castlingRights =
DomainCastlingRights(cr.getWhiteKingSide, cr.getWhiteQueenSide, cr.getBlackKingSide, cr.getBlackQueenSide),
enPassantSquare = Option(p.getEnPassantSquare).filter(_.nonEmpty).flatMap(Square.fromAlgebraic),
halfMoveClock = p.getHalfMoveClock,
moves = p.getMovesList.asScala.flatMap(fromProtoMove).toList,
result = fromProtoResultKind(p.getResult),
initialBoard = fromProtoBoard(p.getInitialBoardList),
halfMoveClock = p.getHalfMoveClock,
moves = p.getMovesList.asScala.flatMap(fromProtoMove).toList,
result = fromProtoResultKind(p.getResult),
initialBoard = fromProtoBoard(p.getInitialBoardList),
)
@@ -12,15 +12,22 @@ import io.quarkus.grpc.GrpcService
class RuleGrpcService extends RuleServiceGrpc.RuleServiceImplBase:
private def parseSquare(s: String): Square =
Square.fromAlgebraic(s).getOrElse(
throw Status.INVALID_ARGUMENT.withDescription(s"Invalid square: $s").asRuntimeException(),
)
Square
.fromAlgebraic(s)
.getOrElse(
throw Status.INVALID_ARGUMENT.withDescription(s"Invalid square: $s").asRuntimeException(),
)
override def candidateMoves(req: ProtoSquareRequest, resp: StreamObserver[ProtoMoveList]): Unit =
val ctx = ProtoMapper.fromProtoGameContext(req.getContext)
val sq = parseSquare(req.getSquare)
val moves = DefaultRules.candidateMoves(ctx)(sq)
resp.onNext(ProtoMoveList.newBuilder().addAllMoves(moves.map(ProtoMapper.toProtoMove).asInstanceOf[java.util.List[ProtoMove]]).build())
resp.onNext(
ProtoMoveList
.newBuilder()
.addAllMoves(moves.map(ProtoMapper.toProtoMove).asInstanceOf[java.util.List[ProtoMove]])
.build(),
)
resp.onCompleted()
override def legalMoves(req: ProtoSquareRequest, resp: StreamObserver[ProtoMoveList]): Unit =
@@ -52,10 +59,12 @@ class RuleGrpcService extends RuleServiceGrpc.RuleServiceImplBase:
respond(resp, boolResult(DefaultRules.isThreefoldRepetition(ProtoMapper.fromProtoGameContext(req))))
override def applyMove(req: ProtoMoveRequest, resp: StreamObserver[ProtoGameContext]): Unit =
val ctx = ProtoMapper.fromProtoGameContext(req.getContext)
val move = ProtoMapper.fromProtoMove(req.getMove).getOrElse(
throw Status.INVALID_ARGUMENT.withDescription("Invalid move").asRuntimeException(),
)
val ctx = ProtoMapper.fromProtoGameContext(req.getContext)
val move = ProtoMapper
.fromProtoMove(req.getMove)
.getOrElse(
throw Status.INVALID_ARGUMENT.withDescription("Invalid move").asRuntimeException(),
)
respond(resp, ProtoMapper.toProtoGameContext(DefaultRules.applyMove(ctx)(move)))
override def postMoveStatus(req: ProtoGameContext, resp: StreamObserver[ProtoPostMoveStatus]): Unit =