コード例 #1
0
        public void EndGame()
        {
            ActionApplier actionApplier = GetActionApplier(out ActionApplierContext actionApplierContext, false, false,
                                                           ActionApplierContext.EndGameSymbol);
            GameFlow flow = actionApplier.ApplyAction(new Board(), ActionApplierContext.EndGameSymbol);

            Assert.Equal(GameFlow.END_GAME, flow);
            actionApplierContext.MovementDisplayNamesResolver
            .Verify(mdnr => mdnr.TryResolve(Match.Create <string>(action => action == ActionApplierContext.EndGameSymbol), out movement), Times.Once);
            actionApplierContext.TileMover.Verify(tm => tm.TryMove(It.IsAny <Board>(), It.IsAny <Movement>(), out err), Times.Never);
        }
コード例 #2
0
        public void ActionResolved_MovementDone()
        {
            ActionApplier actionApplier = GetActionApplier(out ActionApplierContext actionApplierContext, true, true, ACTION_NAME);
            GameFlow      flow          = actionApplier.ApplyAction(new Board(), ACTION_NAME);

            Assert.Equal(GameFlow.KEEP_PLAYING, flow);
            actionApplierContext.IO.Verify(io => io.WriteLine(It.IsAny <string>(), It.IsAny <int>()), Times.Never);
            actionApplierContext.MovementDisplayNamesResolver
            .Verify(mdnr => mdnr.TryResolve(Match.Create <string>(action => action == ACTION_NAME), out movement), Times.Once);
            actionApplierContext.TileMover.Verify(tm => tm.TryMove(It.IsAny <Board>(), It.IsAny <Movement>(), out err), Times.Once);
        }
コード例 #3
0
        public void UnknownAction()
        {
            string        unknwonAction = "SOME_UNKNOWN_ACTION";
            ActionApplier actionApplier = GetActionApplier(out ActionApplierContext actionApplierContext, false, false, unknwonAction);
            GameFlow      flow          = actionApplier.ApplyAction(new Board(), unknwonAction);

            Assert.Equal(GameFlow.KEEP_PLAYING, flow);
            actionApplierContext.IO.Verify(io => io.WriteLine(
                                               Match.Create <string>(err => err.Contains(unknwonAction)),
                                               It.IsAny <int>()), Times.Once);
            actionApplierContext.MovementDisplayNamesResolver
            .Verify(mdnr => mdnr.TryResolve(Match.Create <string>(action => action == unknwonAction), out movement), Times.Once);
            actionApplierContext.TileMover.Verify(tm => tm.TryMove(It.IsAny <Board>(), It.IsAny <Movement>(), out err), Times.Never);
        }
コード例 #4
0
        /// <summary>
        /// Takes a state and returns the expected value (pre-division) of its outcome.
        /// </summary>
        /// <param name="state">The state to start in.</param>
        /// <param name="samples">The number of samples to take for each actino.</param>
        /// <param name="cloner">The means by which duplicate states may be generated.</param>
        /// <param name="action_enumerator">Enumerates the actions available in any given state.</param>
        /// <param name="updater">Takes a state and an action and applies one to the other.</param>
        /// <param name="state_hueristic">Determines how good a terminal state is.</param>
        /// <returns>Returns the expected value (pre-division) of the state's outcome.</returns>
        private static int Explore(S state, int samples, StateCloner <S> cloner, ActionEnumerator <A, S> action_enumerator, ActionApplier <A, S> updater, StateEvaluator <S> state_hueristic)
        {
            int ret = 0;

            for (int i = 0; i < samples; i++)
            {
                ret += PlayToCompletion(cloner(state), action_enumerator, updater, state_hueristic);
            }

            return(ret);
        }
コード例 #5
0
        /// <summary>
        /// Given a start state, performs a random sampling of it to terminal states and determines which available action is best to take.
        /// </summary>
        /// <param name="state">The state to start in.</param>
        /// <param name="samples">The number of samples to take for each actino.</param>
        /// <param name="cloner">The means by which duplicate states may be generated.</param>
        /// <param name="action_enumerator">Enumerates the actions available in any given state.</param>
        /// <param name="updater">Takes a state and an action and applies one to the other.</param>
        /// <param name="state_hueristic">Determines how good a terminal state is.</param>
        /// <returns>Returns the best action to take.</returns>
        public static A Search(S state, int samples, StateCloner <S> cloner, ActionEnumerator <A, S> action_enumerator, ActionApplier <A, S> updater, StateEvaluator <S> state_hueristic)
        {
            List <A>   actions         = new List <A>();
            List <int> expected_values = new List <int>();

            // Get all the actions
            IEnumerator <A> acts = action_enumerator(state);

            while (acts.MoveNext())
            {
                actions.Add(acts.Current);
            }

            // If we have no actions, we can't do anything
            if (actions.Count == 0)
            {
                return(default(A));
            }

            // If we have one action, don't waste time
            if (actions.Count == 1)
            {
                return(actions[0]);
            }

            // Obtain the expected value of each action
            foreach (A a in actions)
            {
                expected_values.Add(Explore(updater(cloner(state), a), samples, cloner, action_enumerator, updater, state_hueristic));
            }

            // Find the best action
            int max = expected_values[0];

            List <A> best = new List <A>();

            best.Add(actions[0]);

            for (int i = 1; i < expected_values.Count; i++)
            {
                if (expected_values[i] > max)
                {
                    max = expected_values[i];

                    best.Clear();
                    best.Add(actions[i]);
                }
                else if (expected_values[i] == max)
                {
                    best.Add(actions[i]);
                }
            }

            return(best[rand.Next(0, best.Count)]);
        }
コード例 #6
0
        /// <summary>
        /// Takes a state and executes it to completion randomly and returns a value representing how good the outcome is.
        /// </summary>
        /// <param name="state">The state to start in.</param>
        /// <param name="action_enumerator">Enumerates the actions available in any given state.</param>
        /// <param name="updater">Takes a state and an action and applies one to the other.</param>
        /// <param name="state_hueristic">Determines how good a terminal state is.</param>
        /// <returns>A value representing how good a random terminal node of the given state is.</returns>
        private static int PlayToCompletion(S state, ActionEnumerator <A, S> action_enumerator, ActionApplier <A, S> updater, StateEvaluator <S> state_hueristic)
        {
            List <A> actions = new List <A>();

            // Get all the actions
            IEnumerator <A> acts = action_enumerator(state);

            while (acts.MoveNext())
            {
                actions.Add(acts.Current);
            }

            // If we have no actions left, we're in a terminal state
            if (actions.Count == 0)
            {
                return(state_hueristic(state));
            }

            // Pick a random action and proceed further
            return(PlayToCompletion(updater(state, actions[rand.Next(0, actions.Count)]), action_enumerator, updater, state_hueristic));
        }
コード例 #7
0
        /// <summary>
        /// Given a state and a maximum search depth, this performs a recursive search using the alpha-beta algorithm to find the optimal action to take.
        /// </summary>
        /// <param name="state">The current state.</param>
        /// <param name="max_depth">The maximum number of sequential actions that may be taken.</param>
        /// <param name="state_hueristic">The means of determining how good a state is.</param>
        /// <param name="action_enumerator">The means of proceeding to a new state.</param>
        /// <param name="updater">The means by which a state is updated with new actions.</param>
        /// <param name="maximising">True if the algorithm should maximise at this step and false if it should minimise.</param>
        /// <param name="alpha">The best maximisation value so far.</param>
        /// <param name="beta">The best minimisation value so far.</param>
        /// <returns>Returns the value of the optimal action to take in the given state.</returns>
        private static int RSearch(S state, int depth, StateEvaluator <S> state_hueristic, ActionEnumerator <A, S> action_enumerator, ActionApplier <A, S> updater, Maximising <S> maximising, int alpha, int beta)
        {
            // If we've reached our terminal depth, evaluate and return
            if (depth == 0)
            {
                return(state_hueristic(state));
            }

            // Enumerate the available moves in the given state
            IEnumerator <A> actions = action_enumerator(state);

            // If there are no moves, we've reached a terminal state and need to evaluate it and return
            if (!actions.MoveNext())
            {
                return(state_hueristic(state));
            }

            if (maximising(state))
            {
                int max = int.MinValue;

                do
                {
                    max   = Math.Max(max, RSearch(updater(state, actions.Current), depth - 1, state_hueristic, action_enumerator, updater, maximising, alpha, beta));
                    alpha = alpha > max ? alpha : max;

                    if (alpha >= beta)
                    {
                        break;
                    }
                }while(actions.MoveNext());

                return(max);
            }

            int min = int.MaxValue;

            do
            {
                min  = Math.Min(min, RSearch(updater(state, actions.Current), depth - 1, state_hueristic, action_enumerator, updater, maximising, alpha, beta));
                beta = beta < min ? beta : min;

                if (alpha >= beta)
                {
                    break;
                }
            }while(actions.MoveNext());

            return(min);
        }
コード例 #8
0
        /// <summary>
        /// Given a state and a maximum search depth, this performs a search using the alpha-beta algorithm to find the optimal action to take.
        /// </summary>
        /// <param name="state">The current state.</param>
        /// <param name="max_depth">The maximum number of sequential actions that may be taken.</param>
        /// <param name="state_hueristic">The means of determining how good a state is.</param>
        /// <param name="action_enumerator">The means of proceeding to a new state.</param>
        /// <param name="updater">The means by which a state is updated with new actions.</param>
        /// <param name="maximizing">Determines if we are in a maximizing or minimizing state.</param>
        /// <returns>Returns the optimal action to take in the given state or default(A) if something went wrong.</returns>
        public static A Search(S state, int max_depth, StateEvaluator <S> state_hueristic, ActionEnumerator <A, S> action_enumerator, ActionApplier <A, S> updater, Maximising <S> maximising)
        {
            // Sanity check
            if (max_depth < 1 || state == null || state_hueristic == null || action_enumerator == null || maximising == null)
            {
                return(default(A));
            }

            // Bookkeeping
            List <A> best_actions = new List <A>();
            int      best_val     = int.MinValue;

            // Enumerate the available moves in the given state
            IEnumerator <A> actions = action_enumerator(state);

            // If there are no moves, we've reached a terminal state and can't do anything
            if (!actions.MoveNext())
            {
                return(default(A));
            }

            // If there's only one move (a forced move) then just take it
            // Note that this is only a useful case in the inital call as later one we're trying to compute the values of each move we could take
            if (!actions.MoveNext())
            {
                actions.Reset();
                actions.MoveNext();

                return(actions.Current);
            }

            // There's more than one move, so check them all out
            actions.Reset();
            actions.MoveNext();

            // We assume we start with the maximising player
            do
            {
                // Check out how good the current action is
                int val = RSearch(updater(state, actions.Current), max_depth - 1, state_hueristic, action_enumerator, updater, maximising, best_val, int.MaxValue);

                // If the current action is trash, ignore it
                if (val < best_val)
                {
                    continue;
                }

                // If the current action is okay, add it to the list
                if (val == best_val)
                {
                    best_actions.Add(actions.Current);
                    continue;
                }

                // The current action is crazy, so rock on
                best_actions.Clear();
                best_actions.Add(actions.Current);

                best_val = val;
            }while(actions.MoveNext());

            // We're garunteed to have at least one action to take, so this list will never be empty
            return(best_actions[rand.Next() % best_actions.Count]);
        }