Пример #1
0
        /// <summary>
        /// Gets one or two (normally two) states from one by applying the Right Timer transitions.
        /// Adds these to the Queue for continuing the breadth first search.
        /// Where the right timer is no longer relevant (for Skedar3Kill) this just queues the single state.
        /// </summary>
        /// <param name="basePr">The base probability for the stage we are considering Right Timer transitions from</param>
        /// <param name="compStg">The compressed stage</param>
        static void doRightTimerTransitionsAndQueue(ref double[] stateProb, ref distinctIntQueue Q, double basePr, int t_elapsed, int T_R, int t_M, int t_K, compStage compStg)
        {
            // If already 0, we've stopped (Skedar #3 is spawned), so just make the call
            if (T_R == 0)
            {
                combineQueueUpdate(ref stateProb, ref Q, basePr, t_elapsed, T_R, t_M, t_K, compStg);
            }
            else
            {
                // If the timer is not 1, do the event that we don't finish the timer (just timer changes)
                // Also adjust base probability for 1/256 of success next
                if (T_R != 1)
                {
                    combineQueueUpdate(ref stateProb, ref Q, basePr * q, t_elapsed, T_R - 1, t_M, t_K, compStg);
                    basePr /= 256;
                }

                // Now consider the reset event. Set timer back to 100.
                // Determine full stage to determine flow on
                T_R = 100;
                switch (compStg)
                {
                // More interesting stages, set the movement time to t_open when Joanna is still at Skedar #1.
                // But only set it once - there was a bug here where we kept reseting.
                case compStage.Skedar1:
                    // NTS: Awkward case where we used Parent's full stage was here, but being able to use current compressed is ideal.
                    //      Indeed it also would effect the bottom two - it was a bad idea.
                    if (t_M == -1)
                    {
                        t_M = t_open;
                    }
                    break;

                case compStage.JustJoannaMoving:
                    // If Joanna is already moving, update timer to max of time for Joanna and time for Skedar #2
                    // Notice here we actually change stage, preventing running this twice.
                    if (t_open > t_M)
                    {
                        t_M = t_open;
                    }
                    compStg = compStage.BothMoving;
                    break;

                case compStage.Skedar3:
                    // Set to 0 to show we're done with all events, stopping the timer.
                    // This is slightly cleaner, since the right timer would otherwise run while we're killing Skedar #3.
                    // Also observe we must be in Skedar3Await, since timer is running, so set kill timer
                    T_R = 0;
                    t_K = t_kill;
                    break;

                    // In full states BothMoving and Skedar2Kill Skedar #2 is spawned and not killed, so we only reset the timer (above the switch).
                }

                // Finally we queue this reset-timer state.
                combineQueueUpdate(ref stateProb, ref Q, basePr, t_elapsed, T_R, t_M, t_K, compStg);
            }
        }
Пример #2
0
        /// <summary>
        /// Performs the 3 functions which we regularly do in this order
        /// 1. Combine the values into a single integer
        /// 2. Add the probability to that stored against the integer in stateProb array
        /// 3. Enqueue the integer state (unless already queued)
        /// </summary>
        static void combineQueueUpdate(ref double[] stateProb, ref distinctIntQueue Q, double prob,
                                       int t_elapsed, int T_R, int t_M, int t_K, compStage compStg)
        {
            int S = combineState(t_elapsed, T_R, t_M, t_K, compStg);

            // ADD the probability.
            stateProb[S] += prob;
            Q.Enqueue(S);
        }
Пример #3
0
        static void Main(string[] args)
        {
            // Some reasonable assumptions, which aren't part of the model, but have been assumed for practicality.
            // Essentially these could be avoided if I changed the code.
            Debug.Assert(t_move <= t_open);
            Debug.Assert(t_kill > 0);

            // Initialise the array to hold the probability of reaching each state from the start state. Filled with 0s.
            // Note these are UPDATED (added to) potentially multiple times during execution.
            int N = (maxTime + 1) * 101 * (t_open + 2) * (t_kill + 1) * 4;

            Console.WriteLine("Total number of possible states: " + N.ToString());
            // Issue warning (will probably crash after) if we think we're going to have memory trouble.
            // Note this is less than 2 times the standard parameters' N.
            if (N > 134217727)
            {
                Console.WriteLine("Uhoh I think we're going to run out of memory, press enter to see..");
                Console.ReadLine();
            }
            double[] stateProb = new double[N];


            Console.WriteLine("Generating big Markov tree breadth first..");

            // Declare queue for use in bfs. Shouldn't quite need to be size 10,000 (but is dynamic anyway)
            // Add our start state to the queue, and note it's current (and final) probability as 1.
            distinctIntQueue Q = new distinctIntQueue(10000, N);

            combineQueueUpdate(ref stateProb, ref Q, 1, 0, 100, -1, 0, compStage.Skedar1);

            // Main loop, repeated drawing from the front of the queue (and potentially adding to it).
            // INVARIANT: Let the state at the front of Q have time elapsed = t.
            //              Then all states in Q have time elapsed t or t+1, with the t+1's at the back.
            while (Q.Count() > 0)
            {
                queueTransitionsFrom(Q.Dequeue(), ref stateProb, ref Q);
            }

            Console.WriteLine("Selected probabilities:");
            double[] results = new double[maxTime + 1];

            // Print out the results, and copy to array for if someone wants to extend this.
            for (int t_target = 0; t_target <= maxTime; t_target++)
            {
                // The only success state for this time.
                int    state = combineState(t_target, 0, 1, 0, compStage.Skedar3);
                double pr    = stateProb[state];
                results[t_target] = pr;
                // Print out like a CSV file - for easy manipulation.
                Console.Write(t_target);
                Console.Write(", ");
                Console.Write(pr);
                if (pr > 0)
                {
                    Console.Write(", 1 in ");
                    Console.Write(1 / pr);
                }
                Console.WriteLine();
            }


            Console.ReadLine();
        }
Пример #4
0
        /// <summary>
        /// Considers all transitions from the given state S,
        ///  updating the probabilities to reach it's children, and queueing them if they aren't already queued.
        /// </summary>
        static void queueTransitionsFrom(int S, ref double[] stateProb, ref distinctIntQueue Q)
        {
            // Extract all the values which make up the compressed state S, and read the probability.
            int       t_elapsed, T_R, t_M, t_K;
            compStage compStg;

            recoverState(S, out t_elapsed, out T_R, out t_M, out t_K, out compStg);
            double pr = stateProb[S];

            // Get the full (decompressed) state S, and 'switch' on this value.
            stage stg = getFullStage(compStg, t_M, t_K);

            // Increment time as all children are 1 frame later.
            // T_R dealt with in doRightTimerTransitions.
            // Recent change: now decreasing kill and movement timers here. We've already used them to recover the full state.
            t_elapsed += 1;
            if (t_M > 0)
            {
                t_M--;
            }
            if (t_K > 0)
            {
                t_K--;
            }

            // Friendly protection against the time too high.
            if (t_elapsed > maxTime)
            {
                return;
            }

            switch (stg)
            {
            case stage.Skedar1Await:
                // If the first timer hasn't finished, consider the 255/256 chance of just decrementing it.
                //  and change the chance of finishing early to 1/256 (otherwise it is 1).
                if (t_elapsed != 100)
                {
                    doRightTimerTransitionsAndQueue(ref stateProb, ref Q, pr * q, t_elapsed, T_R, t_M, t_K, compStg);
                    pr /= 256;
                }

                // Set up for timer elapsing transition (below switch): set the kill timer.
                t_K = t_kill;
                break;

            case stage.Skedar1Kill:
                // Start moving if we've finished killing.
                if (t_K <= 0)
                {
                    // Determine if Skedar #2 has started moving, and transition state accordingly,
                    //  also adjusting the t_M accordingly.
                    if (t_M == -1)
                    {
                        compStg = compStage.JustJoannaMoving;
                        t_M     = t_move;
                    }
                    else
                    {
                        compStg = compStage.BothMoving;
                        if (t_move > t_M)
                        {
                            t_M = t_move;
                        }
                    }
                }

                break;

            case stage.JustJoannaMoving:
                // Simply move, which has alrady been computed.
                // No change of state even if we've reached the door.
                break;

            case stage.BothMoving:
                // If we've both finished, the door has cracked and we're there.
                if (t_M <= 0)
                {
                    // Start kill timer to mark Skedar2Kill
                    t_K = t_kill;
                }
                break;

            case stage.Skedar2Kill:
                // Simply transition if we've finished killing.
                if (t_K <= 0)
                {
                    compStg = compStage.Skedar3;
                }
                break;

            case stage.Skedar3Await:
                // All handled with Right timer
                break;

            case stage.Skedar3Kill:
                // On kill, enter wierd compressed success state
                if (t_K <= 0)
                {
                    t_M = 1;
                }
                break;

            case stage.Success:
                // Just recurse with incremented time.
                // But reset movement timer to 1 since it persists in decreasing
                //  (that's the issue with this success state being very artifical)
                t_M = 1;
                combineQueueUpdate(ref stateProb, ref Q, pr, t_elapsed, T_R, t_M, t_K, compStg);

                // Don't even call Right Timer stuff.
                return;
            }

            // All stages above except success break out having set the values for this call.
            // This is because the right timer is always relevant, always transitioning throughout the game.
            doRightTimerTransitionsAndQueue(ref stateProb, ref Q, pr, t_elapsed, T_R, t_M, t_K, compStg);
        }