feat: NCS-41 Bot Platform (#33)
Co-authored-by: Janis <janis@nowchess.de> Reviewed-on: #33 Co-authored-by: Janis <janis.e.20@gmx.de> Co-committed-by: Janis <janis.e.20@gmx.de>
This commit is contained in:
@@ -81,8 +81,7 @@ object DefaultRules extends RuleSet:
|
||||
|
||||
private def countPositionOccurrences(context: GameContext, targetPosition: Position): Int =
|
||||
try
|
||||
var count = 0
|
||||
var tempCtx = GameContext(
|
||||
val initialCtx = GameContext(
|
||||
board = context.initialBoard,
|
||||
turn = Color.White,
|
||||
castlingRights = CastlingRights.Initial,
|
||||
@@ -91,20 +90,24 @@ object DefaultRules extends RuleSet:
|
||||
moves = List.empty,
|
||||
initialBoard = context.initialBoard,
|
||||
)
|
||||
var tempPos = Position(tempCtx.board, tempCtx.turn, tempCtx.castlingRights, tempCtx.enPassantSquare)
|
||||
if tempPos == targetPosition then count += 1
|
||||
|
||||
for move <- context.moves do
|
||||
tempCtx = applyMove(tempCtx)(move)
|
||||
tempPos = Position(
|
||||
board = tempCtx.board,
|
||||
turn = tempCtx.turn,
|
||||
castlingRights = tempCtx.castlingRights,
|
||||
enPassantSquare = tempCtx.enPassantSquare,
|
||||
def positionOf(ctx: GameContext): Position =
|
||||
Position(
|
||||
board = ctx.board,
|
||||
turn = ctx.turn,
|
||||
castlingRights = ctx.castlingRights,
|
||||
enPassantSquare = ctx.enPassantSquare,
|
||||
)
|
||||
if tempPos == targetPosition then count += 1
|
||||
|
||||
count
|
||||
val initialCount = if positionOf(initialCtx) == targetPosition then 1 else 0
|
||||
|
||||
context.moves
|
||||
.foldLeft((initialCtx, initialCount)) { case ((tempCtx, count), move) =>
|
||||
val nextCtx = applyMove(tempCtx)(move)
|
||||
val nextCount = if positionOf(nextCtx) == targetPosition then count + 1 else count
|
||||
(nextCtx, nextCount)
|
||||
}
|
||||
._2
|
||||
catch
|
||||
case _: Exception =>
|
||||
// If replay fails, conservatively count only the current position (never triggers a draw)
|
||||
|
||||
@@ -29,10 +29,12 @@ class DefaultRulesTest extends AnyFunSuite with Matchers:
|
||||
|
||||
test("pawn can capture diagonally"):
|
||||
// FEN: white pawn e4, black pawn d5
|
||||
val fen = "8/8/8/3p4/4P3/8/8/8 w - - 0 1"
|
||||
val context = FenParser.parseFen(fen).fold(_ => fail(), identity)
|
||||
val moves = rules.allLegalMoves(context)
|
||||
val captures = moves.filter(m => m.from == Square(File.E, Rank.R4) && (m.moveType match { case _: MoveType.Normal => true; case _ => false }))
|
||||
val fen = "8/8/8/3p4/4P3/8/8/8 w - - 0 1"
|
||||
val context = FenParser.parseFen(fen).fold(_ => fail(), identity)
|
||||
val moves = rules.allLegalMoves(context)
|
||||
val captures = moves.filter(m =>
|
||||
m.from == Square(File.E, Rank.R4) && (m.moveType match { case _: MoveType.Normal => true; case _ => false }),
|
||||
)
|
||||
captures.exists(m => m.to == Square(File.D, Rank.R5)) shouldBe true
|
||||
|
||||
test("pawn cannot move backward"):
|
||||
@@ -208,7 +210,7 @@ class DefaultRulesTest extends AnyFunSuite with Matchers:
|
||||
|
||||
test("threefold repetition catch block returns false for inconsistent context"):
|
||||
// A context whose moves cannot be replayed from initialBoard (forces the catch path)
|
||||
val m = Move(Square(File.E, Rank.R5), Square(File.E, Rank.R6)) // e5→e6, no pawn there in initial board
|
||||
val m = Move(Square(File.E, Rank.R5), Square(File.E, Rank.R6)) // e5→e6, no pawn there in initial board
|
||||
val brokenCtx = GameContext(
|
||||
board = Board.initial,
|
||||
turn = Color.White,
|
||||
|
||||
Reference in New Issue
Block a user