示例#1
0
        /// <summary>
        /// Moves a piece with the specified color and source/target positions and promotes to the new
        /// piece with the specified type.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="from">The source piece position.</param>
        /// <param name="to">The target piece position.</param>
        /// <param name="promotionPieceType">The promotion piece type.</param>
        public void Move(Color color, Position from, Position to, PieceType promotionPieceType)
        {
            Bitboard.Calculate(GeneratorMode.CalculateMoves | GeneratorMode.CalculateAttacks, false);
            CheckBitboardIntegrity();

            UpdateMovesCount(color);
            if (CheckIfGameHasEnded())
            {
                return;
            }

            var possibleMovesToApply = Bitboard.Moves
                                       .OfType <PromotionMove>()
                                       .FirstOrDefault(p => p.From == from &&
                                                       p.To == to &&
                                                       p.PromotionPiece == promotionPieceType);

            // Temporary but I think that's not necessary
            if (possibleMovesToApply == null)
            {
                Console.WriteLine($"{from} {to} not found");
                return;
            }

            Bitboard = Bitboard.Move(possibleMovesToApply);
            _history.Add(possibleMovesToApply);
        }
        /// <summary>
        /// Applies friendly board to the bitboard and updates the visual board.
        /// </summary>
        /// <param name="friendlyBoard">The friendly board to apply.</param>
        /// <param name="whiteMode">The white generator mode.</param>
        /// <param name="blackMode">The black generator mode.</param>
        /// <param name="quiescenceSearch">If true, only quiescence moves (mainly captures) will be generated.</param>
        protected void CalculateBitboard(FriendlyBoard friendlyBoard, GeneratorMode whiteMode, GeneratorMode blackMode, bool quiescenceSearch)
        {
            Bitboard = new Bitboard(friendlyBoard);
            Bitboard.Calculate(whiteMode, blackMode, quiescenceSearch);

            VisualBoard.FriendlyBoard = new FriendlyBoard(Bitboard);
        }
示例#3
0
        public int Do(Color color, Bitboard bitboard, int alpha, int beta, AIStats stats)
        {
            var enemyColor = ColorOperations.Invert(color);
            var colorSign  = ColorOperations.ToSign(color);

            stats.QuiescenceTotalNodes++;

            var whiteGeneratorMode = GetGeneratorMode(color, Color.White);
            var blackGeneratorMode = GetGeneratorMode(color, Color.Black);

            bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, true);

            if (bitboard.IsCheck(enemyColor))
            {
                stats.QuiescenceEndNodes++;
                return(AIConstants.MateValue);
            }

            var evaluation = colorSign * bitboard.GetEvaluation();

            if (evaluation >= beta)
            {
                stats.QuiescenceEndNodes++;
                return(beta);
            }

            if (evaluation > alpha)
            {
                alpha = evaluation;
            }

            var sortedMoves = SortMoves(color, bitboard, bitboard.Moves);

            foreach (var move in sortedMoves)
            {
                var bitboardAfterMove = bitboard.Move(move);
                var nodeValue         = -Do(enemyColor, bitboardAfterMove, -beta, -alpha, stats);

                if (nodeValue >= beta)
                {
                    return(beta);
                }

                if (nodeValue > alpha)
                {
                    alpha = nodeValue;
                }
            }

            if (!sortedMoves.Any())
            {
                stats.QuiescenceEndNodes++;
            }

            return(alpha);
        }
示例#4
0
        /// <summary>
        /// Recursive method for calculating bitboard. If depth is equal or less than zero, then
        /// current node is the last and next CalculateBitboard call will not be executed.
        /// </summary>
        /// <param name="color">Color of the current player.</param>
        /// <param name="bitboard">Currently calculated bitboard</param>
        /// <param name="depth">Current depth</param>
        /// <param name="calculateEndNodes">If true, every end node will calculate attacks and evaluation function.</param>
        /// <param name="verifyIntegrity">If true, every board will be checked whether incremental-updating parameters are correctly calculated.</param>
        /// <param name="testData">Container for test data which will be returned when test is done.</param>
        private void CalculateBitboard(Color color, Bitboard bitboard, int depth, bool calculateEndNodes, bool verifyIntegrity, MovesTestData testData)
        {
            if (verifyIntegrity)
            {
                bitboard.Calculate(GeneratorMode.CalculateAttacks, false);
                if (!bitboard.VerifyIntegrity())
                {
                    testData.Integrity = false;

                    var boardWriter = new BoardWriter();
                    boardWriter.Write("Boards/error.board", new FriendlyBoard(bitboard));
                }
            }

            if (depth <= 0)
            {
                if (calculateEndNodes)
                {
                    bitboard.Calculate(GeneratorMode.CalculateAttacks, false);
                    bitboard.GetEvaluation();
                }

                testData.EndNodes++;
            }
            else
            {
                var enemyColor = ColorOperations.Invert(color);
                var whiteMode  = GetGeneratorMode(color);
                var blackMode  = GetGeneratorMode(enemyColor);

                bitboard.Calculate(whiteMode, blackMode, false);

                foreach (var move in bitboard.Moves)
                {
                    var bitboardAfterMove = bitboard.Move(move);

                    CalculateBitboard(enemyColor, bitboardAfterMove, depth - 1, calculateEndNodes, verifyIntegrity, testData);
                }
            }

            testData.TotalNodes++;
        }
示例#5
0
        /// <summary>
        /// Runs AI and does best found move.
        /// </summary>
        /// <param name="color">The engine color.</param>
        /// <returns>The AI result.</returns>
        public AIResult MoveAI(Color color)
        {
            Bitboard.Calculate(GeneratorMode.CalculateMoves | GeneratorMode.CalculateAttacks, false);
            CheckBitboardIntegrity();

            UpdateMovesCount(color);
            if (CheckIfGameHasEnded())
            {
                return(null);
            }

            var openingBookMove = _openingBook.GetMoveFromBook(_history);

            if (openingBookMove != null)
            {
                var moveToApply =
                    Bitboard.Moves.First(p => p.From == openingBookMove.From && p.To == openingBookMove.To);

                Bitboard = Bitboard.Move(moveToApply);
                _history.Add(moveToApply);

                return(new AIResult()
                {
                    PVNodes = new PVNodesList()
                    {
                        moveToApply
                    }
                });
            }
            else
            {
                var preferredTime = _preferredTimeCalculator.Calculate(MovesCount, _remainingTime[(int)color]);
                var aiResult      = _aiCore.Calculate(color, Bitboard, preferredTime);

                // Temporary
                if (aiResult.PVNodes.Count < 1)
                {
                    Console.WriteLine($"Move not found");
                    return(null);
                }

                Bitboard = Bitboard.Move(aiResult.PVNodes[0]);
                _history.Add(aiResult.PVNodes[0]);

                return(aiResult);
            }
        }
示例#6
0
        /// <summary>
        /// Moves a piece with the specified color and source/target positions.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="from">The source piece position.</param>
        /// <param name="to">The target piece position.</param>
        public void Move(Color color, Position from, Position to)
        {
            Bitboard.Calculate(GeneratorMode.CalculateMoves | GeneratorMode.CalculateAttacks, false);
            CheckBitboardIntegrity();

            UpdateMovesCount(color);
            CheckIfGameHasEnded();
            if (CheckIfGameHasEnded())
            {
                return;
            }

            var moveToApply = Bitboard.Moves.FirstOrDefault(p => p.From == from && p.To == to);

            Bitboard = Bitboard.Move(moveToApply);
            _history.Add(moveToApply);
        }
示例#7
0
        public List <Move> ParseMoves(List <string> moves)
        {
            var parsedMoves = new List <Move>();

            var bitboard     = new Bitboard(new DefaultFriendlyBoard());
            var currentColor = Color.White;

            foreach (var move in moves)
            {
                bitboard.Calculate(GeneratorMode.CalculateMoves, false);

                var parsedMove = GetMove(bitboard, currentColor, move);
                bitboard = bitboard.Move(parsedMove);
                parsedMoves.Add(parsedMove);

                currentColor = ColorOperations.Invert(currentColor);
            }

            return(parsedMoves);
        }
示例#8
0
        /// <summary>
        /// Moves a piece with the specified color and source/target positions and promotes to the new
        /// piece with the specified type.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="from">The source piece position.</param>
        /// <param name="to">The target piece position.</param>
        /// <param name="promotionPieceType">The promotion piece type.</param>
        public void Move(Color color, Position from, Position to, PieceType promotionPieceType)
        {
            Bitboard.Calculate(GeneratorMode.CalculateMoves | GeneratorMode.CalculateAttacks, false);
            CheckBitboardIntegrity();

            UpdateMovesCount(color);
            if (CheckIfGameHasEnded())
            {
                return;
            }

            var possibleMovesToApply = Bitboard.Moves
                                       .OfType <PromotionMove>()
                                       .FirstOrDefault(p => p.From == from &&
                                                       p.To == to &&
                                                       p.PromotionPiece == promotionPieceType);

            Bitboard = Bitboard.Move(possibleMovesToApply);
            _history.Add(possibleMovesToApply);
        }
示例#9
0
        /// <summary>
        /// Moves a piece with the specified color and source/target positions.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="from">The source piece position.</param>
        /// <param name="to">The target piece position.</param>
        public void Move(Color color, Position from, Position to)
        {
            Bitboard.Calculate(GeneratorMode.CalculateMoves | GeneratorMode.CalculateAttacks, false);
            CheckBitboardIntegrity();

            UpdateMovesCount(color);
            CheckIfGameHasEnded();
            if (CheckIfGameHasEnded())
            {
                return;
            }

            // Temporary but I think that's not necessary
            var moveToApply = Bitboard.Moves.FirstOrDefault(p => p.From == from && p.To == to);

            if (moveToApply == null)
            {
                Console.WriteLine($"{from} {to} not found");
                return;
            }

            Bitboard = Bitboard.Move(moveToApply);
            _history.Add(moveToApply);
        }
示例#10
0
        /// <summary>
        /// Temporary method to calculating best move.
        /// </summary>
        /// <param name="color">The player color.</param>
        /// <param name="bitboard">The bitboard.</param>
        /// <param name="depth">The current depth.</param>
        /// <param name="bestMove">The best possible move from nested nodes.</param>
        /// <param name="stats">The AI stats.</param>
        /// <returns>The evaluation score of best move.</returns>
        public int Do(Color color, Bitboard bitboard, int depth, int alpha, int beta, AIStats stats)
        {
            var bestValue     = AIConstants.InitialAlphaValue;
            var enemyColor    = ColorOperations.Invert(color);
            var boardHash     = bitboard.GetHashForColor(color);
            var originalAlpha = alpha;

            stats.TotalNodes++;

            if (bitboard.IsThreefoldRepetition())
            {
                stats.EndNodes++;
                return(0);
            }

            if (_transpositionTable.Exists(boardHash))
            {
                var transpositionNode = _transpositionTable.Get(boardHash);

                if (transpositionNode.Depth >= depth)
                {
                    stats.TranspositionTableHits++;
                    switch (transpositionNode.Type)
                    {
                    case ScoreType.Exact:
                    {
                        return(transpositionNode.Score);
                    }

                    case ScoreType.LowerBound:
                    {
                        alpha = Math.Max(alpha, transpositionNode.Score);
                        break;
                    }

                    case ScoreType.UpperBound:
                    {
                        beta = Math.Min(beta, transpositionNode.Score);
                        break;
                    }
                    }

                    if (alpha >= beta)
                    {
                        return(transpositionNode.Score);
                    }
                }
            }

            if (depth <= 0)
            {
                stats.EndNodes++;
                return(_quiescenceSearch.Do(color, bitboard, alpha, beta, stats));
            }

            var whiteGeneratorMode = GetGeneratorMode(color, Color.White);
            var blackGeneratorMode = GetGeneratorMode(color, Color.Black);

            bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, false);

            if (bitboard.IsCheck(enemyColor))
            {
                stats.EndNodes++;
                return(AIConstants.MateValue + depth);
            }

            Move bestMove = null;

            var availableMoves = SortMoves(color, bitboard, bitboard.Moves);
            var firstMove      = true;

            foreach (var move in availableMoves)
            {
                var bitboardAfterMove = bitboard.Move(move);
                var nodeValue         = 0;

                if (firstMove)
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, stats);
                    firstMove = false;
                }
                else
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -alpha - 1, -alpha, stats);

                    if (nodeValue > alpha && nodeValue < beta)
                    {
                        bitboardAfterMove = bitboard.Move(move);
                        nodeValue         = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, stats);
                    }
                }

                if (nodeValue > bestValue)
                {
                    bestValue = nodeValue;
                    bestMove  = move;
                }

                alpha = Math.Max(nodeValue, alpha);

                if (alpha >= beta)
                {
                    stats.AlphaBetaCutoffs++;
                    break;
                }
            }

            if (bestValue == -(AIConstants.MateValue + depth - 1) && !bitboard.IsCheck(color))
            {
                stats.EndNodes++;
                return(0);
            }

            var updateTranspositionNode = new TranspositionNode();

            updateTranspositionNode.Score    = bestValue;
            updateTranspositionNode.Depth    = depth;
            updateTranspositionNode.BestMove = bestMove;

            if (bestValue <= originalAlpha)
            {
                updateTranspositionNode.Type = ScoreType.UpperBound;
            }
            else if (bestValue >= beta)
            {
                updateTranspositionNode.Type = ScoreType.LowerBound;
            }
            else
            {
                updateTranspositionNode.Type = ScoreType.Exact;
            }

            _transpositionTable.AddOrUpdate(boardHash, updateTranspositionNode);

            return(bestValue);
        }
        /// <summary>
        /// Regular search, the core of AI algorithms.
        /// </summary>
        /// <param name="color">The player color.</param>
        /// <param name="bitboard">The bitboard.</param>
        /// <param name="depth">The current depth.</param>
        /// <param name="alpha">The alpha value.</param>
        /// <param name="beta">The beta value.</param>
        /// <param name="deadline">The deadline (time after which search is immediately terminated).</param>
        /// <param name="helper">The flag indicating whether the search is an helper or not.</param>
        /// <param name="stats">The AI stats.</param>
        /// <returns>The evaluation score of best move.</returns>
        public int Do(Color color, Bitboard bitboard, int depth, int alpha, int beta, long deadline, bool helper, AIStats stats)
        {
            var root = stats.TotalNodes == 0;

            var bestValue     = AIConstants.InitialAlphaValue;
            var enemyColor    = ColorOperations.Invert(color);
            var boardHash     = bitboard.GetHashForColor(color);
            var originalAlpha = alpha;

            stats.TotalNodes++;

            if (bitboard.IsThreefoldRepetition())
            {
                stats.EndNodes++;
                return(0);
            }

#if TRANSPOSITION_TABLE
            if (_transpositionTable.Exists(boardHash))
            {
                var transpositionNode = _transpositionTable.Get(boardHash);

                if (transpositionNode.Depth >= depth)
                {
                    stats.TranspositionTableHits++;
                    switch (transpositionNode.Type)
                    {
                    case ScoreType.Exact:
                    {
                        return(transpositionNode.Score);
                    }

                    case ScoreType.LowerBound:
                    {
                        alpha = Math.Max(alpha, transpositionNode.Score);
                        break;
                    }

                    case ScoreType.UpperBound:
                    {
                        beta = Math.Min(beta, transpositionNode.Score);
                        break;
                    }
                    }

                    if (alpha >= beta)
                    {
                        return(transpositionNode.Score);
                    }
                }
            }
#endif

            if (depth <= 0)
            {
                stats.EndNodes++;
#if QUIESCENCE_SEARCH
                return(_quiescenceSearch.Do(color, bitboard, alpha, beta, stats));
#else
                bitboard.Calculate(GeneratorMode.CalculateAttacks, false);
                return(bitboard.GetEvaluation());
#endif
            }

            var whiteGeneratorMode = GetGeneratorMode(color, Color.White);
            var blackGeneratorMode = GetGeneratorMode(color, Color.Black);
            bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, false);

            if (bitboard.IsCheck(enemyColor))
            {
                stats.EndNodes++;
                return(AIConstants.MateValue + depth);
            }

            Move bestMove = null;

            var availableMoves = SortMoves(color, depth, bitboard, bitboard.Moves, helper);
            var firstMove      = true;

            foreach (var move in availableMoves)
            {
                if (DateTime.Now.Ticks >= deadline)
                {
                    break;
                }

                if (root)
                {
                    if (_patternsDetector.IsPattern(bitboard, move))
                    {
                        continue;
                    }
                }

                var bitboardAfterMove = bitboard.Move(move);
                var nodeValue         = 0;

                if (firstMove)
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, deadline, helper, stats);
#if NEGASCOUT
                    firstMove = false;
#endif
                }
                else
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -alpha - 1, -alpha, deadline, helper, stats);

                    if (nodeValue > alpha && nodeValue < beta)
                    {
                        bitboardAfterMove = bitboard.Move(move);
                        nodeValue         = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, deadline, helper, stats);
                    }
                }

                if (nodeValue > bestValue)
                {
                    bestValue = nodeValue;
                    bestMove  = move;
                }

                alpha = Math.Max(nodeValue, alpha);

                if (alpha >= beta)
                {
                    if (move is QuietMove)
                    {
                        _historyTable.AddKiller(color, depth, bestMove);
                        _killerTable.AddKiller(depth, move);
                    }

#if ALPHABETA_PRUNNING
                    stats.AlphaBetaCutoffs++;
                    break;
#endif
                }
            }

            if (bestValue == -(AIConstants.MateValue + depth - 1) && !bitboard.IsCheck(color))
            {
                stats.EndNodes++;
                return(0);
            }

            var updateTranspositionNode = new TranspositionNode
            {
                Score    = bestValue,
                Depth    = depth,
                BestMove = bestMove,
                Type     = GetTranspositionNodeType(originalAlpha, beta, bestValue)
            };

            _transpositionTable.AddOrUpdate(boardHash, updateTranspositionNode);

            return(bestValue);
        }
示例#12
0
        public async Task Chess(CommandContext ctx, [Description("`new` aby rozpocząć grę lub ruch (np. `e2e4`)")] string action = null)
        {
            if (action == null)
            {
                await ctx.RespondAsync("Użycie: `!szachy new` aby rozpocząć nową grę lub `!szachy e2e4` aby wykonać ruch.");
            }
            else if (action == "new")
            {
                _selectedPositions.Clear();
                _messageIds.Clear();

                CreateSession();

                var boardMessage = await ctx.RespondAsync(new DiscordMessageBuilder().WithContent("**Nowa gra utworzona:**").WithFile("board.png", GetBoardImage()));

                _messageIds.Add(boardMessage.Id);
            }
            else
            {
                _gameSession.UpdateRemainingTime(Color.White, 200);
                _gameSession.UpdateRemainingTime(Color.Black, 200);

                Position fromPosition, toPosition;
                try
                {
                    var from = action.Substring(0, 2);
                    var to   = action.Substring(2, 2);

                    fromPosition = PositionConverter.ToPosition(from);
                    toPosition   = PositionConverter.ToPosition(to);
                }
                catch (Exception e)
                {
                    await ctx.RespondAsync("Nieprawidłowy ruch");

                    return;
                }

                var moveValidationBitboard = new Bitboard(_gameSession.Bitboard);
                moveValidationBitboard.Calculate(GeneratorMode.CalculateAttacks | GeneratorMode.CalculateMoves, false);

                var move = moveValidationBitboard.Moves.FirstOrDefault(p => p.From == fromPosition && p.To == toPosition);
                if (move == null)
                {
                    await ctx.RespondAsync("Nieprawidłowy ruch");

                    return;
                }

                var validationBitboardAfterMove = moveValidationBitboard.Move(move);
                validationBitboardAfterMove.Calculate(false);

                if (validationBitboardAfterMove.IsCheck(Color.White))
                {
                    await ctx.RespondAsync("Invalid move");

                    return;
                }


                foreach (var msgIdToDelete in _messageIds)
                {
                    var messageToDelete = await ctx.Channel.GetMessageAsync(msgIdToDelete);

                    await ctx.Channel.DeleteMessageAsync(messageToDelete);
                }

                _messageIds.Clear();

                _gameSession.Move(Color.White, fromPosition, toPosition);

                _selectedPositions.Clear();
                _selectedPositions.Add(fromPosition);
                _selectedPositions.Add(toPosition);

                var playerBoardMsg = await ctx.RespondAsync(new DiscordMessageBuilder().WithContent("**Ruch gracza:**").WithFile("board.png", GetBoardImage()));

                _messageIds.Add(playerBoardMsg.Id);

                var thinkingMessage = await ctx.RespondAsync("Myślę...");

                var aiMove = _gameSession.MoveAI(Color.Black);

                _selectedPositions.Clear();
                _selectedPositions.Add(aiMove.PVNodes[0].From);
                _selectedPositions.Add(aiMove.PVNodes[0].To);

                await thinkingMessage.DeleteAsync();

                var aiBoardMsg = await ctx.RespondAsync(new DiscordMessageBuilder().WithContent("**Ruch AI:**").WithFile("board.png", GetBoardImage()));

                _messageIds.Add(aiBoardMsg.Id);
            }
        }