Пример #1
0
        public int Evaluate(SquareBoard board, Side side)
        {
            var enemySide = SideUtil.Opposite(side);

            var score = SideScore(board.GetAll(side)) - SideScore(board.GetAll(enemySide));

            return(score);
        }
Пример #2
0
        //HOWTO: make it possible to visualize search progress - let WPF to see each State and decisions?

        public BotMove FindBestMove(SquareBoard board, Side botSide, CancellationToken cancellation, BotOptions options = default)
        {
            this.options      = options ?? new BotOptions();
            this.botSide      = botSide;
            this.playerSide   = SideUtil.Opposite(botSide);
            this.cancellation = cancellation;

            return(Negamax(board, this.options.MaxDepth, Int32.MinValue + 1, Int32.MaxValue, botSide, CancellationToken.None));
        }
Пример #3
0
 private Point GetJumpPoint(Figure figure, Func <Point, int, Point> direction, out Figure neighbour)
 {
     neighbour = board.Get(direction(figure.Point, 1));
     if (neighbour.Side == SideUtil.Opposite(figure.Side))
     {
         var doubleStep = direction(figure.Point, 2);
         if (board.IsEmpty(doubleStep))
         {
             return(doubleStep);
         }
     }
     return(Point.Nop);
 }
Пример #4
0
 public VoxelLocation(RaycastHit hit, bool modifyBySideCollided = false)
 {
     if (!modifyBySideCollided)
     {
         x = hit.x;
         y = hit.y;
         z = hit.z;
     }
     else
     {
         x = SideUtil.modify_x(hit.x, hit.side);
         y = SideUtil.modify_y(hit.y, hit.side);
         z = SideUtil.modify_z(hit.z, hit.side);
     }
 }
Пример #5
0
        private void CheckForWin()
        {
            // TODO: extract it into Rules class - because there are a lot of draw cases (pretty complex)
            var validEnemyMoves = _rulesProvider.GetMoves(Board, SideUtil.Opposite(SideUtil.Convert(CurrentPlayer.Side)));

            if (validEnemyMoves.Count == 0)
            {
                _winnerIndex = CurrentPlayerIndex;
                Status       = _winnerIndex == 0 ? GameStatus.Player1Wins : GameStatus.Player2Wins;
                //_isRunning = false;
                //TODO: potentially it's ok to undo move after game is over?
                // - don't stop the game with _isRunning = false
                // - then Undo should unmark it?
                // - then to be able to make Redo should store it in History
                // etc.
            }
        }
Пример #6
0
        public IGameMove Choose(IGameMove[] moves, SquareBoard board)
        {
            var walkMoves = moves.Select(x => x as WalkGameMove).Where(x => x != null).ToArray();

            if (walkMoves.Length == 0)
            {
                return(null);
            }

            _cts = new CancellationTokenSource(TimeoutPerMoveMilliseconds);

            var depth = 8 * Math.Pow(TimeoutPerMoveMilliseconds / 1000, 0.4);
            var move  = _bot.FindBestMove(board, SideUtil.Convert(Side), _cts.Token, new NegaMaxBot.BotOptions {
                MaxDepth = (int)depth
            });

            return(walkMoves.FirstOrDefault(x => x.Figure == move.Figure && x.MoveSequence == move.Sequence));
        }
Пример #7
0
        private async Task GameLoop()
        {
            do
            {
                var validMoves = _rulesProvider.GetMoves(Board, SideUtil.Convert(CurrentPlayer.Side));
                var gameMoves  = new List <IGameMove>(validMoves.Values.Count);
                if (validMoves.Count > 0)
                {
                    gameMoves.AddRange(validMoves.Flatten((f, m) => new WalkGameMove(this, f, m)));
                }
                if (_currentHistoryItem.Previous != null && _currentHistoryItem.Previous.Previous != null)
                {
                    gameMoves.Add(UndoMove);
                }
                if (_currentHistoryItem.Next != null && _currentHistoryItem.Next.Next != null)
                {
                    gameMoves.Add(RedoMove);
                }

                try
                {
                    var move = await TryChooseMove(gameMoves);

                    if (move == null)
                    {
                        return;               //gameLoop was interrupted
                    }
                    move.Execute();
                    _gameStatistics.Append(move, this);
                }
                catch (Exception ex)
                {
                    Error      = ex?.InnerException ?? ex;
                    Status     = GameStatus.Error;
                    _isRunning = false;
                    break;
                }

                foreach (var player in _players)
                {
                    player.GameUpdated(this);
                }
            } while (_isRunning);
        }
Пример #8
0
 public Cell(Figure figure, GameSide playerSide)
 {
     _figure = figure;
     _active = figure.Side == SideUtil.Convert(playerSide);
 }
Пример #9
0
        // NegaMax algorithm with alpha/beta, source: https://en.wikipedia.org/wiki/Negamax
        //TODO: append transposition tables
        private BotMove Negamax(SquareBoard board, int depth, int alpha, int beta, Side side, CancellationToken branchToken)
        {
            if (cancellation.IsCancellationRequested ||
                !CanSearchDeeper(board, depth))
            {
                return(BotMove.Empty(Estimate(board)));
            }

            var states = GetStates(board, side);

            if (depth == options.MaxDepth && states.Count == 1)
            {
                var singleState = states[0];
                return(new BotMove(singleState.Figure, singleState.MoveSequence, 0));
            }

            BotMove bestMove = new BotMove(Int32.MinValue);

            var noMoves = true;
            var cts     = new CancellationTokenSource();
            var workers = new List <Task>();

            foreach (var state in states)
            {
                noMoves = false;
                if (cts.IsCancellationRequested)
                {
                    break;
                }
                if (branchToken.IsCancellationRequested)
                {
                    cts.Cancel();
                    break;
                }

                if (states.Count > 1 && _runningWorkersCount < options.DegreeOfParallelism && depth <= options.MaxDepth - 1)
                {
                    Interlocked.Increment(ref _runningWorkersCount);
                    workers.Add(Task.Run(() =>
                    {
                        DoNegamax(state);
                        Interlocked.Decrement(ref _runningWorkersCount);
                    }));
                }
                else
                {
                    DoNegamax(state);
                }
            }

            Task.WaitAll(workers.ToArray());

            if (noMoves)
            {
                return(BotMove.Empty(Estimate(board)));
            }

            return(bestMove);

            void DoNegamax(State state)
            {
                if (options.IsDebug)
                {
                    Log("before", board, side, state, bestMove.Score, depth, alpha, beta);
                }
                var boardAfterMove = new MoveCommandChain(state.Figure, state.Board, state.MoveSequence).Execute();
                var score          = -Negamax(boardAfterMove, depth - 1, -beta, -alpha, SideUtil.Opposite(side), cts.Token).Score;
                var shouldPrun     = false;

                lock (locker)
                {
                    if (score > bestMove.Score)
                    {
                        bestMove = new BotMove(state.Figure, state.MoveSequence, score);
                    }
                    alpha = Math.Max(alpha, bestMove.Score);
                    //let's move cts.Cancel out of the lock scope
                    shouldPrun = options.AllowPrunning && alpha >= beta;
                }
                if (options.IsDebug)
                {
                    Log("after", board, side, state, score, depth, alpha, beta);
                }
                if (shouldPrun)
                {
                    cts.Cancel();
                }
            }
        }