Exemplo n.º 1
0
    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());
    }
Exemplo n.º 2
0
    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);
    }