/// <summary> /// The user has released a dragged piece. Verify that its a legal move, if so perform the move and perform the next move if appropriate mode. /// </summary> /// <param name="piece"></param> private void _pieceUp(cgChessPieceScript piece) { if (_downPiece != null) { if (playerCanMove || Mode == BoardMode.PlayerVsPlayer) { cgSimpleMove legalMove = null; cgSquareScript closestSquare = _findSquareAt(_downPiece.transform.position); List <cgSimpleMove> legalMoves = _abstractBoard.findLegalMoves(whiteTurnToMove); foreach (cgSimpleMove move in legalMoves) { if (cgGlobal.SquareNames[move.from] == _downPiece.square.uniqueName && cgGlobal.SquareNames[move.to] == closestSquare.uniqueName) { legalMove = move; } } //test legality of move here. if (legalMove != null && _abstractBoard.verifyLegality(legalMove)) { //piece.moveToSquare(closestSquare); _makeMove(legalMove); if (Mode == BoardMode.PlayerVsEngine) { _engine.MakeMove(_abstractBoard, false, _engineCallback); } else if (Mode == BoardMode.EngineVsPlayer) { _engine.MakeMove(_abstractBoard, true, _engineCallback); } } else { piece.moveToSquare(piece.square); } } else { piece.moveToSquare(piece.square); } _downPiece = null; } else { piece.moveToSquare(piece.square); _downPiece = null; } if (highlightLastMove) {//revert colors if highlighting is active foreach (cgSquareScript square in _squares) { square.changeColor(square.startColor); } } }
/// <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> /// 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; } } }
public static string NotationFromMove(cgBoard _board, cgSimpleMove _move) { string str = ""; if (_board.moves.Count % 2 == 0) { str += (Math.Floor(_board.moves.Count / 2f) + 1).ToString() + ". "; } int typ = Mathf.Abs(_board.squares[_move.from]); List <cgSimpleMove> othermoves = _board.findLegalMoves(_board.whiteTurnToMove); List <cgSimpleMove> ambiguousMoves = new List <cgSimpleMove>(); foreach (cgSimpleMove othermove in othermoves) { if (othermove.to == _move.to && othermove.from != _move.from && Mathf.Abs(_board.squares[othermove.from]) == typ) { ambiguousMoves.Add(othermove); } } if (typ == 1 && _move.capturedType != 0) { str += cgGlobal.SquareNames[_move.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 && !(_move 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 (cgGlobal.SquareNames[ambiguousMove.from].Substring(0, 1) == cgGlobal.SquareNames[_move.from].Substring(0, 1)) { fileMatch = true; } if (cgGlobal.SquareNames[ambiguousMove.from].Substring(1, 1) == cgGlobal.SquareNames[_move.from].Substring(1, 1)) { rankMatch = true; } } if (!fileMatch) { str += cgGlobal.SquareNames[_move.from].Substring(0, 1); } else if (fileMatch && !rankMatch) { str += cgGlobal.SquareNames[_move.from].Substring(1, 1); } else if (fileMatch && rankMatch) { str += cgGlobal.SquareNames[_move.from]; } } if (_move.capturedType != 0) { str += "x"; } if (_move is cgCastlingMove) { if (_move.to == 2 || _move.to == 58) { str += "O-O-O"; } else { str += "O-O"; } } else { str += cgGlobal.SquareNames[_move.to]; } if (_move.promoted) { switch (_move.promotionType) { case 2: str += "=R"; break; case 3: str += "=N"; break; case 4: str += "=B"; break; case 5: str += "=Q"; break; default: break; } } return(str); }