示例#1
0
        /**
         * This function will generate getRandomTurnCount() number of non cancelling,
         * random turns. If a puzzle wants to provide custom scrambles
         * (for example: Pochmann style megaminx or MRSS), it should override this method.
         *
         * NOTE: It is assumed that this method is thread safe! That means that if you're
         * overriding this method and you don't know what you're doing,
         * use the synchronized keyword when implementing this method:<br>
         * <code>protected synchronized String generateScramble(Random r);</code>
         * @param r An instance of Random
         * @return A PuzzleStateAndGenerator that contains a scramble string, and the
         *         state achieved by applying that scramble.
         */

        public virtual PuzzleStateAndGenerator GenerateRandomMoves(Random r)
        {
            var ab = new AlgorithmBuilder(MergingMode.NoMerging, GetSolvedState());

            while (ab.GetTotalCost() < GetRandomMoveCount())
            {
                var successors = ab.GetState().GetScrambleSuccessors();
                try
                {
                    string move;
                    do
                    {
                        move = Functions.Choose(r, successors.Keys);
                        // If this move happens to be redundant, there is no
                        // reason to select this move again in vain.
                        successors.Remove(move);
                    } while (ab.IsRedundant(move));
                    ab.AppendMove(move);
                }
                catch (InvalidMoveException e)
                {
                    Assert(false, e.Message, e);
                    return(null);
                }
            }
            return(ab.GetStateAndGenerator());
        }
示例#2
0
        public static PuzzleStateAndGenerator ApplyOrientation(CubePuzzle puzzle, CubeMove[] randomOrientation,
                                                               PuzzleStateAndGenerator psag, bool discardRedundantMoves)
        {
            if (randomOrientation.Length == 0)
            {
                return(psag);
            }

            // Append reorientation to scramble.
            try
            {
                var ab = new AlgorithmBuilder(MergingMode.NoMerging, puzzle.GetSolvedState());
                ab.AppendAlgorithm(psag.Generator);
                foreach (var cm in randomOrientation)
                {
                    ab.AppendMove(cm.ToString());
                }

                psag = ab.GetStateAndGenerator();
                return(psag);
            }
            catch (InvalidMoveException e)
            {
                Assert(false, e.Message, e);
                return(null);
            }
        }
        public override PuzzleStateAndGenerator GenerateRandomMoves(Random r)
        {
            var scramble = _threePhaseSearcher.RandomState(r);
            var ab       = new AlgorithmBuilder(MergingMode.CanonicalizeMoves, GetSolvedState());

            try
            {
                ab.AppendAlgorithm(scramble);
            }
            catch (InvalidMoveException e)
            {
                Assert(false, e.Message, new InvalidScrambleException(scramble, e));
            }
            return(ab.GetStateAndGenerator());
        }
示例#4
0
        public PuzzleStateAndGenerator GenerateRandomMoves(Random r, string firstAxisRestriction,
                                                           string lastAxisRestriction)
        {
            var randomState = Tools.RandomCube(r);
            var scramble    =
                _twoPhaseSearcher.Solution(randomState, ThreeByThreeMaxScrambleLength, ThreeByThreeTimeout,
                                           ThreeByThreeTimemin, Search.INVERSE_SOLUTION, firstAxisRestriction, lastAxisRestriction).Trim();

            var ab = new AlgorithmBuilder(MergingMode.CanonicalizeMoves, GetSolvedState());

            try
            {
                ab.AppendAlgorithm(scramble);
            }
            catch (InvalidMoveException e)
            {
                Assert(false, e.Message, new InvalidScrambleException(scramble, e));
            }
            return(ab.GetStateAndGenerator());
        }
示例#5
0
        public static PuzzleStateAndGenerator ApplyOrientation(CubePuzzle puzzle, CubeMove[] randomOrientation,
                                                               PuzzleStateAndGenerator psag, bool discardRedundantMoves)
        {
            if (randomOrientation.Length == 0)
            {
                return(psag);
            }

            // Append reorientation to scramble.
            try
            {
                var ab = new AlgorithmBuilder(MergingMode.NoMerging, puzzle.GetSolvedState());
                ab.AppendAlgorithm(psag.Generator);
                // Check if our reorientation is going to cancel with the last
                // turn of our scramble. If it does, then we just discard
                // that last turn of our scramble. This ensures we have a scramble
                // with no redundant turns, and I can't see how it could hurt the
                // quality of our scrambles to do this.
                var firstReorientMove = randomOrientation[0].ToString();
                while (ab.IsRedundant(firstReorientMove))
                {
                    //azzert(discardRedundantMoves);
                    var im = ab.FindBestIndexForMove(firstReorientMove, MergingMode.CanonicalizeMoves);
                    ab.PopMove(im.Index);
                }
                foreach (var cm in randomOrientation)
                {
                    ab.AppendMove(cm.ToString());
                }

                psag = ab.GetStateAndGenerator();
                return(psag);
            }
            catch (InvalidMoveException e)
            {
                Assert(false, e.Message, e);
                return(null);
            }
        }
示例#6
0
        public override PuzzleStateAndGenerator GenerateRandomMoves(Random r)
        {
            // For fewest moves, we want to minimize the probability that the
            // scramble has useful "stuff" in it. The problem with conventional
            // Kociemba 2 phase solutions is that there's a pretty obvious
            // orientation step, which competitors might intentionally (or even
            // accidentally!) use in their solution. To lower the probability of this happening,
            // we intentionally generate dumbed down solutions.

            // We eventually decided to go with "Tom2", which is described by Tom
            // Rokicki (https://groups.google.com/d/msg/wca-admin/vVnuhk92hqg/P5oaJJQjDQAJ):

            // START TOM MESSAGE
            // If we're going this direction, and the exact length isn't critical, why not combine a bunch of the
            // ideas we've seen so far into something this simple:
            //
            // 1. Fix a set of prefix/suffix moves, say, U F R / U F R.
            // 2. For a given position p, find *any* solution where that solution prefixed and suffixed with
            //    the appropriate moves is canonical.
            //
            // That's it.  So we generate a random position, then find any two-phase solution g such
            // that U F R g U F R is a canonical sequence, and that's our FMC scramble.
            //
            // The prefix/suffix will be easily recognizable and memorable and ideally finger-trick
            // friendly (if it matters).
            //
            // Someone wanting to practice speed FMC (hehe) can make up their own scrambles just
            // by manually doing the prefix/suffix thing on any existing scrambler.
            //
            // It does not perturb the uniformity of the random position selection.
            //
            // It's simple enough that it is less likely to suffer from subtle defects due to the
            // perturbation of the two-phase search (unlikely those these may be).
            //
            // And even if the two-phase solution is short, adding U F R to the front and back makes it
            // no longer be unusually short (with high probability).
            // END TOM MESSAGE

            // Michael Young suggested using R' U' F as our padding (https://groups.google.com/d/msg/wca-admin/vVnuhk92hqg/EzQfG_vPBgAJ):

            // START MICHAEL MESSSAGE
            // I think that something more like R' U' F (some sequence that
            // involves a quarter-turn on all three "axes") is better because it
            // guarantees at least one bad edge in every orientation; with EO-first
            // options becoming more popular, "guaranteeing" that solving EO
            // optimally can't be derived from the scramble is a nice thing to
            // have, I think.  (Someone who was both unscrupulous and lucky could
            // see that R' F R doesn't affect U2/D2 EO, and therefore find out how
            // to solve EO by looking at the solution ignoring the R' F R.  That
            // being said, it still does change things, and I like how
            // finger-tricky/reversible the current prefix is.)  Just my two cents,
            // 'tho.
            // END MICHAEL MESSSAGE
            var scramblePrefix = SplitAlgorithm("R' U' F");
            var scrambleSuffix = SplitAlgorithm("R' U' F");

            // super.generateRandomMoves(...) will pick a random state S and find a solution:
            //  solution = sol_0, sol_1, ..., sol_n-1, sol_n
            // We then invert that solution to create a scramble:
            //  scramble = sol_n' + sol_(n-1)' + ... + sol_1' + sol_0'
            // We then prefix the scramble with scramblePrefix and suffix it with
            // scrambleSuffix to create paddedScramble:
            //  paddedScramble = scramblePrefix + scramble + scrambleSuffix
            //  paddedScramble = scramblePrefix + (sol_n' + sol_(n-1)' + ... + sol_1' + sol_0') + scrambleSuffix
            //
            // We don't want any moves to cancel here, so we need to make sure that
            // sol_n' doesn't cancel with the first move of scramblePrefix:
            var solutionLastAxisRestriction = scramblePrefix[scramblePrefix.Length - 1].Substring(0, 1);
            // and we need to make sure that sol_0' doesn't cancel with the first move of
            // scrambleSuffix:
            var solutionFirstAxisRestriction = scrambleSuffix[0].Substring(0, 1);
            var psag = GenerateRandomMoves(r, solutionFirstAxisRestriction, solutionLastAxisRestriction);
            var ab   = new AlgorithmBuilder(MergingMode.NoMerging, GetSolvedState());

            try
            {
                ab.AppendAlgorithms(scramblePrefix);
                ab.AppendAlgorithm(psag.Generator);
                ab.AppendAlgorithms(scrambleSuffix);
            }
            catch (InvalidMoveException e)
            {
                Assert(false, e.Message, e);
                return(null);
            }
            return(ab.GetStateAndGenerator());
        }
示例#7
0
        protected virtual string SolveIn(PuzzleState ps, int n)
        {
            if (ps.IsSolved())
            {
                return("");
            }

            var seenSolved      = new Dictionary <PuzzleState, int>();
            var fringeSolved    = new SortedBuckets <PuzzleState>();
            var seenScrambled   = new Dictionary <PuzzleState, int>();
            var fringeScrambled = new SortedBuckets <PuzzleState>();

            // We're only interested in solutions of cost <= n
            var         bestIntersectionCost = n + 1;
            PuzzleState bestIntersection     = null;

            var solvedNormalized = GetSolvedState().GetNormalized();

            fringeSolved.Add(solvedNormalized, 0);
            seenSolved[solvedNormalized] = 0;
            fringeScrambled.Add(ps.GetNormalized(), 0);
            seenScrambled[ps.GetNormalized()] = 0;

            //TimedLogRecordStart start = new TimedLogRecordStart(Level.FINER, "Searching for solution in " + n + " moves.");
            //l.log(start);

            var fringeTies = 0;

            // The task here is to do a breadth-first search starting from both the solved state and the scrambled state.
            // When we got an intersection from the two hash maps, we are done!
            int minFringeScrambled = -1, minFringeSolved = -1;
            int i     = 0;
            int j     = 0;

            while (!fringeSolved.IsEmpty() || !fringeScrambled.IsEmpty())
            {
                // We have to choose on which side we are extending our search.
                // I'm choosing the non empty fringe with the node nearest
                // its origin. In the event of a tie, we make sure to alternate.
                if (!fringeScrambled.IsEmpty())
                {
                    minFringeScrambled = fringeScrambled.SmallestValue();
                }
                if (!fringeSolved.IsEmpty())
                {
                    minFringeSolved = fringeSolved.SmallestValue();
                }
                bool extendSolved;
                if (fringeSolved.IsEmpty() || fringeScrambled.IsEmpty())
                {
                    // If the solved fringe is not empty, we'll expand it.
                    // Otherwise, we're expanding the scrambled fringe.
                    extendSolved = !fringeSolved.IsEmpty();
                }
                else
                {
                    if (minFringeSolved < minFringeScrambled)
                    {
                        extendSolved = true;
                    }
                    else if (minFringeSolved > minFringeScrambled)
                    {
                        extendSolved = false;
                    }
                    else
                    {
                        extendSolved = fringeTies++ % 2 == 0;
                    }
                }

                // We are using references for a more concise code.
                Dictionary <PuzzleState, int> seenExtending;
                SortedBuckets <PuzzleState>   fringeExtending;
                Dictionary <PuzzleState, int> seenComparing;
                int minComparingFringe;
                if (extendSolved)
                {
                    i++;
                    seenExtending      = seenSolved;
                    fringeExtending    = fringeSolved;
                    seenComparing      = seenScrambled;
                    minComparingFringe = minFringeScrambled;
                }
                else
                {
                    j++;
                    seenExtending      = seenScrambled;
                    fringeExtending    = fringeScrambled;
                    seenComparing      = seenSolved;
                    minComparingFringe = minFringeSolved;
                }

                PuzzleState node     = fringeExtending.Pop();
                int         distance = seenExtending[node];
                if (seenComparing.ContainsKey(node))
                {
                    // We found an intersection! Compute the total cost of the
                    // path going through this node.
                    int cost = seenComparing[node] + distance;
                    if (cost < bestIntersectionCost)
                    {
                        bestIntersection     = node;
                        bestIntersectionCost = cost;
                    }
                    continue;
                }
                // The best possible solution involving this node would
                // be through a child of this node that gets us across to
                // the other fringe's smallest distance node.
                int bestPossibleSolution = distance + minComparingFringe;
                if (bestPossibleSolution >= bestIntersectionCost)
                {
                    continue;
                }
                if (distance >= (n + 1) / 2)
                {
                    continue;
                }


                var movesByState = node.GetCanonicalMovesByState();
                foreach (PuzzleState next in movesByState.Keys)
                {
                    int moveCost     = node.GetMoveCost(movesByState[next]);
                    int nextDistance = distance + moveCost;
                    //next = next.getNormalized();
                    PuzzleState nNext = next.GetNormalized();
                    if (seenExtending.ContainsKey(nNext))
                    {
                        if (nextDistance >= seenExtending[nNext])
                        {
                            continue;
                        }
                    }
                    fringeExtending.Add(nNext, nextDistance);
                    seenExtending[nNext] = nextDistance;
                }
            }

            //l.log(start.finishedNow("expanded " + (seenSolved.size() + seenScrambled.size()) + " nodes"));

            if (bestIntersection == null)
            {
                return(null);
            }

            // We have found a solution, but we still have to recover the move sequence.
            // the `bestIntersection` is the bound between the solved and the scrambled states.
            // We can travel from `bestIntersection` to either states, like that:
            // solved <----- bestIntersection -----> scrambled
            // However, to build a solution, we need to travel like that:
            // solved <----- bestIntersection <----- scrambled
            // So we have to travel backward for the scrambled side.

            // Step 1: bestIntersection -----> scrambled

            Assert(bestIntersection.IsNormalized());
            var state = bestIntersection;
            var distanceFromScrambled = seenScrambled[state];

            // We have to keep track of all states we have visited
            var linkedStates = new PuzzleState[distanceFromScrambled + 1];

            linkedStates[distanceFromScrambled] = state;

            while (distanceFromScrambled > 0)
            {
                foreach (var next in state.GetCanonicalMovesByState().Keys)
                {
                    var nNext = next.GetNormalized();
                    if (!seenScrambled.ContainsKey(nNext))
                    {
                        continue;
                    }
                    var newDistanceFromScrambled = seenScrambled[nNext];
                    if (newDistanceFromScrambled >= distanceFromScrambled)
                    {
                        continue;
                    }
                    state = nNext;
                    distanceFromScrambled = newDistanceFromScrambled;
                    linkedStates[distanceFromScrambled] = state;
                    goto outer1;
                }
                Assert(false);
outer1:
                ;
            }

            // Step 2: bestIntersection <----- scrambled

            var solution = new AlgorithmBuilder(MergingMode.CanonicalizeMoves, ps);

            state = ps;
            distanceFromScrambled = 0;

            while (!state.EqualsNormalized(bestIntersection))
            {
                foreach (var next in state.GetCanonicalMovesByState())
                {
                    var nextState = next.Key;
                    var moveName  = next.Value;
                    if (!nextState.EqualsNormalized(linkedStates[distanceFromScrambled + 1]))
                    {
                        continue;
                    }
                    state = nextState;
                    try
                    {
                        solution.AppendMove(moveName);
                    }
                    catch (InvalidMoveException e)
                    {
                        Assert(false, e.Message, e);
                    }
                    distanceFromScrambled = seenScrambled[state.GetNormalized()];
                    goto outer2;
                }
                Assert(false);
outer2:
                ;
            }

            // Step 3: solved <----- bestIntersection

            var distanceFromSolved = seenSolved[state.GetNormalized()];

            while (distanceFromSolved > 0)
            {
                foreach (var next in state.GetCanonicalMovesByState())
                {
                    var nextState           = next.Key;
                    var nextStateNormalized = nextState.GetNormalized();
                    var moveName            = next.Value;
                    if (!seenSolved.ContainsKey(nextStateNormalized))
                    {
                        continue;
                    }
                    var newDistanceFromSolved = seenSolved[nextStateNormalized];
                    if (newDistanceFromSolved >= distanceFromSolved)
                    {
                        continue;
                    }
                    state = nextState;
                    distanceFromSolved = newDistanceFromSolved;
                    try
                    {
                        solution.AppendMove(moveName);
                    }
                    catch (InvalidMoveException e)
                    {
                        Assert(false, e.Message, e);
                    }
                    goto outer3;
                }
                Assert(false);
outer3:
                ;
            }

            return(solution.ToString());
        }