refactor: NCS-22 NCS-23 reworked modules and tests #17
+6
-4
@@ -1,13 +1,13 @@
|
||||
package de.nowchess.chess.logic
|
||||
package de.nowchess.api.game
|
||||
|
||||
import de.nowchess.api.board.{PieceType, Square}
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.api.move.{Move, PromotionPiece}
|
||||
|
||||
/** A single move recorded in the game history. Distinct from api.move.Move which represents user intent. */
|
||||
case class HistoryMove(
|
||||
from: Square,
|
||||
to: Square,
|
||||
castleSide: Option[CastleSide],
|
||||
castleSide: Option[String] = None,
|
||||
promotionPiece: Option[PromotionPiece] = None,
|
||||
pieceType: PieceType = PieceType.Pawn,
|
||||
isCapture: Boolean = false
|
||||
@@ -17,6 +17,8 @@ case class HistoryMove(
|
||||
*
|
||||
* @param moves moves played so far, oldest first
|
||||
* @param halfMoveClock plies since the last pawn move or capture (FIDE 50-move rule counter)
|
||||
*
|
||||
* Deprecated: Use GameContext instead. This exists for compatibility during migration.
|
||||
*/
|
||||
case class GameHistory(moves: List[HistoryMove] = List.empty, halfMoveClock: Int = 0):
|
||||
|
||||
@@ -36,7 +38,7 @@ case class GameHistory(moves: List[HistoryMove] = List.empty, halfMoveClock: Int
|
||||
def addMove(
|
||||
from: Square,
|
||||
to: Square,
|
||||
castleSide: Option[CastleSide] = None,
|
||||
castleSide: Option[String] = None,
|
||||
promotionPiece: Option[PromotionPiece] = None,
|
||||
wasPawnMove: Boolean = false,
|
||||
wasCapture: Boolean = false,
|
||||
@@ -1,7 +1,7 @@
|
||||
package de.nowchess.chess.command
|
||||
|
||||
import de.nowchess.api.board.{Square, Board, Color, Piece}
|
||||
import de.nowchess.chess.logic.GameHistory
|
||||
import de.nowchess.api.game.GameHistory
|
||||
|
||||
/** Marker trait for all commands that can be executed and undone.
|
||||
* Commands encapsulate user actions and game state transitions.
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.nowchess.chess.controller
|
||||
|
||||
import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.api.game.GameHistory
|
||||
import de.nowchess.chess.logic.*
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -83,6 +84,7 @@ object GameController:
|
||||
|
||||
private def applyNormalMove(board: Board, history: GameHistory, turn: Color, from: Square, to: Square): MoveResult =
|
||||
val castleOpt = Option.when(MoveValidator.isCastle(board, from, to))(MoveValidator.castleSide(from, to))
|
||||
val castleOptStr = castleOpt.map(_.toString) // Convert CastleSide to String
|
||||
val isEP = EnPassantCalculator.isEnPassant(board, history, from, to)
|
||||
val (newBoard, captured) = castleOpt match
|
||||
case Some(side) => (board.withCastle(turn, side), None)
|
||||
@@ -95,7 +97,7 @@ object GameController:
|
||||
val pieceType = board.pieceAt(from).map(_.pieceType).getOrElse(PieceType.Pawn)
|
||||
val wasPawnMove = pieceType == PieceType.Pawn
|
||||
val wasCapture = captured.isDefined
|
||||
val newHistory = history.addMove(from, to, castleOpt, wasPawnMove = wasPawnMove, wasCapture = wasCapture, pieceType = pieceType)
|
||||
val newHistory = history.addMove(from, to, castleOptStr, wasPawnMove = wasPawnMove, wasCapture = wasCapture, pieceType = pieceType)
|
||||
toMoveResult(newBoard, newHistory, captured, turn)
|
||||
|
||||
private def toMoveResult(newBoard: Board, newHistory: GameHistory, captured: Option[Piece], turn: Color): MoveResult =
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.nowchess.chess.engine
|
||||
|
||||
import de.nowchess.api.board.{Board, Color, Piece, Square}
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.chess.logic.{GameHistory, GameRules, PositionStatus}
|
||||
import de.nowchess.api.board.{Board, Color, Piece, Square, CastlingRights}
|
||||
import de.nowchess.api.move.{PromotionPiece, Move}
|
||||
import de.nowchess.api.game.{GameHistory, GameContext}
|
||||
import de.nowchess.chess.controller.{GameController, Parser, MoveResult}
|
||||
import de.nowchess.chess.observer.*
|
||||
import de.nowchess.chess.command.{CommandInvoker, MoveCommand}
|
||||
@@ -43,6 +43,16 @@ class GameEngine(
|
||||
def history: GameHistory = synchronized { currentHistory }
|
||||
def turn: Color = synchronized { currentTurn }
|
||||
|
||||
/** Create a GameContext from current state (for event creation). */
|
||||
private def currentContext: GameContext = GameContext(
|
||||
board = currentBoard,
|
||||
turn = currentTurn,
|
||||
castlingRights = CastlingRights.Initial, // TODO: derive from history
|
||||
enPassantSquare = None, // TODO: derive from history
|
||||
halfMoveClock = currentHistory.halfMoveClock,
|
||||
moves = List.empty // TODO: convert history moves to api.move.Move
|
||||
)
|
||||
|
||||
/** Check if undo is available. */
|
||||
def canUndo: Boolean = synchronized { invoker.canUndo }
|
||||
|
||||
@@ -74,18 +84,16 @@ class GameEngine(
|
||||
currentHistory = GameHistory.empty
|
||||
currentTurn = Color.White
|
||||
invoker.clear()
|
||||
notifyObservers(DrawClaimedEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(DrawClaimedEvent(currentContext))
|
||||
else
|
||||
notifyObservers(InvalidMoveEvent(
|
||||
currentBoard, currentHistory, currentTurn,
|
||||
currentContext,
|
||||
"Draw cannot be claimed: the 50-move rule has not been triggered."
|
||||
))
|
||||
|
||||
case "" =>
|
||||
val event = InvalidMoveEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
currentTurn,
|
||||
currentContext,
|
||||
"Please enter a valid move or command."
|
||||
)
|
||||
notifyObservers(event)
|
||||
@@ -94,7 +102,7 @@ class GameEngine(
|
||||
Parser.parseMove(moveInput) match
|
||||
case None =>
|
||||
notifyObservers(InvalidMoveEvent(
|
||||
currentBoard, currentHistory, currentTurn,
|
||||
currentContext,
|
||||
s"Invalid move format '$moveInput'. Use coordinate notation, e.g. e2e4."
|
||||
))
|
||||
case Some((from, to)) =>
|
||||
@@ -119,16 +127,16 @@ class GameEngine(
|
||||
updateGameState(newBoard, newHistory, newTurn)
|
||||
emitMoveEvent(from.toString, to.toString, captured, newTurn)
|
||||
if currentHistory.halfMoveClock >= 100 then
|
||||
notifyObservers(FiftyMoveRuleAvailableEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(FiftyMoveRuleAvailableEvent(currentContext))
|
||||
|
||||
case MoveResult.MovedInCheck(newBoard, newHistory, captured, newTurn) =>
|
||||
val updatedCmd = cmd.copy(moveResult = Some(de.nowchess.chess.command.MoveResult.Successful(newBoard, newHistory, newTurn, captured)))
|
||||
invoker.execute(updatedCmd)
|
||||
updateGameState(newBoard, newHistory, newTurn)
|
||||
emitMoveEvent(from.toString, to.toString, captured, newTurn)
|
||||
notifyObservers(CheckDetectedEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(CheckDetectedEvent(currentContext))
|
||||
if currentHistory.halfMoveClock >= 100 then
|
||||
notifyObservers(FiftyMoveRuleAvailableEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(FiftyMoveRuleAvailableEvent(currentContext))
|
||||
|
||||
case MoveResult.Checkmate(winner) =>
|
||||
val updatedCmd = cmd.copy(moveResult = Some(de.nowchess.chess.command.MoveResult.Successful(Board.initial, GameHistory.empty, Color.White, None)))
|
||||
@@ -136,7 +144,7 @@ class GameEngine(
|
||||
currentBoard = Board.initial
|
||||
currentHistory = GameHistory.empty
|
||||
currentTurn = Color.White
|
||||
notifyObservers(CheckmateEvent(currentBoard, currentHistory, currentTurn, winner))
|
||||
notifyObservers(CheckmateEvent(currentContext, winner))
|
||||
|
||||
case MoveResult.Stalemate =>
|
||||
val updatedCmd = cmd.copy(moveResult = Some(de.nowchess.chess.command.MoveResult.Successful(Board.initial, GameHistory.empty, Color.White, None)))
|
||||
@@ -144,11 +152,11 @@ class GameEngine(
|
||||
currentBoard = Board.initial
|
||||
currentHistory = GameHistory.empty
|
||||
currentTurn = Color.White
|
||||
notifyObservers(StalemateEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(StalemateEvent(currentContext))
|
||||
|
||||
case MoveResult.PromotionRequired(promFrom, promTo, boardBefore, histBefore, _, promotingTurn) =>
|
||||
pendingPromotion = Some(PendingPromotion(promFrom, promTo, boardBefore, histBefore, promotingTurn))
|
||||
notifyObservers(PromotionRequiredEvent(currentBoard, currentHistory, currentTurn, promFrom, promTo))
|
||||
notifyObservers(PromotionRequiredEvent(currentContext, promFrom, promTo))
|
||||
|
||||
/** Undo the last move. */
|
||||
def undo(): Unit = synchronized {
|
||||
@@ -166,7 +174,7 @@ class GameEngine(
|
||||
def completePromotion(piece: PromotionPiece): Unit = synchronized {
|
||||
pendingPromotion match
|
||||
case None =>
|
||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "No promotion pending."))
|
||||
notifyObservers(InvalidMoveEvent(currentContext, "No promotion pending."))
|
||||
case Some(pending) =>
|
||||
pendingPromotion = None
|
||||
val cmd = MoveCommand(
|
||||
@@ -191,7 +199,7 @@ class GameEngine(
|
||||
invoker.execute(updatedCmd)
|
||||
updateGameState(newBoard, newHistory, newTurn)
|
||||
emitMoveEvent(pending.from.toString, pending.to.toString, captured, newTurn)
|
||||
notifyObservers(CheckDetectedEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(CheckDetectedEvent(currentContext))
|
||||
|
||||
case MoveResult.Checkmate(winner) =>
|
||||
val updatedCmd = cmd.copy(moveResult = Some(de.nowchess.chess.command.MoveResult.Successful(Board.initial, GameHistory.empty, Color.White, None)))
|
||||
@@ -199,7 +207,7 @@ class GameEngine(
|
||||
currentBoard = Board.initial
|
||||
currentHistory = GameHistory.empty
|
||||
currentTurn = Color.White
|
||||
notifyObservers(CheckmateEvent(currentBoard, currentHistory, currentTurn, winner))
|
||||
notifyObservers(CheckmateEvent(currentContext, winner))
|
||||
|
||||
case MoveResult.Stalemate =>
|
||||
val updatedCmd = cmd.copy(moveResult = Some(de.nowchess.chess.command.MoveResult.Successful(Board.initial, GameHistory.empty, Color.White, None)))
|
||||
@@ -207,10 +215,10 @@ class GameEngine(
|
||||
currentBoard = Board.initial
|
||||
currentHistory = GameHistory.empty
|
||||
currentTurn = Color.White
|
||||
notifyObservers(StalemateEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(StalemateEvent(currentContext))
|
||||
|
||||
case _ =>
|
||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Error completing promotion."))
|
||||
notifyObservers(InvalidMoveEvent(currentContext, "Error completing promotion."))
|
||||
}
|
||||
|
||||
/** Validate and load a PGN string.
|
||||
@@ -253,7 +261,7 @@ class GameEngine(
|
||||
currentTurn = initialTurnBeforeLoad
|
||||
Left(err)
|
||||
case None =>
|
||||
notifyObservers(PgnLoadedEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(PgnLoadedEvent(currentContext))
|
||||
Right(())
|
||||
}
|
||||
|
||||
@@ -264,7 +272,7 @@ class GameEngine(
|
||||
currentTurn = turn
|
||||
pendingPromotion = None
|
||||
invoker.clear()
|
||||
notifyObservers(BoardResetEvent(currentBoard, currentHistory, currentTurn))
|
||||
notifyObservers(BoardResetEvent(currentContext))
|
||||
}
|
||||
|
||||
/** Reset the board to initial position. */
|
||||
@@ -274,9 +282,7 @@ class GameEngine(
|
||||
currentTurn = Color.White
|
||||
invoker.clear()
|
||||
notifyObservers(BoardResetEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
currentTurn
|
||||
currentContext
|
||||
))
|
||||
}
|
||||
|
||||
@@ -292,9 +298,9 @@ class GameEngine(
|
||||
moveCmd.previousHistory.foreach(currentHistory = _)
|
||||
moveCmd.previousTurn.foreach(currentTurn = _)
|
||||
invoker.undo()
|
||||
notifyObservers(MoveUndoneEvent(currentBoard, currentHistory, currentTurn, notation))
|
||||
notifyObservers(MoveUndoneEvent(currentContext, notation))
|
||||
else
|
||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to undo."))
|
||||
notifyObservers(InvalidMoveEvent(currentContext, "Nothing to undo."))
|
||||
|
||||
private def performRedo(): Unit =
|
||||
if invoker.canRedo then
|
||||
@@ -306,9 +312,9 @@ class GameEngine(
|
||||
invoker.redo()
|
||||
val notation = nh.moves.lastOption.map(PgnExporter.moveToAlgebraic).getOrElse("")
|
||||
val capturedDesc = cap.map(c => s"${c.color.label} ${c.pieceType.label}")
|
||||
notifyObservers(MoveRedoneEvent(currentBoard, currentHistory, currentTurn, notation, moveCmd.from.toString, moveCmd.to.toString, capturedDesc))
|
||||
notifyObservers(MoveRedoneEvent(currentContext, notation, moveCmd.from.toString, moveCmd.to.toString, capturedDesc))
|
||||
else
|
||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to redo."))
|
||||
notifyObservers(InvalidMoveEvent(currentContext, "Nothing to redo."))
|
||||
|
||||
private def updateGameState(newBoard: Board, newHistory: GameHistory, newTurn: Color): Unit =
|
||||
currentBoard = newBoard
|
||||
@@ -318,9 +324,7 @@ class GameEngine(
|
||||
private def emitMoveEvent(fromSq: String, toSq: String, captured: Option[Piece], newTurn: Color): Unit =
|
||||
val capturedDesc = captured.map(c => s"${c.color.label} ${c.pieceType.label}")
|
||||
notifyObservers(MoveExecutedEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
newTurn,
|
||||
currentContext,
|
||||
fromSq,
|
||||
toSq,
|
||||
capturedDesc
|
||||
@@ -330,23 +334,17 @@ class GameEngine(
|
||||
(GameController.processMove(currentBoard, currentHistory, currentTurn, moveInput): @unchecked) match
|
||||
case MoveResult.NoPiece =>
|
||||
notifyObservers(InvalidMoveEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
currentTurn,
|
||||
currentContext,
|
||||
"No piece on that square."
|
||||
))
|
||||
case MoveResult.WrongColor =>
|
||||
notifyObservers(InvalidMoveEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
currentTurn,
|
||||
currentContext,
|
||||
"That is not your piece."
|
||||
))
|
||||
case MoveResult.IllegalMove =>
|
||||
notifyObservers(InvalidMoveEvent(
|
||||
currentBoard,
|
||||
currentHistory,
|
||||
currentTurn,
|
||||
currentContext,
|
||||
"Illegal move."
|
||||
))
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package de.nowchess.chess.logic
|
||||
|
||||
import de.nowchess.api.board.{Color, File, Rank, Square}
|
||||
import de.nowchess.api.game.CastlingRights
|
||||
import de.nowchess.api.game.{CastlingRights, GameHistory}
|
||||
|
||||
/** Derives castling rights from move history. */
|
||||
object CastlingRightsCalculator:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.nowchess.chess.logic
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import de.nowchess.api.game.GameHistory
|
||||
|
||||
object EnPassantCalculator:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package de.nowchess.chess.logic
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import de.nowchess.chess.logic.GameHistory
|
||||
import de.nowchess.api.game.GameHistory
|
||||
|
||||
enum PositionStatus:
|
||||
case Normal, InCheck, Mated, Drawn
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package de.nowchess.chess.logic
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import de.nowchess.chess.logic.{CastleSide, GameHistory}
|
||||
import de.nowchess.chess.logic.CastleSide
|
||||
import de.nowchess.api.game.GameHistory
|
||||
|
||||
object MoveValidator:
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ package de.nowchess.chess.notation
|
||||
|
||||
import de.nowchess.api.board.{PieceType, *}
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.chess.logic.{CastleSide, GameHistory, HistoryMove}
|
||||
import de.nowchess.api.game.{GameHistory, HistoryMove}
|
||||
import de.nowchess.chess.logic.CastleSide
|
||||
|
||||
object PgnExporter:
|
||||
|
||||
@@ -32,9 +33,9 @@ object PgnExporter:
|
||||
/** Convert a HistoryMove to Standard Algebraic Notation. */
|
||||
def moveToAlgebraic(move: HistoryMove): String =
|
||||
move.castleSide match
|
||||
case Some(CastleSide.Kingside) => "O-O"
|
||||
case Some(CastleSide.Queenside) => "O-O-O"
|
||||
case None =>
|
||||
case Some("Kingside") => "O-O"
|
||||
case Some("Queenside") => "O-O-O"
|
||||
case Some(_) | None =>
|
||||
val dest = move.to.toString
|
||||
val capStr = if move.isCapture then "x" else ""
|
||||
val promSuffix = move.promotionPiece match
|
||||
|
||||
@@ -2,7 +2,8 @@ package de.nowchess.chess.notation
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.chess.logic.{CastleSide, GameHistory, HistoryMove, GameRules, MoveValidator, withCastle}
|
||||
import de.nowchess.api.game.{GameHistory, HistoryMove}
|
||||
import de.nowchess.chess.logic.{CastleSide, GameRules, MoveValidator, withCastle}
|
||||
|
||||
/** A parsed PGN game containing headers and the resolved move list. */
|
||||
case class PgnGame(
|
||||
@@ -63,8 +64,9 @@ object PgnParser:
|
||||
/** Apply a single HistoryMove to a Board, handling castling and promotion. */
|
||||
private def applyMoveToBoard(board: Board, move: HistoryMove, color: Color): Board =
|
||||
move.castleSide match
|
||||
case Some(side) => board.withCastle(color, side)
|
||||
case None =>
|
||||
case Some("Kingside") => board.withCastle(color, CastleSide.Kingside)
|
||||
case Some("Queenside") => board.withCastle(color, CastleSide.Queenside)
|
||||
case _ =>
|
||||
val (boardAfterMove, _) = board.withMove(move.from, move.to)
|
||||
move.promotionPiece match
|
||||
case Some(pp) =>
|
||||
@@ -89,11 +91,11 @@ object PgnParser:
|
||||
notation match
|
||||
case "O-O" | "O-O+" | "O-O#" =>
|
||||
val rank = if color == Color.White then Rank.R1 else Rank.R8
|
||||
Some(HistoryMove(Square(File.E, rank), Square(File.G, rank), Some(CastleSide.Kingside), pieceType = PieceType.King))
|
||||
Some(HistoryMove(Square(File.E, rank), Square(File.G, rank), Some("Kingside"), pieceType = PieceType.King))
|
||||
|
||||
case "O-O-O" | "O-O-O+" | "O-O-O#" =>
|
||||
val rank = if color == Color.White then Rank.R1 else Rank.R8
|
||||
Some(HistoryMove(Square(File.E, rank), Square(File.C, rank), Some(CastleSide.Queenside), pieceType = PieceType.King))
|
||||
Some(HistoryMove(Square(File.E, rank), Square(File.C, rank), Some("Queenside"), pieceType = PieceType.King))
|
||||
|
||||
case _ =>
|
||||
parseRegularMove(notation, board, history, color)
|
||||
@@ -212,12 +214,12 @@ object PgnParser:
|
||||
case "O-O" | "O-O+" | "O-O#" =>
|
||||
val dest = Square(File.G, rank)
|
||||
Option.when(MoveValidator.castlingTargets(board, history, color).contains(dest))(
|
||||
HistoryMove(Square(File.E, rank), dest, Some(CastleSide.Kingside), pieceType = PieceType.King)
|
||||
HistoryMove(Square(File.E, rank), dest, Some("Kingside"), pieceType = PieceType.King)
|
||||
)
|
||||
case "O-O-O" | "O-O-O+" | "O-O-O#" =>
|
||||
val dest = Square(File.C, rank)
|
||||
Option.when(MoveValidator.castlingTargets(board, history, color).contains(dest))(
|
||||
HistoryMove(Square(File.E, rank), dest, Some(CastleSide.Queenside), pieceType = PieceType.King)
|
||||
HistoryMove(Square(File.E, rank), dest, Some("Queenside"), pieceType = PieceType.King)
|
||||
)
|
||||
case _ =>
|
||||
strictParseRegularMove(notation, board, history, color)
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
package de.nowchess.chess.observer
|
||||
|
||||
import de.nowchess.api.board.{Board, Color, Square}
|
||||
import de.nowchess.chess.logic.GameHistory
|
||||
import de.nowchess.api.board.{Color, Square}
|
||||
import de.nowchess.api.game.GameContext
|
||||
|
||||
/** Base trait for all game state events.
|
||||
* Events are immutable snapshots of game state changes.
|
||||
*/
|
||||
sealed trait GameEvent:
|
||||
def board: Board
|
||||
def history: GameHistory
|
||||
def turn: Color
|
||||
def context: GameContext
|
||||
|
||||
/** Fired when a move is successfully executed. */
|
||||
case class MoveExecutedEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
fromSquare: String,
|
||||
toSquare: String,
|
||||
capturedPiece: Option[String]
|
||||
@@ -23,77 +19,57 @@ case class MoveExecutedEvent(
|
||||
|
||||
/** Fired when the current player is in check. */
|
||||
case class CheckDetectedEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when the game reaches checkmate. */
|
||||
case class CheckmateEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
winner: Color
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when the game reaches stalemate. */
|
||||
case class StalemateEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when a move is invalid. */
|
||||
case class InvalidMoveEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
reason: String
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when a pawn reaches the back rank and the player must choose a promotion piece. */
|
||||
case class PromotionRequiredEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
from: Square,
|
||||
to: Square
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when the board is reset. */
|
||||
case class BoardResetEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired after any move where the half-move clock reaches 100 — the 50-move rule is now claimable. */
|
||||
case class FiftyMoveRuleAvailableEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when a player successfully claims a draw under the 50-move rule. */
|
||||
case class DrawClaimedEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when a move is undone, carrying PGN notation of the reversed move. */
|
||||
case class MoveUndoneEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
pgnNotation: String
|
||||
) extends GameEvent
|
||||
|
||||
/** Fired when a previously undone move is redone, carrying PGN notation of the replayed move. */
|
||||
case class MoveRedoneEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color,
|
||||
context: GameContext,
|
||||
pgnNotation: String,
|
||||
fromSquare: String,
|
||||
toSquare: String,
|
||||
@@ -102,9 +78,7 @@ case class MoveRedoneEvent(
|
||||
|
||||
/** Fired after a PGN string is successfully loaded and all moves are replayed into history. */
|
||||
case class PgnLoadedEvent(
|
||||
board: Board,
|
||||
history: GameHistory,
|
||||
turn: Color
|
||||
context: GameContext
|
||||
) extends GameEvent
|
||||
|
||||
/** Observer trait: implement to receive game state updates. */
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
rootProject.name = "NowChessSystems"
|
||||
include("modules:core", "modules:api", "modules:ui")
|
||||
include("modules:core", "modules:api", "modules:ui", "modules:rule")
|
||||
Reference in New Issue
Block a user