void moveNextMove(bool _moveOneSide = false) { m_Missed = false; m_WhiteTurn = !m_WhiteTurn; m_Board.move(m_CurLine.moveList[m_CurMoveIdx++]); UpdateInfos(); if (m_Board.isChecked(m_Board.whiteTurnToMove)) { AudioSource.PlayClipAtPoint(Check_Sound, Vector3.zero, 1.0f); } else { AudioSource.PlayClipAtPoint(MovePiece_Sound, Vector3.zero, 1.0f); } if (checkEndLine()) { return; } Invoke("moveComputerMove", 1.5f); }
public void OnNextMove() { if (curMove >= moveList.Count) { return; } board.move(moveList[curMove]); UpdateInfos(); Wnd_Board.UpdateBoard(); curMove++; AudioSource.PlayClipAtPoint(MovePiece_Sound, Vector3.zero, 1.0f); }
/// <summary> /// Utilizing an AlphaBeta searching algorithm, we generate moves evaluate them, prune and decide which is best. /// https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning /// </summary> /// <param name="node">the move to analyze</param> /// <param name="depth">The max depth to search to, execution time increases exponentially the higher the depth</param> /// <param name="alpha"></param> /// <param name="beta"></param> /// <param name="maximizing"></param> /// <returns>The value of the provided node</returns> private int _alfaBeta(cgSimpleMove node, int depth, int alpha = int.MinValue, int beta = int.MaxValue, bool maximizing = true) { _board.move(node); if (depth == 0) { int val = _board.Evaluate(); _board.revert(); return(val); } if (maximizing) { int v = int.MinValue; List <cgSimpleMove> replies = _board.findLegalMoves(true); foreach (cgSimpleMove reply in replies) { int candidate = _alfaBeta(reply, depth - 1, alpha, beta, false); v = candidate > v ? candidate : v; alpha = alpha > v ? alpha : v; if (beta < alpha) { break; } } _board.revert(); return(v); } else { int v = int.MaxValue; List <cgSimpleMove> replies = _board.findLegalMoves(false); foreach (cgSimpleMove reply in replies) { int candidate = _alfaBeta(reply, depth - 1, alpha, beta, true); v = candidate < v ? candidate : v; if (beta > v) { beta = v; //node.bestResponse = reply; } if (beta < alpha) { break; } } _board.revert(); return(v); } }
/// <summary> /// Peform the provided move on the visual board and the abstract board, with no legality checks - thus should be performed prior to calling this. /// </summary> /// <param name="move"></param> private void _makeMove(cgSimpleMove move) { UnityEngine.Debug.Log("White: " + _abstractBoard.whiteTurnToMove); movesMade++; playSound(moveSound); _abstractBoard.move(move); _writeLog(move); //_abstractBoard.debugReadBoard(); if (_getPieceOn(_abstractBoard.SquareNames[move.to]) != null && !(move is cgCastlingMove)) { _setDeadPiece(_getPieceOn(_abstractBoard.SquareNames[move.to])); } cgChessPieceScript piece = _getPieceOn(_abstractBoard.SquareNames[move.from]); if (move.queened) { piece.SetType(_abstractBoard.squares[move.to] > 0 ? cgChessPieceScript.Type.WhiteQueen : cgChessPieceScript.Type.BlackQueen); } if (move is cgCastlingMove) { cgChessPieceScript piece2 = _getPieceOn(_abstractBoard.SquareNames[(move as cgCastlingMove).secondFrom]); if (piece2) { piece2.moveToSquare(_getSquare(_abstractBoard.SquareNames[(move as cgCastlingMove).secondTo])); } } else if (move is cgEnPassantMove) { cgChessPieceScript piece2 = _getPieceOn(_abstractBoard.SquareNames[(move as cgEnPassantMove).attackingSquare]); piece2.dead = true; } piece.moveToSquare(_getSquare(_abstractBoard.SquareNames[move.to])); whiteTurnToMove = _abstractBoard.whiteTurnToMove; _checkGameOver(); if (highlightLastMove) { //Color copyFrom = _getSquare(cgGlobal.SquareNames[move.to]).startColor; Color color = _getSquare(_abstractBoard.SquareNames[move.to]).recentMoveColor; _getSquare(_abstractBoard.SquareNames[move.to]).highlightTemporarily(color); } //Debug.Log("making move. " + _abstractBoard.whiteTurnToMove+" moves "+_abstractBoard.moves.Count); //UnityEngine.Debug.Log("Time elapsed: " + stopwatch.Elapsed); if (Mode == BoardMode.EngineVsEngine) { MakeEngineMove(_abstractBoard.duplicate(), _abstractBoard.whiteTurnToMove, _engineCallback); } }
/// <summary> /// Peform the provided move on the visual board and the abstract board, with no legality checks - thus should be performed prior to calling this. /// </summary> /// <param name="move"></param> private void _makeMove(cgSimpleMove move) { //Debug.Log("making move:" + cgGlobal.MoveToString(move)); movesMade++; playSound(moveSound); _abstractBoard.move(move); _writeLog(move); //_abstractBoard.debugReadBoard(); if (_getPieceOn(cgGlobal.SquareNames[move.to]) != null) { _setDeadPiece(_getPieceOn(cgGlobal.SquareNames[move.to])); } cgChessPieceScript piece = _getPieceOn(cgGlobal.SquareNames[move.from]); piece.moveToSquare(_getSquare(cgGlobal.SquareNames[move.to])); if (move.promoted) { piece.SetType(_abstractBoard.squares[move.to] > 0 ? cgChessPieceScript.Type.WhiteQueen : cgChessPieceScript.Type.BlackQueen); } if (move is cgCastlingMove) { cgChessPieceScript piece2 = _getPieceOn(cgGlobal.SquareNames[(move as cgCastlingMove).secondFrom]); if (piece2) { piece2.moveToSquare(_getSquare(cgGlobal.SquareNames[(move as cgCastlingMove).secondTo])); } } else if (move is cgEnPassantMove) { cgChessPieceScript piece2 = _getPieceOn(cgGlobal.SquareNames[(move as cgEnPassantMove).attackingSquare]); piece2.dead = true; } whiteTurnToMove = _abstractBoard.whiteTurnToMove; _checkGameOver(); if (highlightLastMove) { //Color copyFrom = _getSquare(cgGlobal.SquareNames[move.to]).startColor; Color color = _getSquare(cgGlobal.SquareNames[move.to]).recentMoveColor; _getSquare(cgGlobal.SquareNames[move.to]).highlightTemporarily(color); } }
/// <summary> /// Writes the full game notation from the current moves stored in Moves list. /// </summary> /// <param name="type">What notationtype should it be?</param> /// <param name="formatType">Should it be PGN format or not?</param> /// <returns>A string with full game notation.</returns> public string writeFullNotation(NotationType type, FormatType formatType = FormatType.None) { string str = ""; cgBoard disambiguationBoard = board.duplicate().revertToStart(); if (type == NotationType.Coordinate) { foreach (cgSimpleMove pcem in moves) { str += (disambiguationBoard.SquareNames[pcem.from] + "-" + disambiguationBoard.SquareNames[pcem.to]) + " "; } } if (formatType == FormatType.PGN) { string q = "\""; str = " [Event " + q + "Pro Chess" + q + "]\n"; str += " [Site " + q + "Undefined site" + q + "]\n"; str += " [Date " + q + DateTime.Now + q + "]\n"; str += " [White " + q + "Chessplayer1" + q + "]\n"; str += " [Black " + q + "Chessplayer2" + q + "]\n"; str += " [Result " + q + "1/2-1/2" + q + "]\n"; } if (type == NotationType.Algebraic) { foreach (cgSimpleMove pcem in moves) { if (disambiguationBoard.moves.Count % 2 == 0) { str += (Math.Floor(disambiguationBoard.moves.Count / 2f) + 1).ToString() + ". "; } int typ = Math.Abs(disambiguationBoard.squares[pcem.from]); List <cgSimpleMove> othermoves = disambiguationBoard.findLegalMoves(disambiguationBoard.whiteTurnToMove); List <cgSimpleMove> ambiguousMoves = new List <cgSimpleMove>(); foreach (cgSimpleMove othermove in othermoves) { if (othermove.to == pcem.to && othermove.from != pcem.from && Math.Abs(disambiguationBoard.squares[othermove.from]) == typ) { ambiguousMoves.Add(othermove); } } if (typ == 1 && pcem.capturedType != 0) { str += disambiguationBoard.SquareNames[pcem.from].Substring(0, 1); } if (typ == 2) { str += "R"; } if (typ == 3) { str += "N"; } if (typ == 4) { str += "B"; } if (typ == 5) { str += "Q"; } if (typ == 6 && !(pcem is cgCastlingMove)) { str += "K"; } //if (typ == 6) str += "K"; if (ambiguousMoves.Count > 0 && typ != 1) { bool fileMatch = false; bool rankMatch = false; foreach (cgSimpleMove ambiguousMove in ambiguousMoves) { if (disambiguationBoard.SquareNames[ambiguousMove.from].Substring(0, 1) == disambiguationBoard.SquareNames[pcem.from].Substring(0, 1)) { fileMatch = true; } if (disambiguationBoard.SquareNames[ambiguousMove.from].Substring(1, 1) == disambiguationBoard.SquareNames[pcem.from].Substring(1, 1)) { rankMatch = true; } } if (!fileMatch) { str += disambiguationBoard.SquareNames[pcem.from].Substring(0, 1); } else if (fileMatch && !rankMatch) { str += disambiguationBoard.SquareNames[pcem.from].Substring(1, 1); } else if (fileMatch && rankMatch) { str += disambiguationBoard.SquareNames[pcem.from]; } } if (pcem.capturedType != 0) { str += "x"; } if (pcem is cgCastlingMove) { if (pcem.to == 2 || pcem.to == 58) { str += "O-O-O"; } else { str += "O-O"; } } else { str += disambiguationBoard.SquareNames[pcem.to]; } if (pcem.queened) { str += "=Q"; } str += " "; disambiguationBoard.move(pcem); } } return(str); }
/// <summary> /// Read the notations in the provided string. /// </summary> /// <param name="curgame">the string to decipher.</param> public void Read(string curgame) { int lastIndex = 0; int nextIndex = 0; string move = curgame.Substring(lastIndex, nextIndex); NotationType ntype = NotationType.Algebraic; //= NotationType.Coordinate; //Debug.Log("first move:" + move); //if (move.Contains("-")) ntype = NotationType.Coordinate; moves = new List <cgSimpleMove>(); if (ntype == NotationType.Coordinate) { while (lastIndex != -1) { byte fromp = board.IndexFromCellName(move.Substring(0, 2)); byte top = board.IndexFromCellName(move.Substring(3, 2)); moves.Add(new cgSimpleMove(fromp, top)); nextIndex = curgame.IndexOf(" ", lastIndex + 1); if (nextIndex == -1) { break; } move = curgame.Substring(lastIndex + 1, nextIndex - lastIndex); lastIndex = nextIndex; //Debug.Log("current move being analyzed="+move); } } else if (ntype == NotationType.Algebraic) { cgBoard disambBoard = new cgBoard(); cgSimpleMove chosenMove; while (lastIndex != -1) { chosenMove = null; nextIndex = curgame.IndexOf(" ", nextIndex + 1); if (nextIndex == -1 || lastIndex == -1) { break; } move = curgame.Substring(lastIndex + 1, nextIndex - lastIndex); bool legitMove = (!move.Contains(".") && move.Length > 1 && !move.Contains("\n")) ? true : false; move = move.Trim(' '); //move = move.Trim('\n'); //Debug.Log("trimmed:" + move+" contains .:"+move.Contains(".")+" contains newline:"+move.Contains("\n")+" legit move:"+legitMove); if (move.Contains("{")) { nextIndex = curgame.IndexOf("}", lastIndex + 1); } else if (move.Contains("[")) { nextIndex = curgame.IndexOf("]", lastIndex + 1); } else if (legitMove) { //Debug.Log("found to be legit move."); byte tosquare; byte pushback = 2; byte type = 1; //bool promotion = false; bool shortCastling = (move == "O-O"); bool longCastling = (move == "O-O-O"); if (move.Contains("=")) { //promotion = true; move.Remove(move.IndexOf("="), 2); } else if (move.Contains("+")) { move.Remove(move.IndexOf("+"), 1); } else if (move.Contains("!")) { move.Remove(move.IndexOf("!"), 1); } else if (move.Contains("?")) { move.Remove(move.IndexOf("?"), 1); } tosquare = board.IndexFromCellName(move.Substring(move.Length - pushback, 2)); if (move[0] == 'R') { type = 2; } if (move[0] == 'N') { type = 3; } if (move[0] == 'B') { type = 4; } if (move[0] == 'Q') { type = 5; } if (move[0] == 'K') { type = 6; } List <cgSimpleMove> ambiguousMoves = new List <cgSimpleMove>(); foreach (cgSimpleMove legalMove in disambBoard.findLegalMoves(disambBoard.whiteTurnToMove)) { if (shortCastling && legalMove is cgCastlingMove) { if (legalMove.to == 6 || legalMove.to == 62) { chosenMove = legalMove; break; } } else if (longCastling && legalMove is cgCastlingMove) { if (legalMove.to == 2 || legalMove.to == 58) { chosenMove = legalMove; break; } } if (Math.Abs(disambBoard.squares[legalMove.from]) == type && legalMove.to == tosquare) { ambiguousMoves.Add(legalMove); } } if (ambiguousMoves.Count == 0 && chosenMove == null) { //Debug.WriteLine("found no matching move for the string: " + move+" type:"+type+" tosquare:"+tosquare+" chosenMove:"+chosenMove+" castling:"+shortCastling); break; } else if (ambiguousMoves.Count == 1) { chosenMove = ambiguousMoves[0]; } else if (ambiguousMoves.Count > 1) { //UnityEngine.Debug.Log("2 or mroe ambiguousmoves"); //2 or more ambiguous moves in which the piece type matches and the destination square matches. Further disambiguation needed. List <cgSimpleMove> matching = new List <cgSimpleMove>(); foreach (cgSimpleMove mov in ambiguousMoves) { if (board.SquareNames[mov.from].Contains(move.Substring(1 + (type == 1?-1:0), 1))) { matching.Add(mov); } } if (matching.Count == 1) { chosenMove = matching[0]; //only 1 of the ambiguous moves have the correct rank and/or file. } else { foreach (cgSimpleMove mov in ambiguousMoves) { if (board.SquareNames[mov.from].Contains(move.Substring(1, 2))) { chosenMove = ambiguousMoves[0]; break; } } } } if (chosenMove != null) { disambBoard.move(chosenMove); moves.Add(chosenMove); } } Debug.WriteLine("legitmove:" + legitMove); lastIndex = nextIndex; } } }