Skip to content

Commit

Permalink
Merge remote-tracking branch 'author/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
eduherminio committed Jul 31, 2023
2 parents fdb167e + f19b15d commit 3de2b69
Show file tree
Hide file tree
Showing 10 changed files with 571 additions and 71 deletions.
144 changes: 121 additions & 23 deletions Chess-Challenge/src/API/Board.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,25 @@ namespace ChessChallenge.API
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public sealed class Board
{
readonly Chess.Board board;
readonly APIMoveGen moveGen;
readonly RepetitionTable repetitionTable;

readonly HashSet<ulong> repetitionHistory;
readonly PieceList[] allPieceLists;
readonly PieceList[] validPieceLists;

readonly Move[] movesDest;
Move[] cachedLegalMoves;
bool hasCachedMoves;
Move[] cachedLegalCaptureMoves;
bool hasCachedCaptureMoves;
readonly Move[] movesDest;
bool hasCachedMoveCount;
int cachedMoveCount;
int depth;

/// <summary>
/// Create a new board. Note: this should not be used in the challenge,
Expand All @@ -31,6 +35,7 @@ public Board(Chess.Board boardSource)
board = new Chess.Board();
board.LoadPosition(boardSource.StartPositionInfo);
GameMoveHistory = new Move[boardSource.AllGameMoves.Count];
repetitionTable = new();

for (int i = 0; i < boardSource.AllGameMoves.Count; i ++)
{
Expand Down Expand Up @@ -61,9 +66,8 @@ public Board(Chess.Board boardSource)
this.validPieceLists = validPieceLists.ToArray();

// Init rep history
repetitionHistory = new HashSet<ulong>(board.RepetitionPositionHistory);
GameRepetitionHistory = repetitionHistory.ToArray();
repetitionHistory.Remove(board.ZobristKey);
GameRepetitionHistory = board.RepetitionPositionHistory.ToArray();
repetitionTable.Init(board);
}

/// <summary>
Expand All @@ -75,10 +79,12 @@ public void MakeMove(Move move)
{
if (!move.IsNull)
{
repetitionHistory.Add(board.ZobristKey);
OnPositionChanged();
board.MakeMove(new Chess.Move(move.RawValue), inSearch: true);
}
repetitionTable.Push(ZobristKey, move.IsCapture || move.MovePieceType == PieceType.Pawn);
depth++;

}
}

/// <summary>
Expand All @@ -88,9 +94,9 @@ public void UndoMove(Move move)
{
if (!move.IsNull)
{
repetitionTable.TryPop();
board.UndoMove(new Chess.Move(move.RawValue), inSearch: true);
OnPositionChanged();
repetitionHistory.Remove(board.ZobristKey);
}
}

Expand Down Expand Up @@ -151,6 +157,8 @@ public Move[] GetLegalMoves(bool capturesOnly = false)
moveGen.GenerateMoves(ref moveSpan, board, includeQuietMoves: true);
cachedLegalMoves = moveSpan.ToArray();
hasCachedMoves = true;
hasCachedMoveCount = true;
cachedMoveCount = moveSpan.Length;
}

return cachedLegalMoves;
Expand All @@ -166,7 +174,9 @@ public void GetLegalMovesNonAlloc(ref Span<Move> moveList, bool capturesOnly = f
{
bool includeQuietMoves = !capturesOnly;
moveGen.GenerateMoves(ref moveList, board, includeQuietMoves);
}
hasCachedMoveCount = true;
cachedMoveCount = moveList.Length;
}


Move[] GetLegalCaptureMoves()
Expand All @@ -184,12 +194,12 @@ Move[] GetLegalCaptureMoves()
/// <summary>
/// Test if the player to move is in check in the current position.
/// </summary>
public bool IsInCheck() => board.IsInCheck();
public bool IsInCheck() => moveGen.IsInitialized ? moveGen.InCheck() : board.IsInCheck();

/// <summary>
/// Test if the current position is checkmate
/// </summary>
public bool IsInCheckmate() => IsInCheck() && GetLegalMoves().Length == 0;
public bool IsInCheckmate() => IsInCheck() && HasZeroLegalMoves();

/// <summary>
/// Test if the current position is a draw due stalemate, repetition, insufficient material, or 50-move rule.
Expand All @@ -199,17 +209,24 @@ Move[] GetLegalCaptureMoves()
public bool IsDraw()
{
return IsFiftyMoveDraw() || IsInsufficientMaterial() || IsInStalemate() || IsRepeatedPosition();

bool IsInStalemate() => !IsInCheck() && GetLegalMoves().Length == 0;
bool IsFiftyMoveDraw() => board.currentGameState.fiftyMoveCounter >= 100;
}

/// <summary>
/// Test if the current position has occurred at least once before on the board.
/// This includes both positions in the actual game, and positions reached by
/// making moves while the bot is thinking.
/// </summary>
public bool IsRepeatedPosition() => repetitionHistory.Contains(board.ZobristKey);
/// <summary>
/// Test if the current position is a draw due to stalemate
/// </summary>
public bool IsInStalemate() => !IsInCheck() && HasZeroLegalMoves();

/// <summary>
/// Test if the current position is a draw due to the fifty move rule
/// </summary>
public bool IsFiftyMoveDraw() => board.currentGameState.fiftyMoveCounter >= 100;

/// <summary>
/// Test if the current position has occurred at least once before on the board.
/// This includes both positions in the actual game, and positions reached by
/// making moves while the bot is thinking.
/// </summary>
public bool IsRepeatedPosition() => depth > 0 && repetitionTable.Contains(board.ZobristKey);

/// <summary>
/// Test if there are sufficient pieces remaining on the board to potentially deliver checkmate.
Expand Down Expand Up @@ -344,10 +361,81 @@ public ulong GetPieceBitboard(PieceType pieceType, bool white)
public Move[] GameMoveHistory { get; private set; }

/// <summary>
/// Creates a board from the given fen string. Please note that this is quite slow, and so it is advised
/// to use the board given in the Think function, and update it using MakeMove and UndoMove instead.
/// Creates an ASCII-diagram of the current position.
/// The capital letters are the white pieces, while the lowercase letters are the black ones.
/// NOTE: To distinguish kings from knights, kings are represented by K/k and knights by N/n.
/// </summary>
public static Board CreateBoardFromFEN(string fen)
public string CreateDiagram(bool blackAtTop = true, bool includeFen = true, bool includeZobristKey = true, Square? highlightedSquare = null)
{
StringBuilder result = new();

for (int y = 0; y < 8; y++)
{
int rankIndex = blackAtTop ? 7 - y : y;
result.AppendLine("+---+---+---+---+---+---+---+---+");

for (int x = 0; x < 8; x++)
{
int fileIndex = blackAtTop ? x : 7 - x;
Square square = new Square(fileIndex, rankIndex);
Piece pieceInSquare = GetPiece(square);
if (square != highlightedSquare)
{
result.Append($"| {GetPieceSymbol(pieceInSquare)} ");
}
else
{
// To highlight this square, we add brackets around its piece
result.Append($"|({GetPieceSymbol(pieceInSquare)})");
}

if (x == 7)
{
// Show rank number on the right side
result.AppendLine($"| {rankIndex + 1}");
}
}

if (y == 7)
{
// Show files at the bottom
result.AppendLine("+---+---+---+---+---+---+---+---+");
const string fileNames = " a b c d e f g h ";
const string fileNamesRev = " h g f e d c b a ";
result.AppendLine(blackAtTop ? fileNames : fileNamesRev);
result.AppendLine();

if (includeFen)
{
result.AppendLine($"Fen : {FenUtility.CurrentFen(board)}");
}
if (includeZobristKey)
{
result.AppendLine($"Zobrist Key : {board.ZobristKey}");
}
}
}

return result.ToString();

static char GetPieceSymbol(Piece piece)
{
if (piece.IsNull)
{
return ' ';
}
char pieceSymbol = piece.IsKnight ? 'N' : piece.PieceType.ToString()[0];
return piece.IsWhite ? char.ToUpper(pieceSymbol) : char.ToLower(pieceSymbol);
}
}

public override string ToString() => CreateDiagram();

/// <summary>
/// Creates a board from the given fen string. Please note that this is quite slow, and so it is advised
/// to use the board given in the Think function, and update it using MakeMove and UndoMove instead.
/// </summary>
public static Board CreateBoardFromFEN(string fen)
{
Chess.Board boardCore = new Chess.Board();
boardCore.LoadPosition(fen);
Expand All @@ -359,7 +447,17 @@ void OnPositionChanged()
moveGen.NotifyPositionChanged();
hasCachedMoves = false;
hasCachedCaptureMoves = false;
hasCachedMoveCount = false;
}

bool HasZeroLegalMoves()
{
if (hasCachedMoveCount)
{
return cachedMoveCount == 0;
}
return moveGen.NoLegalMovesInPosition(board);
}

}
}
14 changes: 10 additions & 4 deletions Chess-Challenge/src/API/Timer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ public sealed class Timer
/// </summary>
public readonly int GameStartTimeMilliseconds;

/// <summary>
/// The amount of time (in milliseconds) that gets added to the clock after each turn
/// </summary>
public readonly int IncrementMilliseconds;

/// <summary>
/// Amount of time elapsed since the current player started thinking (in milliseconds)
/// </summary>
Expand All @@ -33,12 +38,13 @@ public Timer(int millisRemaining)
sw = System.Diagnostics.Stopwatch.StartNew();
}

public Timer(int millisRemaining, int opponentMillisRemaining, int startingTimeMillis)
public Timer(int remainingMs, int opponentRemainingMs, int startingMs, int incrementMs = 0)
{
millisRemainingAtStartOfTurn = millisRemaining;
millisRemainingAtStartOfTurn = remainingMs;
sw = System.Diagnostics.Stopwatch.StartNew();
GameStartTimeMilliseconds = startingTimeMillis;
OpponentMillisecondsRemaining = opponentMillisRemaining;
GameStartTimeMilliseconds = startingMs;
OpponentMillisecondsRemaining = opponentRemainingMs;
IncrementMilliseconds = incrementMs;
}

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Move GetBotMove()

try
{
API.Timer timer = new(PlayerToMove.TimeRemainingMs, PlayerNotOnMove.TimeRemainingMs, GameDurationMilliseconds);
API.Timer timer = new(PlayerToMove.TimeRemainingMs, PlayerNotOnMove.TimeRemainingMs, GameDurationMilliseconds, IncrementMilliseconds);
API.Move move = PlayerToMove.Bot.Think(botBoard, timer);
return new Move(move.RawValue);
}
Expand Down Expand Up @@ -234,6 +234,7 @@ void OnMoveChosen(Move chosenMove)
{
if (IsLegal(chosenMove))
{
PlayerToMove.AddIncrement(IncrementMilliseconds);
if (PlayerToMove.IsBot)
{
moveToPlay = chosenMove;
Expand Down
3 changes: 2 additions & 1 deletion Chess-Challenge/src/Framework/Application/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ namespace ChessChallenge.Application
{
public static class Settings
{
public const string Version = "1.17";
public const string Version = "1.18";

// Game settings
public const int GameDurationMilliseconds = 60 * 1000;
public const int IncrementMilliseconds = 0 * 1000;
public const float MinMoveDelay = 0;
public static readonly bool RunBotsOnSeparateThread = false;

Expand Down
Loading

0 comments on commit 3de2b69

Please sign in to comment.