예제 #1
0
        public TurnResult PlayTurn(Turn turn)
        {
            TurnResult turnResult = GameState.PlayTurn(turn);

            PlayedTurns.Add(Tuple.Create(turn, turnResult));
            Board.LastTurn = turn;
            return(turnResult);
        }
예제 #2
0
        public void Progress()
        {
            TurnPlay?.Invoke(this, new GameEventArgs(Game, null));
            int        inTurnPlayerIndex = Game.GameState.InTurnPlayerIndex;
            Turn       turn       = GameClients[inTurnPlayerIndex].GetTurn(Game);
            TurnResult turnResult = Game.PlayTurn(turn);

            TurnPlayed?.Invoke(this, new TurnEventArgs(Game, turn, turnResult));
        }
예제 #3
0
        /// <summary>
        /// This method runs the ABS algorithm parallel, in the following way:
        /// Only parallelize the calculation of the score of the depth=1 nodes, then take the max of those.
        /// To do the calculation of the depth=1 node correctly, we need to pass on the Invert=true property and set MaxMoves one lower.
        /// Also, we need to sort on evaluation score as usual.
        /// Finally, in order for the pruning to work well, we have to pass on the updated Min (alpha) as much as possible.
        /// Here, we have a trade-off: on the one hand we want to pass on the updated Min value immediately to the next node, but then we can't parallelize anything.
        /// On the other hand, if we parallelize with 8 cores and start calculating 8 nodes immediately with Min = -inf, then those calculations might all be slow, and the parallelizing overhead results in time lost instead of time won.
        /// After some trial, parallelizing 4 seems to have the best results.
        /// </summary>
        /// <param name="originalGame"></param>
        /// <returns></returns>
        private Turn GetTurnParallel(Game originalGame)
        {
            try {
                GameClientStatsCollector?.StartGetTurn(originalGame);

                List <Tuple <Turn, Game, double> > games = new List <Tuple <Turn, Game, double> >();
                foreach (Turn turn in originalGame.GetValidTurns())
                {
                    TurnResult turnResult = null;
                    try {
                        turnResult = originalGame.GameState.PlayTurn(turn);
                        var    game  = originalGame.Clone();
                        double score = Evaluator.Evaluate(game, 1 - game.GameState.InTurnPlayerIndex);
                        // @@@ if the turn is directly winning, we are still going to try to explore the subtree and that leads to problems.
                        games.Add(Tuple.Create(turn, game, score));
                    } finally {
                        // roll back
                        originalGame.GameState.UndoTurn(turn, turnResult);
                    }
                }

                ConcurrentBag <Tuple <Turn, double> > bag = new ConcurrentBag <Tuple <Turn, double> >();
                object dummyLock    = new object();
                double min          = AlphaBetaSearch.GameResultLosing;
                var    orderedGames = games.OrderByDescending(t => t.Item3).ToList();
                Parallel.ForEach(orderedGames,
                                 new ParallelOptions()
                {
                    MaxDegreeOfParallelism = Math.Min(4, Environment.ProcessorCount)
                },
                                 tup => {
                    Turn turn           = tup.Item1;
                    var game            = tup.Item2;
                    AlphaBetaSearch abs = new AlphaBetaSearch(game, MaxMoves - 1, Evaluator, false, DoPrune, CollectStats, DoLog, invert: true, startingMin: min);
                    var gameResults     = abs.GetGameResult();
                    var gameResult      = gameResults.Item1;
                    lock (dummyLock) {
                        min = Math.Max(min, gameResult);
                    }
                    bag.Add(Tuple.Create(turn, gameResult));
                });

                var best = bag.OrderByDescending(tup => tup.Item2).First();
                GameResult = best.Item2;
                return(best.Item1);
            } finally {
                GameClientStatsCollector?.EndGetTurn();
            }
        }
예제 #4
0
        public void UndoTurn(Turn turn, TurnResult turnResult, bool doChecks = true)
        {
            MiniMax.UndoTurnCount++;
            AlphaBetaSearch.UndoTurnCount++;

            InTurnPlayerIndex  = 1 - InTurnPlayerIndex;
            WinningPlayerIndex = null;
            Vector movedPosition = turn.OriginalPosition.Add(turn.Move);
            Piece  piece         = Board[movedPosition.X, movedPosition.Y];

            if (turnResult.CapturedPieceType != null)
            {
                Piece capturedPiece = CapturedPieces.Pop();
                if (doChecks)
                {
                    if (!capturedPiece.IsCaptured)
                    {
                        throw new Exception("Huh");
                    }
                    if (capturedPiece.PlayerIndex != 1 - InTurnPlayerIndex)
                    {
                        throw new Exception("huh");
                    }
                    if (capturedPiece.PieceType != turnResult.CapturedPieceType)
                    {
                        throw new ArgumentException("Bad TurnResult.CapturedPieceType");
                    }
                    if (!capturedPiece.Position.Equals(movedPosition))
                    {
                        throw new ArgumentException("Bad CapturedPiece Position mismatch");
                    }
                }
                capturedPiece.IsCaptured = false;
                Board[movedPosition.X, movedPosition.Y] = capturedPiece;
            }
            else
            {
                Board[movedPosition.X, movedPosition.Y] = null;
            }
            piece.Position = turn.OriginalPosition;
            Board[turn.OriginalPosition.X, turn.OriginalPosition.Y] = piece;

            Card tmp = GameCards[MiddleCardIndex];

            GameCards[MiddleCardIndex] = GameCards[PlayerCardIndices[InTurnPlayerIndex][turn.CardIndex]];
            GameCards[PlayerCardIndices[InTurnPlayerIndex][turn.CardIndex]] = tmp;
        }
예제 #5
0
        /// <summary>
        /// Returns all neighbouring states and whether or not the move to the neighbouring state is winning or not.
        /// Changes Game.GameState during loop.
        /// </summary>
        private IEnumerable <Tuple <Turn, bool> > GetNeighbours()
        {
            GetNeighboursCount++;

            foreach (Turn turn in Game.GetValidTurns())
            {
                TurnResult turnResult = null;
                try {
                    turnResult = Game.GameState.PlayTurn(turn, DoChecks);
                    if (turnResult.GameIsFinished)
                    {
                        yield return(Tuple.Create(turn, true));
                    }
                    else
                    {
                        yield return(Tuple.Create(turn, false));
                    }
                } finally {
                    // roll back
                    Game.GameState.UndoTurn(turn, turnResult, DoChecks);
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Returns all neighbouring states and whether or not the move to the neighbouring state is winning or not and the neighbouring gameStateId.
        /// Changes Game.GameState during loop.
        /// Return neighbours in order of score of evaluation function if DoPrune is true.
        /// </summary>
        private IEnumerable <Tuple <Turn, bool, long?> > GetNeighbours(bool isMaxNode, int movesDone)
        {
            GetNeighboursCount++;

            if (movesDone == MaxMoves - 1 || !DoPrune)
            {
                // we don't want to calculate the evaluation score of all leave nodes (to sort on them)
                // because that's slower than traversing them unsorted and then (due to ABS pruning) NOT calculating the evaluation score of some of the leaves at all.
                // so, getting neighbours for the almost-leave-nodes is different than for the other nodes.
                foreach (Turn turn in Game.GetValidTurns())
                {
                    TurnResult turnResult = null;
                    try {
                        turnResult = Game.GameState.PlayTurn(turn, DoChecks);
                        if (turnResult.GameIsFinished)
                        {
                            yield return(Tuple.Create(turn, true, (long?)null));
                        }
                        else
                        {
                            yield return(Tuple.Create(turn, false, (long?)null));
                        }
                    } finally {
                        // roll back
                        Game.GameState.UndoTurn(turn, turnResult, DoChecks);
                    }
                }
            }
            else
            {
                List <Tuple <Turn, double, long?> > turnsWithScore = new List <Tuple <Turn, double, long?> >();

                foreach (Turn turn in Game.GetValidTurns())
                {
                    TurnResult turnResult = null;
                    try {
                        turnResult = Game.GameState.PlayTurn(turn, DoChecks);
                        long?  gameStateId = GetUniqueIdentifier(Game.GameState); // don't calculate gameStateId again in the ABS min/max loop, so pass it on.
                        double score       = GetEvaluationScore(gameStateId.Value);
                        turnsWithScore.Add(Tuple.Create(turn, score, gameStateId));
                    } finally {
                        // roll back
                        Game.GameState.UndoTurn(turn, turnResult, DoChecks);
                    }
                }

                if (isMaxNode)
                {
                    turnsWithScore.Sort((tup1, tup2) => - 1 * tup1.Item2.CompareTo(tup2.Item2)); // descending by score for max nodes.
                }
                else
                {
                    turnsWithScore.Sort((tup1, tup2) => 1 * tup1.Item2.CompareTo(tup2.Item2)); // ascending by score for min nodes.
                }

                foreach (var tup in turnsWithScore)
                {
                    Turn       turn        = tup.Item1;
                    TurnResult turnResult  = null;
                    long?      gameStateId = tup.Item3;
                    try {
                        turnResult = Game.GameState.PlayTurn(turn, DoChecks);
                        if (turnResult.GameIsFinished)
                        {
                            yield return(Tuple.Create(turn, true, gameStateId));
                        }
                        else
                        {
                            yield return(Tuple.Create(turn, false, gameStateId));
                        }
                    } finally {
                        // roll back
                        Game.GameState.UndoTurn(turn, turnResult, DoChecks);
                    }
                }
            }
        }
예제 #7
0
 public TurnEventArgs(Game game, Turn turn, TurnResult turnResult)
 {
     Game       = game;
     Turn       = turn;
     TurnResult = turnResult;
 }