private IEnumerator GetEngineMove() { // This prevents the player from making a move during the calculation. isReady = false; ChangeText("Thinking...", ""); // This checks if it is stalemate for the engine. if (position.IsStalemate(color.Flip())) { StartCoroutine(HandleStrike("You lose!", "Stalemate!")); yield break; } // This checks if there are only kings on the board, which is always a draw. if (position.All(c => c == '_' || c.ToLower() == 'k')) { StartCoroutine(HandleStrike("You lose!", "Kings only!")); yield break; } yield return(_bestMove.Start(position, (MovesLeft * 2) + 1, color == PieceColor.Black)); // This looks convoluted, but it's only asking if the player has made a blunder, to the point of an unwinnable position. if (isWinning && ((color == PieceColor.White && _bestMove.Result.Evaluation != sbyte.MaxValue - (MovesLeft * 2)) || (color == PieceColor.Black && _bestMove.Result.Evaluation != (MovesLeft * 2) + sbyte.MinValue))) { isWinning = false; Log("Rustmate has evaluated that this position is now unwinnable for you. Congratulations."); } ChangeText("Mate in", "", MovesLeft.ToString()); PlaySound(Sounds._1dch.Opponent); // This indicates if the game has ended. if (_bestMove.Result.IsEqual(Position.finishedGame)) { isReady = false; PlaySound(Sounds._1dch.Check); // Stalemate. if (_bestMove.Result.Evaluation == 0) { StartCoroutine(HandleStrike("You lose!", "Stalemate!")); } // Checkmate against the player. else if (color == PieceColor.White ^ _bestMove.Result.Evaluation > 0) { StartCoroutine(HandleStrike("You lose!", "Checkmate!")); } // Checkmate for the player. else { PlaySound(Sounds._1dch.GameEnd, Sounds._1dch.Solve); string message = new[] { "Good game!", "Well played!" }.PickRandom(); Solve(message); ChangeText("Solved!", message); } } else { // This allows the player to make a move again. isReady = true; position = _bestMove .Result .Move(position, this); Log("Rustmate plays {0}, the position is now {1}.", ToLog(_bestMove.Result), position); souvenirPositions.Add(ToLog(_bestMove.Result)); // Having the game end after an engine move always means a loss for the player. if (position.IsGameEnd(color)) { StartCoroutine(HandleStrike("You lose!", position.IsStalemate(color) ? "Stalemate!" : "Checkmate!")); } // This checks if there are only kings on the board, which is always a draw. else if (position.All(c => c == '_' || c.ToLower() == 'k')) { StartCoroutine(HandleStrike("You lose!", "Kings only!")); } else if (MovesLeft == 0) { StartCoroutine(HandleStrike("You lose!", "Out of moves!")); } } RenderPosition(position); }
private IEnumerator GetGoodPosition() { isWinning = true; isReady = false; ChangeText("Waiting for", "other modules..."); // This waits for an arbitrary amount of time, to let other copies of this module through at different rates. yield return(new WaitForSecondsRealtime(URandom.Range(0, 2))); // This waits until another module that uses threads in this exact method is finished. yield return(new WaitWhile(() => _isUsingThreads)); _isUsingThreads = true; bool isEvaluating = false; string position = ""; ChangeText("Preparing...", "Please wait."); var game = new CGameResult { }; var moves = new List <CGameResult>(); new Thread(() => { // Find a game that takes 6-8 moves to complete in ideal play, using the Rust library. while (true) { position = RandomPosition; isEvaluating = true; game = Engine.Calculate(position, Position.Depth, true); if (!Mathf.Abs(game.Evaluation).InRange(112, 116)) { continue; } // Sometimes the evaluation is in favor of black, we need to advance the game by one move so that it is black to move. if (game.Evaluation < 0) { position = game.Move(position); } // Set the player side to always whichever one is winning. color = (PieceColor)Convert.ToInt32(game.Evaluation < 0); // Reset the move list. moves = new List <CGameResult>(); var colorMut = color; string positionMut = position; bool isGameCorrectlyOver = false; // The bot plays against itself until the perfect game is constructed. for (int depth = Position.Depth; depth > 0; depth--) { var gameMut = Engine.Calculate(positionMut, depth, colorMut == PieceColor.White); if ((gameMut.Evaluation == sbyte.MaxValue && color == PieceColor.White) || (gameMut.Evaluation == sbyte.MinValue && color == PieceColor.Black)) { isGameCorrectlyOver = true; } try { positionMut = gameMut.Move(positionMut); } // There are no moves to play when this exception is triggered. catch (IndexOutOfRangeException) { break; } colorMut = colorMut.Flip(); moves.Add(gameMut); } // Ensures that what it logs is indeed a checkmate. This reverifies that the puzzle is possible. if (isGameCorrectlyOver && moves.Count == 128 - Math.Abs(game.Evaluation) - (color == PieceColor.White ? 1 : 0)) { break; } } this.position = position; souvenirPositions = new List <string>(); isReady = true; }).Start(); // As long as the thread is running, it should generate and render random positions to distract the player. while (!isReady) { yield return(new WaitUntil(() => isEvaluating || isReady)); isEvaluating = false; RenderPosition(position); PlaySound(new[] { Sounds._1dch.Capture, Sounds._1dch.Check, Sounds._1dch.Opponent, Sounds._1dch.Self }.PickRandom()); } Log("The position is {0}; {1} to play, mate in {2}. To beat Rustmate, the best sequence of moves are {3}.", position, color, (128 - Math.Abs(game.Evaluation)) / 2, ToLog(moves)); MovesLeft = (128 - Math.Abs(game.Evaluation)) / 2; _isUsingThreads = false; PlaySound(Sounds._1dch.GameStart); ChangeText("Mate in", "", MovesLeft.ToString()); }
public int CompareTo(Score score) { return(MovesLeft.CompareTo(score.MovesLeft)); }