コード例 #1
0
            public OddmentTable <int> Create(SearchContext <object, TicTacToeState, TicTacToeMove, object, TicTacToeMove> context, int samplesForGeneration)
            {
                var table = new Dictionary <int, double>();

                for (var i = 0; i < samplesForGeneration; i++)
                {
                    var action   = new TicTacToeMove(TicTacToeMoveGenerator.AllEmptyPositions(context.Source).RandomElementOrDefault(), context.Source.ActivePlayerID);
                    var newState = GameLogic.Apply(context, (TicTacToeState)context.Source.Copy(), action);
                    var endState = PlayoutStrategy.Playout(context, newState);
                    var value    = EvaluationStrategy.Evaluate(context, new TreeSearchNode <TicTacToeState, TicTacToeMove>(action), endState);
                    if (!table.ContainsKey(action.PositionToPlace))
                    {
                        table.Add(action.PositionToPlace, 0);
                    }
                    table[action.PositionToPlace] += value;
                }

                var maxValue = table.Values.Max();
                var minValue = table.Values.Min();

                var oddmentTable = new OddmentTable <int>();

                foreach (var kvPair in table)
                {
                    var normalisedValue = Util.Normalise(kvPair.Value, minValue, maxValue);
                    oddmentTable.Add(kvPair.Key, normalisedValue, recalculate: false);
                }
                oddmentTable.Recalculate();

                return(oddmentTable);
            }
コード例 #2
0
            public TicTacToeMove Sample(TicTacToeState state, OddmentTable <int> sideInformation)
            {
                // Get all available positions in this state
                var emptyPositions = TicTacToeMoveGenerator.AllEmptyPositions(state);

                var positionSelected = false;
                var selectedPosition = -1;

                while (!positionSelected)
                {
                    // Sample a position from the OddmentTable
                    selectedPosition = sideInformation.Next();
                    // Check if this position can be played, otherwise generate a new one
                    positionSelected = emptyPositions.Contains(selectedPosition);
                }

                return(new TicTacToeMove(selectedPosition, state.ActivePlayerID));
            }
コード例 #3
0
        public List <int> CheckThreats(TicTacToeState state, int playerID)
        {
            var possibilities = TicTacToeMoveGenerator.AllEmptyPositions(state);
            var threats       = new List <int>();

            foreach (var possibility in possibilities)
            {
                var testMove = new TicTacToeMove(possibility, playerID);
                var clone    = (TicTacToeState)state.Copy();
                clone = Apply(null, clone, testMove);
                if (!CheckWinningPositions(clone, playerID).IsNullOrEmpty())
                {
                    threats.Add(possibility);
                }
            }

            return(threats);
        }
コード例 #4
0
        public List <int> CheckWinningPositions(TicTacToeState state, int playerID)
        {
            var possibilities = TicTacToeMoveGenerator.AllEmptyPositions(state);
            var wins          = new List <int>();

            foreach (var possibility in possibilities)
            {
                var testMove = new TicTacToeMove(possibility, playerID);
                var clone    = (TicTacToeState)state.Copy();
                clone = Apply(null, clone, testMove);
                if (clone.Done && clone.PlayerWon == playerID)
                {
                    wins.Add(possibility);
                }
            }

            return(wins);
        }
コード例 #5
0
        /// <inheritdoc />
        /// <summary>
        /// See <see cref="https://en.wikipedia.org/wiki/Tic-tac-toe#Strategy"/>
        /// </summary>
        public TicTacToeMove Act(SearchContext <object, TicTacToeState, TicTacToeMove, object, TicTacToeMove> context, TicTacToeState state)
        {
            var possibilities = TicTacToeMoveGenerator.AllEmptyPositions(state);
            var myID          = state.ActivePlayerID;
            var oppID         = TicTacToeState.SwitchPlayerID(myID);
            var oppMove       = oppID == TicTacToeState.PLAYER_ONE_ID ? TicTacToeState.PLAYER_ONE_MOVE : TicTacToeState.PLAYER_TWO_MOVE;
            var openCorners   = possibilities.Where(i => CornerPositions.Contains(i)).ToList();
            var openEdges     = possibilities.Where(i => EdgePositions.Contains(i)).ToList();

            #region Opening and response

            // Opening move to corner or middle position
            if (possibilities.Count == 9)
            {
                //return new TicTacToeMove(CornerPositions.RandomElementOrDefault(), myID);
                return(new TicTacToeMove(4, myID));
            }

            // Second turn
            if (possibilities.Count == 8)
            {
                // If the middle position was opened with, play corner
                if (!possibilities.Contains(4))
                {
                    return(new TicTacToeMove(CornerPositions.RandomElementOrDefault(), myID));
                }
                // If a corner or an edge was opened with, play middle
                return(new TicTacToeMove(4, myID));
            }

            #endregion

            #region 1. Win

            // Check for own winning moves
            var myWins = CheckWinningPositions(state, myID);
            // Take the win
            if (!myWins.IsNullOrEmpty())
            {
                return(new TicTacToeMove(myWins.RandomElementOrDefault(), myID));
            }

            #endregion

            #region 2. Block

            // Check for opponent's winning moves
            var oppWins = CheckWinningPositions(state, oppID);
            // Prevent the loss
            if (!oppWins.IsNullOrEmpty())
            {
                return(new TicTacToeMove(oppWins.RandomElementOrDefault(), myID));
            }

            #endregion

            #region 3. Fork

            // Check if we have any forks available
            var forks = CheckForks(state, myID);
            // Move to one of the available forks
            if (!forks.IsNullOrEmpty())
            {
                return(new TicTacToeMove(forks.RandomElementOrDefault(), myID));
            }

            #endregion

            #region 4. Blocking an opponent's fork

            // Check if the opponent has any forks available
            var oppForks = CheckForks(state, oppID);
            if (!oppForks.IsNullOrEmpty())
            {
                // If there is only one possible fork for the opponent, the player should block it.
                if (oppForks.Count == 1)
                {
                    return(new TicTacToeMove(oppForks[0], myID));
                }
                // Otherwise, the player should block any forks in any way that simultaneously allows them to create two in a row, as long as it doesn't result in them creating a fork.
                var threats             = CheckThreats(state, myID);
                var safeThreats         = threats.Where(i => !DoesThisThreatCreateAForkForOpponent(state, i, myID)).ToList();
                var safeBlockingThreats = safeThreats.Where(i => oppForks.Contains(i)).ToList();
                if (!safeBlockingThreats.IsNullOrEmpty())
                {
                    return(new TicTacToeMove(safeBlockingThreats.RandomElementOrDefault(), myID));
                }
                // Otherwise, the player should create a two in a row to force the opponent into defending, as long as it doesn't result in them creating a fork.
                if (!safeThreats.IsNullOrEmpty())
                {
                    return(new TicTacToeMove(safeThreats.RandomElementOrDefault(), myID));
                }
            }

            #endregion

            #region 5. Center

            // If middle is open, play it
            if (possibilities.Contains(4))
            {
                return(new TicTacToeMove(4, myID));
            }

            #endregion

            #region 6. Opposite corner

            // If the opponent is in a corner and the opposite corner is available, move there
            foreach (var cornerPosition in CornerPositions)
            {
                var oppositeCorner = OppositeCorner(cornerPosition);
                if (state.State[cornerPosition] == oppMove && openCorners.Contains(oppositeCorner))
                {
                    return(new TicTacToeMove(oppositeCorner, myID));
                }
            }

            #endregion

            #region 7. Empty corner

            // If a corner is open, play it
            if (!openCorners.IsNullOrEmpty())
            {
                return(new TicTacToeMove(openCorners.RandomElementOrDefault(), myID));
            }

            #endregion

            #region 8. Empty side

            // If an edge is open, play it
            if (!openEdges.IsNullOrEmpty())
            {
                return(new TicTacToeMove(openEdges.RandomElementOrDefault(), myID));
            }

            #endregion

            // Otherwise, act random
            var index          = rng.Next(possibilities.Count);
            var randomPosition = possibilities.ToArray()[index];
            // Return a random position to play for the active player
            return(new TicTacToeMove(randomPosition, myID));
        }
コード例 #6
0
 public TicTacToeMove Sample(TicTacToeState state)
 {
     return(new TicTacToeMove(TicTacToeMoveGenerator.AllEmptyPositions(state).RandomElementOrDefault(), state.ActivePlayerID));
 }