public void Next() { if (index < moveList.Count) { BoardGround.MakeMove(moveList [index], true); index++; } }
public void Previous() { if (index > 0) { index--; BoardGround.UnmakeMove(moveList [index], true); } }
void Update() { Debug.LogError("Test"); BoardGround.MakeMove(new MoveGeneratorGround().GetMoves(false, false).GetMove(0), true); if (Input.GetKeyDown(KeyCode.Space)) { MoveGeneratorGround.Debug(); } }
// draw requested by player public void Draw() { if (BoardGround.ThreefoldRepetition()) { GameOver(0, DefinitionsGround.ResultType.Repetition); } else if (BoardGround.halfmoveCountSinceLastPawnMoveOrCap >= 100) { GameOver(0, DefinitionsGround.ResultType.FiftyMoveRule); } }
public void HighlightMove(int fromIndex, int toIndex) { int fromX = GetIndex(BoardGround.FileFrom128(fromIndex) - 1); int fromY = GetIndex(BoardGround.RankFrom128(fromIndex) - 1); int toX = GetIndex(BoardGround.FileFrom128(toIndex) - 1); int toY = GetIndex(BoardGround.RankFrom128(toIndex) - 1); // highlight move squares; squares [fromX, fromY].material.color = themes[themeIndex].moveFromHighlightColour; squares [toX, toY].material.color = themes[themeIndex].moveToHighlightColour; }
public static void DebugGameState(ushort state) { UnityEngine.Debug.Log("state " + state); bool whiteToMove = (state & 1) != 0; bool w00 = (state & 1 << 1) != 0; bool w000 = (state & 1 << 2) != 0; bool b00 = (state & 1 << 3) != 0; bool b000 = (state & 1 << 4) != 0; int epFile = (state >> 5) & 15; int capturedPieceCode = (state >> 9) & 15; UnityEngine.Debug.Log("white to move: " + whiteToMove); UnityEngine.Debug.Log("white 0-0 " + w00 + " 0-0-0 " + w000 + " black 0-0 " + b00 + " 0-0-0 " + b000); UnityEngine.Debug.Log("ep file: " + epFile); string boardString = ""; string colourString = ""; if (capturedPieceCode != 0) { UnityEngine.Debug.Log("Captured piece: " + pieceNameDictionary [capturedPieceCode]); } for (int y = 7; y >= 0; y--) { for (int x = 0; x < 8; x++) { int i = BoardGround.Convert64to128(y * 8 + x); // pieces int code = boardArray [i]; if (code == 0) { boardString += " # "; } else { boardString += " " + pieceNameDictionary [code] + " "; } // colour string colString = (boardColourArray[i] == 0)?" B ":" W "; if (boardColourArray[i] == -1) { colString = " # "; } colourString += colString; } boardString += "\n"; colourString += "\n"; } UnityEngine.Debug.Log(boardString); UnityEngine.Debug.Log(colourString); UnityEngine.Debug.Log("########################### \n"); }
int RunTest() { nodeSearchCount = 0; BoardGround.SetPositionFromFen(fen); TimerGround.Start("PerftGround"); PerfTest(searchDepth); TimerGround.Stop("PerftGround"); TimerGround.Print("PerftGround"); print("Leaf nodes at depth " + searchDepth + ": " + nodeSearchCount); return(nodeSearchCount); }
void OnMove(ushort move) { int moveToIndex = (move >> 7) & 127; int capturedPieceCode = BoardGround.boardArray [moveToIndex]; // get capture piece code if (capturedPieceCode == 0) { AudioSource.PlayClipAtPoint(moveAudio, Vector3.zero, 1f); } else { AudioSource.PlayClipAtPoint(captureAudio, Vector3.zero, 1f); } BoardGround.MakeMove(move, true); if (OnMoveMade != null) { OnMoveMade(whiteToPlay, move); } whiteToPlay = !whiteToPlay; // detect mate/stalemate if (moveGenerator.PositionIsMate()) { GameOver(((whiteToPlay) ? -1 : 1), DefinitionsGround.ResultType.Checkmate); // player is mated } else if (moveGenerator.PositionIsStaleMate()) { GameOver(0, DefinitionsGround.ResultType.Stalemate); // player is mated } else if (moveGenerator.InsuffientMatingMaterial()) { GameOver(0, DefinitionsGround.ResultType.InsufficientMaterial); } else { if (whitePlayerType == PlayerType.AI && blackPlayerType == PlayerType.AI) { StartCoroutine(RequestMoveCoroutine(.15f)); // force delay between moves when two AI are playing } else { RequestMove(); } } }
/// Creates and adds king move to move list. Also checks legality if not in psuedolegal mode. /// castleThroughIndex is the square which king passes through during castling (so that can't castle through check) void CreateKingMove(int fromIndex, int toIndex, int castleThroughIndex, bool isCastles) { ushort newMove = (ushort)(fromIndex | toIndex << 7); int capturedPieceType = BoardGround.boardArray[toIndex] & ~1; int moveOrderEval = 0; if (!pseudolegalMode) // if not in psuedolegal mode, elimate moves that leave king in check / castling through check { BoardGround.MakeMove(newMove); bool inCheckAfterMove = SquareAttackedByEnemy(toIndex); BoardGround.UnmakeMove(newMove); if (inCheckAfterMove) { return; } } if (isCastles) { if (inCheck) { return; } if (SquareAttackedByEnemy(castleThroughIndex)) // cannot castle if castling through check { return; } if (trackStats) { castles++; } } if (trackStats) { if (BoardGround.boardColourArray[toIndex] == (1 - moveColour)) { captures++; } } moves.Add(newMove, (short)moveOrderEval); }
/// Updates the board UI to match the board array public void AutoUpdate() { ResetHighlights(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { int x = GetIndex(j); int y = GetIndex(i); int index = BoardGround.Convert64to128(i * 8 + j); char pieceCode = BoardGround.pieceNameDictionary[BoardGround.boardArray[index]]; board[x, y] = pieceCode; pieceSquares[x, y].sprite = spriteDictionary[pieceCode]; } } }
void PerfTest(int depth) { if (depth == 0) { nodeSearchCount++; return; } IList <ushort> moves = moveGenerator.GetMoves(false, false) as IList <ushort>; for (int i = 0; i < moves.Count; i++) { BoardGround.MakeMove(moves[i]); PerfTest(depth - 1); BoardGround.UnmakeMove(moves[i]); } }
public void StartSearch() { bestMoveSoFar = 0; transpositionTable.Clear(); nodesSearched = 0; breakCount = 0; finishedSearch = false; moveGenerator = new MoveGeneratorGround(); findingMoveForWhite = BoardGround.IsWhiteToPlay(); Thread searchThread = new Thread(Iterate); searchThread.Start(); //Iterate (); }
/// Creates and adds move to move list. Also checks legality if not in psuedolegal mode /// Note: for king moves use separate CreateKingMove method void CreateMove(int fromIndex, int toIndex) { ushort newMove = (ushort)(fromIndex | toIndex << 7); int capturedPieceType = BoardGround.boardArray[toIndex] & ~1; if (!pseudolegalMode) // if not in psuedolegal mode, elimate moves that leave king in check { BoardGround.MakeMove(newMove); bool inCheckAfterMove = InCheckAfterMove(fromIndex, toIndex, capturedPieceType); BoardGround.UnmakeMove(newMove); if (inCheckAfterMove) { return; } } if (trackStats) { if (BoardGround.boardColourArray[toIndex] == (1 - moveColour)) { captures++; } } int movePieceType = BoardGround.boardArray [fromIndex] & ~1; int moveOrderEval = 0; if (capturedPieceType != 0) // capture move { moveOrderEval = PieceEvalFromType(capturedPieceType) + 100 - PieceEvalFromType(movePieceType); // prefer capturing high-value enemy piece with low-value friendly piece } else // non capture move { int toRank = BoardGround.RankFrom128(toIndex); int direction = (moveColour == 1)?1:-1; if (Math.Sign(toRank - BoardGround.RankFrom128(fromIndex)) != direction) // penalise retreating moves { moveOrderEval = -10; } } moves.Add(newMove, (short)moveOrderEval); }
public void CreatePlayers() { boardInput = GetComponent <ChessInputGround>(); HumanPlayerGround whiteHuman = null; HumanPlayerGround blackHuman = null; AIPlayerGround whiteAI = null; AIPlayerGround blackAI = null; if (blackPlayerType == PlayerType.Human) { ChessUIGround.instance.SetBoardOrientation(false); blackHuman = new HumanPlayerGround(); boardInput.AddPlayer(blackHuman); } else { blackAI = new AIPlayerGround(); } if (whitePlayerType == PlayerType.Human) { ChessUIGround.instance.SetBoardOrientation(true); whiteHuman = new HumanPlayerGround(); boardInput.AddPlayer(whiteHuman); // FindObjectOfType<NotationInput>().SetPlayer(whiteHuman); } else { whiteAI = new AIPlayerGround(); } whitePlayer = (PlayerGround)whiteHuman ?? (PlayerGround)whiteAI; blackPlayer = (PlayerGround)blackHuman ?? (PlayerGround)blackAI; whitePlayer.OnMove += OnMove; blackPlayer.OnMove += OnMove; whitePlayer.Init(true); blackPlayer.Init(false); whiteToPlay = BoardGround.IsWhiteToPlay(); RequestMove(); }
void HighlightSquare(ushort move, string pieceAlgebraic) { int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int moveFromX = BoardGround.Convert128to64(moveFromIndex) % 8; int moveFromY = BoardGround.Convert128to64(moveFromIndex) / 8; int moveToX = BoardGround.Convert128to64(moveToIndex) % 8; int moveToY = BoardGround.Convert128to64(moveToIndex) / 8; string fromAlgebraic = DefinitionsGround.fileNames[moveFromX].ToString() + DefinitionsGround.rankNames[moveFromY].ToString(); string toAlgebraic = DefinitionsGround.fileNames[moveToX].ToString() + DefinitionsGround.rankNames[moveToY].ToString(); if (fromAlgebraic == pieceHeld.algebraicCoordinate) { ChessUIGround.instance.HighlightSquare(toAlgebraic); } }
public static void PrintBoard(string optionalHeader = "") { string boardString = ""; string colourString = ""; if (optionalHeader != "") { optionalHeader += ":\n"; } for (int y = 7; y >= 0; y--) { for (int x = 0; x < 8; x++) { int i = BoardGround.Convert64to128(y * 8 + x); // pieces int code = boardArray [i]; if (code == 0) { boardString += " # "; } else { boardString += " " + pieceNameDictionary [code] + " "; } // colour string colString = (boardColourArray[i] == 0)?" B ":" W "; if (boardColourArray[i] == -1) { colString = " # "; } colourString += colString; } boardString += "\n"; colourString += "\n"; } UnityEngine.Debug.Log(optionalHeader + boardString); }
int AlphaBetaSearch(int plyRemaining, int alpha, int beta, bool isWhite) { HeapGround moveHeap = moveGenerator.GetMoves(false, false); int count = moveHeap.Count; if (moveHeap.Count == 0) { return(((isWhite)?-1:1) * (10000000 + plyRemaining)); // if no moves available, side has been checkmated. Return best score for opponent. Checkmating sooner (higher depth) is rewarded. } if (plyRemaining == 0) { nodesSearched++; return(Evaluate()); //QuiescenceSearch(int.MinValue,int.MaxValue,!isWhite,true); //return quiescenceScore; } if (isWhite) // white is trying to attain the highest evaluation possible { for (int i = 0; i < count; i++) { ushort move = moveHeap.RemoveFirst(); BoardGround.MakeMove(move); alpha = Math.Max(alpha, AlphaBetaSearch(plyRemaining - 1, alpha, beta, false)); BoardGround.UnmakeMove(move); if (plyRemaining == searchDepth) // has searched full depth and is now looking at top layer of moves to select the best { if (alpha > bestScoreThisIteration) { bestScoreThisIteration = alpha; bestMoveThisIteration = move; } } if (beta <= alpha) // break { breakCount++; break; } } return(alpha); } else { // black is trying to obtain the lowest evaluation possible for (int i = 0; i < count; i++) { ushort move = moveHeap.RemoveFirst(); BoardGround.MakeMove(move); beta = Math.Min(beta, AlphaBetaSearch(plyRemaining - 1, alpha, beta, true)); BoardGround.UnmakeMove(move); if (plyRemaining == searchDepth) // has searched full depth and is now looking at top layer of moves to select the best { if (beta < bestScoreThisIteration) { bestScoreThisIteration = beta; bestMoveThisIteration = move; } } if (beta <= alpha) // break { breakCount++; break; } } return(beta); } return(0); }
/// Creates and adds move to move list. Also checks legality if not in psuedolegal mode /// Note: for king moves use separate CreateKingMove method void CreatePawnMove(int fromIndex, int toIndex, int epCaptureIndex = -1) { ushort newMove = (ushort)(fromIndex | toIndex << 7); int capturedPieceType = BoardGround.boardArray[toIndex] & ~1; if (!pseudolegalMode) // if not in psuedolegal mode, elimate moves that leave king in check { BoardGround.MakeMove(newMove); bool inCheckAfterMove = InCheckAfterMove(fromIndex, toIndex, capturedPieceType); //bool inCheck = SquareAttackedByPlayer(friendlyKingIndex,(1-moveColour)); BoardGround.UnmakeMove(newMove); if (inCheckAfterMove) { return; } } if (trackStats) { if (BoardGround.boardColourArray[toIndex] == (1 - moveColour) || toIndex == epCaptureIndex) { captures++; } } if (toIndex >= 112 || toIndex <= 7) // pawn is promoting { if (trackStats) { promotions += 4; } moves.Add(newMove, 100); //promote to queen moves.Add((ushort)(newMove | 1 << 14), -80); // rook moves.Add((ushort)(newMove | 2 << 14), -70); // knight moves.Add((ushort)(newMove | 3 << 14), -100); // bishop } else { int movePieceType = BoardGround.boardArray [fromIndex] & ~1; int moveOrderEval = 0; int toRank = BoardGround.RankFrom128(toIndex); if (movePieceType == BoardGround.pawnCode) // prefer to move pawn the further advanced it is { if (moveColour == 1) { if (toRank > 5) { moveOrderEval = 10 * (toRank - 5); } } else { if (toRank < 4) { moveOrderEval = 10 * (4 - toRank); } } } moves.Add(newMove, (short)moveOrderEval); // regular pawn move } }
void GenerateMove(int moveFromIndex) { int moveToIndex; int movePieceType = BoardGround.boardArray [moveFromIndex] & ~1; // piece type code // Moving the king: if (movePieceType == BoardGround.kingCode) { for (int overlayIndex = 0; overlayIndex < kingOverlay.Length; overlayIndex++) { moveToIndex = moveFromIndex + kingOverlay[overlayIndex]; if (IndexOnBoard(moveToIndex)) { if (BoardGround.boardColourArray[moveToIndex] != moveColour) // can't move to square occupied by friendly piece { CreateKingMove(moveFromIndex, moveToIndex, 0, false); } } } // Castling: if (moveColour == 1 && moveFromIndex == 4) // white king still on starting square { if ((BoardGround.currentGamestate >> 1 & 1) == 1) // has 0-0 right { if (BoardGround.boardArray[5] == 0 && BoardGround.boardArray[6] == 0) // no pieces blocking castling { CreateKingMove(4, 6, 5, true); } } if ((BoardGround.currentGamestate >> 2 & 1) == 1) // has 0-0-0 right { if (BoardGround.boardArray[3] == 0 && BoardGround.boardArray[2] == 0 && BoardGround.boardArray[1] == 0) // no pieces blocking castling { CreateKingMove(4, 2, 3, true); } } } else if (moveColour == 0 && moveFromIndex == 116) // black king still on starting square { if ((BoardGround.currentGamestate >> 3 & 1) == 1) // has 0-0 right { if (BoardGround.boardArray[117] == 0 && BoardGround.boardArray[118] == 0) // no pieces blocking castling { CreateKingMove(116, 118, 117, true); } } if ((BoardGround.currentGamestate >> 4 & 1) == 1) // has 0-0-0 right { if (BoardGround.boardArray[115] == 0 && BoardGround.boardArray[114] == 0 && BoardGround.boardArray[113] == 0) // no pieces blocking castling { CreateKingMove(116, 114, 115, true); } } } } // Moving the knight: else if (movePieceType == BoardGround.knightCode) { for (int overlayIndex = 0; overlayIndex < knightOverlay.Length; overlayIndex++) { moveToIndex = moveFromIndex + knightOverlay[overlayIndex]; if (IndexOnBoard(moveToIndex)) { if (BoardGround.boardColourArray[moveToIndex] != moveColour) // can't move to square occupied by friendly piece { CreateMove(moveFromIndex, moveToIndex); } } } } // Moving a pawn: else if (movePieceType == BoardGround.pawnCode) { int pawnDirection = (moveColour == 1)?1:-1; moveToIndex = moveFromIndex + pawnDirection * 16; if (moveToIndex < 0 || moveToIndex >= BoardGround.boardArray.Length) { BoardGround.DebugGameState(BoardGround.currentGamestate); UnityEngine.Debug.Log("Pawn error: move to: " + moveToIndex + " from: " + moveFromIndex + " dir: " + pawnDirection); } if (BoardGround.boardArray[moveToIndex] == 0) // square in front of pawn is unnocupied { CreatePawnMove(moveFromIndex, moveToIndex); // regular pawn move if ((moveFromIndex <= 23 && moveColour == 1) || (moveFromIndex >= 96 && moveColour == 0)) // pawn on starting rank { moveToIndex = moveFromIndex + pawnDirection * 32; if (BoardGround.boardArray[moveToIndex] == 0) // if no pieces blocking double pawn push { CreatePawnMove(moveFromIndex, moveToIndex); // move two squares } } } int epCaptureIndex = (BoardGround.currentGamestate >> 5 & 15) - 1 + ((opponentColour == 0)?80:32); // pawn captures moveToIndex = moveFromIndex + (16 - pawnDirection) * pawnDirection; // capture left (from white's pov) if (IndexOnBoard(moveToIndex)) { if (BoardGround.boardColourArray[moveToIndex] == opponentColour || moveToIndex == epCaptureIndex) // if capture square contains opponent piece or is ep capture square { CreatePawnMove(moveFromIndex, moveToIndex, epCaptureIndex); // TODO: ep capture index parameter is only for stat counting. Remove. } } moveToIndex = moveFromIndex + (16 + pawnDirection) * pawnDirection; // capture right (from white's pov) if (IndexOnBoard(moveToIndex)) { if (BoardGround.boardColourArray[moveToIndex] == opponentColour || moveToIndex == epCaptureIndex) // if capture square contains opponent piece or is ep capture square { CreatePawnMove(moveFromIndex, moveToIndex, epCaptureIndex); // TODO: ep capture index parameter is only for stat counting. Remove. } } } // Queen, rook and bishop else { int startIndex = 0; int endIndex = 7; if (movePieceType == BoardGround.bishopCode) { startIndex = 4; // skip horizontal overlays } else if (movePieceType == BoardGround.rookCode) { endIndex = 3; // skip diagonal overlays } for (int overlayIndex = startIndex; overlayIndex <= endIndex; overlayIndex++) { for (int i = 1; i <= 8; i++) { moveToIndex = moveFromIndex + kingOverlay[overlayIndex] * i; bool lineOpen = IndexOnBoard(moveToIndex); if (lineOpen) { if (BoardGround.boardArray[moveToIndex] != 0) // something is obstructing movement { lineOpen = false; } if (BoardGround.boardColourArray[moveToIndex] != moveColour) // if square is not friendly, i.e contains enemy or no piece, square can be moves to { CreateMove(moveFromIndex, moveToIndex); } } if (!lineOpen) { break; // stop searching this line once it has reached obstruction/end of board } } } } }
/// Updates the board UI to match the board array public void AutoUpdate() { // Debug.Log("Inside AutoUpdate()"); ResetHighlights(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { int x = GetIndex(j); int y = GetIndex(i); int index = BoardGround.Convert64to128(i * 8 + j); char pieceCode = BoardGround.pieceNameDictionary[BoardGround.boardArray[index]]; board[x, y] = pieceCode; // Neither black nor white. We only need a box collider here. No mesh collider. if (pieceCode == ' ') { // pieceSquares[x, y].GetComponent<EventTrigger>().enabled = false; pieceSquares[x, y].GetComponent <MeshCollider>().enabled = false; pieceSquares[x, y].mesh = null; } // Black. We need an additional mesh collider here. else if (pieceCode == 'r' || pieceCode == 'n' || pieceCode == 'b' || pieceCode == 'q' || pieceCode == 'k' || pieceCode == 'p') { // pieceSquares[x, y].GetComponent<EventTrigger>().enabled = true; pieceSquares[x, y].GetComponent <MeshCollider>().enabled = true; // If its a pawn then we assign the mesh(for the collider) of rook. if (pieceCode == 'p') { pieceSquares[x, y].GetComponent <MeshCollider>().sharedMesh = filterDictionary['r']; } // If its anything other than pawn we assign the mesh(for the collider) of itself. else { pieceSquares[x, y].GetComponent <MeshCollider>().sharedMesh = filterDictionary[pieceCode]; } pieceSquares[x, y].GetComponent <MeshRenderer>().material = blackMaterial; pieceSquares[x, y].mesh = filterDictionary[pieceCode]; } // White. We need an additional mesh collider here. else { // pieceSquares[x, y].GetComponent<EventTrigger>().enabled = true; pieceSquares[x, y].GetComponent <MeshCollider>().enabled = true; // If its a pawn then we assign the mesh(for the collider) of rook. if (pieceCode == 'P') { pieceSquares[x, y].GetComponent <MeshCollider>().sharedMesh = filterDictionary['R']; } // If its anything other than pawn we assign the mesh(for the collider) of itself. else { pieceSquares[x, y].GetComponent <MeshCollider>().sharedMesh = filterDictionary[pieceCode]; } pieceSquares[x, y].GetComponent <MeshRenderer>().material = whiteMaterial; pieceSquares[x, y].mesh = filterDictionary[pieceCode]; } // pieceSquares[x, y].sprite = spriteDictionary[pieceCode]; } } }
public static string NotationFromMove(ushort move) { BoardGround.UnmakeMove(move); // unmake move on board MoveGeneratorGround moveGen = new MoveGeneratorGround(); int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int promotionPieceIndex = (move >> 14) & 3; // 0 = queen, 1 = rook, 2 = knight, 3 = bishop int colourToMove = BoardGround.boardColourArray[moveFromIndex]; int movePieceCode = BoardGround.boardArray [moveFromIndex]; // get move piece code int movePieceType = movePieceCode & ~1; // get move piece type code (no colour info) int capturedPieceCode = BoardGround.boardArray [moveToIndex]; // get capture piece code int promotionPieceType = BoardGround.pieceCodeArray [promotionPieceIndex]; if (movePieceType == BoardGround.kingCode) { if (moveToIndex - moveFromIndex == 2) { BoardGround.MakeMove(move); // remake move return("O-O"); } else if (moveToIndex - moveFromIndex == -2) { BoardGround.MakeMove(move); // remake move return("O-O-O"); } } string moveNotation = GetSymbolFromPieceType(movePieceType); // check if any ambiguity exists in notation (e.g if e2 can be reached via Nfe2 and Nbe2) if (movePieceType != BoardGround.pawnCode && movePieceType != BoardGround.kingCode) { HeapGround allMoves = moveGen.GetMoves(false, false); for (int i = 0; i < allMoves.Count; i++) { int alternateMoveFromIndex = allMoves.moves[i] & 127; int alternateMoveToIndex = (allMoves.moves[i] >> 7) & 127; int alternateMovePieceCode = BoardGround.boardArray [alternateMoveFromIndex]; if (alternateMoveFromIndex != moveFromIndex && alternateMoveToIndex == moveToIndex) // if moving to same square from different square { if (alternateMovePieceCode == movePieceCode) // same piece type { int fromFileIndex = BoardGround.FileFrom128(moveFromIndex) - 1; int alternateFromFileIndex = BoardGround.FileFrom128(alternateMoveFromIndex) - 1; int fromRankIndex = BoardGround.RankFrom128(moveFromIndex) - 1; int alternateFromRankIndex = BoardGround.RankFrom128(alternateMoveFromIndex) - 1; if (fromFileIndex != alternateFromFileIndex) // pieces on different files, thus ambiguity can be resolved by specifying file { moveNotation += DefinitionsGround.fileNames[fromFileIndex]; break; // ambiguity resolved } else if (fromRankIndex != alternateFromRankIndex) { moveNotation += DefinitionsGround.rankNames[fromRankIndex]; break; // ambiguity resolved } } } } } if (capturedPieceCode != 0) // add 'x' to indicate capture { if (movePieceType == BoardGround.pawnCode) { moveNotation += DefinitionsGround.fileNames[BoardGround.FileFrom128(moveFromIndex) - 1]; } moveNotation += "x"; } else // check if capturing ep { if (movePieceType == BoardGround.pawnCode) { if (System.Math.Abs(moveToIndex - moveFromIndex) != 16 && System.Math.Abs(moveToIndex - moveFromIndex) != 32) { moveNotation += DefinitionsGround.fileNames[BoardGround.FileFrom128(moveFromIndex) - 1] + "x"; } } } moveNotation += DefinitionsGround.fileNames [BoardGround.FileFrom128(moveToIndex) - 1]; moveNotation += DefinitionsGround.rankNames [BoardGround.RankFrom128(moveToIndex) - 1]; // add = piece type if promotion if (movePieceType == BoardGround.pawnCode) { if (moveToIndex >= 112 || moveToIndex <= 7) // pawn has reached first/eighth rank { moveNotation += "=" + GetSymbolFromPieceType(promotionPieceType); } } // add check/mate symbol if applicable BoardGround.MakeMove(move); // remake move if (moveGen.PositionIsMate()) { moveNotation += "#"; } else if (moveGen.PositionIsCheck()) { moveNotation += "+"; } return(moveNotation); }
public void Init() { BoardGround.SetPositionFromFen(DefinitionsGround.startFen, true); }
/// Returns a list of moves given a pgn string. /// Note that BoardGround should be set to whatever starting position of pgn is. public static List <ushort> MovesFromPGN(string pgn) { List <string> moveStrings = MoveStringsFromPGN(pgn); List <ushort> allMoves = new List <ushort> (); MoveGeneratorGround moveGen = new MoveGeneratorGround(); for (int i = 0; i < moveStrings.Count; i++) { string moveString = moveStrings[i]; moveString = moveString.Replace("+", ""); // remove check symbol moveString = moveString.Replace("#", ""); // remove mate symbol moveString = moveString.Replace("x", ""); // remove capture symbol string moveStringLower = moveStrings[i].ToLower(); ushort[] movesInPosition = moveGen.GetMoves(false, false).moves; ushort move = 0; for (int j = 0; j < movesInPosition.Length; j++) { move = movesInPosition[j]; int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int movePieceType = BoardGround.boardArray[moveFromIndex] & ~1; int colourCode = BoardGround.boardArray[moveFromIndex] & 1; if (moveStringLower == "oo") // castle kingside { if (movePieceType == BoardGround.kingCode && moveToIndex - moveFromIndex == 2) { break; } } else if (moveStringLower == "ooo") // castle queenside { if (movePieceType == BoardGround.kingCode && moveToIndex - moveFromIndex == -2) { break; } } else if (DefinitionsGround.fileNames.Contains(moveString[0] + "")) // pawn move if starts with any file indicator (e.g. 'e'4. Note that uppercase B is used for bishops) { if (movePieceType != BoardGround.pawnCode) { continue; } if (DefinitionsGround.FileNumberFromAlgebraicName(moveStringLower[0]) == BoardGround.FileFrom128(moveFromIndex)) // correct starting file { if (moveString.Contains("=")) // is promotion { char promotionChar = moveStringLower[moveStringLower.Length - 1]; int promotionPieceIndex = move >> 14 & 3; int promotionPieceCode = BoardGround.pieceCodeArray [promotionPieceIndex]; if ((promotionPieceCode == BoardGround.queenCode && promotionChar != 'q') || (promotionPieceCode == BoardGround.rookCode && promotionChar != 'r') || (promotionPieceCode == BoardGround.bishopCode && promotionChar != 'b') || (promotionPieceCode == BoardGround.knightCode && promotionChar != 'n')) { continue; // skip this move, incorrect promotion type } break; } else { char targetFile = moveString[moveString.Length - 2]; char targetRank = moveString[moveString.Length - 1]; if (DefinitionsGround.FileNumberFromAlgebraicName(targetFile) == BoardGround.FileFrom128(moveToIndex)) // correct ending file { if (DefinitionsGround.RankNumberFromAlgebraicName(targetRank) == BoardGround.RankFrom128(moveToIndex)) // correct ending rank { break; } } } } } else // regular piece move { char movePieceChar = moveString[0]; if (!(movePieceType == BoardGround.queenCode && movePieceChar == 'Q') && !(movePieceType == BoardGround.rookCode && movePieceChar == 'R') && !(movePieceType == BoardGround.bishopCode && movePieceChar == 'B') && !(movePieceType == BoardGround.knightCode && movePieceChar == 'N') && !(movePieceType == BoardGround.kingCode && movePieceChar == 'K')) { continue; // skip this move, incorrect move piece type } char targetFile = moveString[moveString.Length - 2]; char targetRank = moveString[moveString.Length - 1]; if (DefinitionsGround.FileNumberFromAlgebraicName(targetFile) == BoardGround.FileFrom128(moveToIndex)) // correct ending file { if (DefinitionsGround.RankNumberFromAlgebraicName(targetRank) == BoardGround.RankFrom128(moveToIndex)) // correct ending rank { if (moveString.Length == 4) // addition char present for disambiguation (e.g. Nbd7 or R7e2) { char disambiguationChar = moveString[1]; if (DefinitionsGround.fileNames.Contains(disambiguationChar + "")) // is file disambiguation { if (DefinitionsGround.FileNumberFromAlgebraicName(disambiguationChar) != BoardGround.FileFrom128(moveFromIndex)) // incorrect starting file { continue; } } else // is rank disambiguation { if (DefinitionsGround.RankNumberFromAlgebraicName(disambiguationChar) != BoardGround.RankFrom128(moveFromIndex)) // incorrect starting rank { continue; } } } break; } } } } if (move == 0) // move is illegal; discard and return moves up to this point { UnityEngine.Debug.Log(moveString); break; } else { allMoves.Add(move); } BoardGround.MakeMove(move); } for (int i = allMoves.Count - 1; i >= 0; i--) { BoardGround.UnmakeMove(allMoves[i]); } return(allMoves); }
public static int Evaluate() { TimerGround.Start("Eval"); int materialEval = 0; int mobilityEval = 0; int developmentEval = 0; int kingSafetyEval = 0; // piece index vars (assigned when found to be used in later calculations) int whiteKingSquareIndex = -1; int blackKingSquareIndex = -1; List <int> whiteQueenIndices = new List <int> (2); List <int> whiteRookIndices = new List <int> (2); List <int> whiteKnightIndices = new List <int> (2); List <int> whiteBishopIndices = new List <int> (2); List <int> whitePawnIndices = new List <int> (8); List <int> blackQueenIndices = new List <int> (2); List <int> blackRookIndices = new List <int> (2); List <int> blackKnightIndices = new List <int> (2); List <int> blackBishopIndices = new List <int> (2); List <int> blackPawnIndices = new List <int> (8); for (int squareIndex = 0; squareIndex <= 127; squareIndex++) { if ((squareIndex & 8) != 0) // don't look at indices which are not on the real board { continue; } if (BoardGround.boardArray[squareIndex] != 0) { int pieceCode = BoardGround.boardArray[squareIndex]; if (pieceCode == BoardGround.kingCode + 1) // found white king { whiteKingSquareIndex = squareIndex; } else if (pieceCode == BoardGround.kingCode) // found black king { blackKingSquareIndex = squareIndex; } else // non-king pieces { materialEval += pieceValues[pieceCode]; // count material (excluding kings) switch (pieceCode) { case BoardGround.queenCode + 1: whiteQueenIndices.Add(squareIndex); break; case BoardGround.rookCode + 1: whiteRookIndices.Add(squareIndex); break; case BoardGround.knightCode + 1: whiteKnightIndices.Add(squareIndex); break; case BoardGround.bishopCode + 1: whiteBishopIndices.Add(squareIndex); break; case BoardGround.pawnCode + 1: whitePawnIndices.Add(squareIndex); break; case BoardGround.queenCode: blackQueenIndices.Add(squareIndex); break; case BoardGround.rookCode: blackRookIndices.Add(squareIndex); break; case BoardGround.knightCode: blackKnightIndices.Add(squareIndex); break; case BoardGround.bishopCode: blackBishopIndices.Add(squareIndex); break; case BoardGround.pawnCode: blackPawnIndices.Add(squareIndex); break; } } } } if (whiteKingSquareIndex == -1) // return best score for black if white's king has been captured (this may sometimes be allowed during alphabeta search for faster move generation) //return int.MinValue; { } // piece mobility moveGenerator.SetMoveColour(1); mobilityEval += moveGenerator.GetMoves(false, true, false).Count; // white piece mobility mobilityEval += moveGenerator.GetMoves(true, true, false).Count; // white piece attacking black moveGenerator.SetMoveColour(0); mobilityEval -= moveGenerator.GetMoves(false, true, false).Count; // black piece mobility mobilityEval -= moveGenerator.GetMoves(true, true, false).Count; // black piece attacking white // piece development white for (int i = 0; i < whiteKnightIndices.Count; i++) { if (BoardGround.RankFrom128(whiteKnightIndices[i]) == 1) // penalize knight remaining on first rank { developmentEval -= 50; } else if (BoardGround.RankFrom128(whiteKnightIndices[i]) == 2) // penalize knight remaining on second rank { developmentEval -= 10; } if (BoardGround.FileFrom128(whiteKnightIndices[i]) == 1) // knights on the rim are dim { developmentEval -= 5; } else if (BoardGround.FileFrom128(whiteKnightIndices[i]) == 8) // knights on the rim are dim { developmentEval -= 5; } } for (int i = 0; i < whiteBishopIndices.Count; i++) { if (BoardGround.RankFrom128(whiteBishopIndices[i]) == 1) // penalize bishop remaining on first rank { developmentEval -= 50; } } // piece development black for (int i = 0; i < blackKnightIndices.Count; i++) { if (BoardGround.RankFrom128(blackKnightIndices[i]) == 8) // penalize knight remaining on eighth rank { developmentEval += 50; } else if (BoardGround.RankFrom128(blackKnightIndices[i]) == 7) // penalize knight remaining on seventh rank { developmentEval += 10; } if (BoardGround.FileFrom128(blackKnightIndices[i]) == 1) // knights on the rim are dim { developmentEval += 5; } else if (BoardGround.FileFrom128(blackKnightIndices[i]) == 8) // knights on the rim are dim { developmentEval += 5; } } for (int i = 0; i < blackBishopIndices.Count; i++) { if (BoardGround.RankFrom128(blackBishopIndices[i]) == 8) // penalize bishop remaining on eighth rank { developmentEval += 50; } } // king safety white if (BoardGround.WhiteHasCastlingRights()) { kingSafetyEval += 10; // not safe, but at least retaining ability to castle } else { if (whiteKingSquareIndex == 6 || whiteKingSquareIndex == 7) // generally safe kingside squares for king (g1,h1) { kingSafetyEval += 50; for (int i = 0; i < whiteRookIndices.Count; i++) { if (BoardGround.FileFrom128(whiteRookIndices[i]) > 6) { kingSafetyEval -= 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } else if (whiteKingSquareIndex == 2 || whiteKingSquareIndex == 1 || whiteKingSquareIndex == 0) // generally safe queenside squares for king (a1,b1,c1) { kingSafetyEval += 50; for (int i = 0; i < whiteRookIndices.Count; i++) { if (BoardGround.FileFrom128(whiteRookIndices[i]) < 3) { kingSafetyEval -= 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } } // king safety black if (BoardGround.BlackHasCastlingRights()) { kingSafetyEval -= 10; // not safe, but at least retaining ability to castle } else { if (blackKingSquareIndex == 118 || blackKingSquareIndex == 119) // generally safe kingside squares for king (g8,h8) { kingSafetyEval -= 50; for (int i = 0; i < blackRookIndices.Count; i++) { if (BoardGround.FileFrom128(blackRookIndices[i]) > 6) { kingSafetyEval += 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } else if (blackKingSquareIndex == 114 || blackKingSquareIndex == 113 || blackKingSquareIndex == 112) // generally safe queenside squares for king (a8,b8,c8) { kingSafetyEval -= 50; for (int i = 0; i < blackRookIndices.Count; i++) { if (BoardGround.FileFrom128(blackRookIndices[i]) < 3) { kingSafetyEval += 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } } int openingMaterialCount = 16 * pawnValue + 4 * (rookValue + knightValue + bishopValue) + 2 * queenValue; int endgameMaterialCount = 2 * (rookValue + knightValue); //float gameStage = int finalEval = materialEval * 1000 + mobilityEval + kingSafetyEval + developmentEval; TimerGround.Stop("Eval"); return(finalEval); }
void Start() { Debug.LogError("Test"); BoardGround.SetPositionFromFen(DefinitionsGround.startFen, true); }
static void ProcessFiles(string[] files, int minGameLengthPly, int recordToPly) { BoardGround.SetPositionFromFen(DefinitionsGround.startFen); // Read pgns and convert to opening book dictionary for (int fileIndex = 0; fileIndex < files.Length; fileIndex++) { StreamReader reader = new StreamReader(files[fileIndex]); List <string> pgns = new List <string>(); // split text into array of pgn strings bool readingPGN = false; int pgnIndex = -1; bool finishedReadingPGN = false; while (reader.Peek() != -1) { string line = reader.ReadLine() + " "; if (line.Contains("[")) // comment line { finishedReadingPGN = false; readingPGN = false; continue; } else if (!finishedReadingPGN) { for (int charIndex = 0; charIndex < line.Length; charIndex++) { if (!readingPGN && line[charIndex] == '1') { readingPGN = true; pgns.Add(""); pgnIndex++; } if (readingPGN) { pgns[pgnIndex] += line[charIndex] + ""; if (pgns[pgnIndex].Split('.').Length * 2 > recordToPly) // only record the first n moves for opening book { finishedReadingPGN = true; break; } } } } } reader.Close(); // get moves from pgn files for (int i = 0; i < pgns.Count; i++) { string pgn = pgns[i]; if (pgn.Split('.').Length * 2 < minGameLengthPly) // don't record games that were shorter than minGameLengthPly. This is to avoid games where an opening distaster occured { continue; } List <string> moveStrings = PGNReaderGround.MoveStringsFromPGN(pgn); List <ushort> moves = PGNReaderGround.MovesFromPGN(pgn); for (int j = 0; j < moves.Count; j++) { if (!book.ContainsKey(BoardGround.zobristKey)) { keys.Add(BoardGround.zobristKey); book.Add(BoardGround.zobristKey, new List <ushort>()); } if (!book[BoardGround.zobristKey].Contains(moves[j])) { book[BoardGround.zobristKey].Add(moves[j]); } BoardGround.MakeMove(moves[j]); } for (int k = moves.Count - 1; k >= 0; k--) { BoardGround.UnmakeMove(moves[k]); } } } }