Exemple #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 = 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)
                {
                    azzert(false, e.Message);
                    return(null);
                }
            }
            return(ab.GetStateAndGenerator());
        }
Exemple #2
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;

            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)
                {
                    seenExtending      = seenSolved;
                    fringeExtending    = fringeSolved;
                    seenComparing      = seenScrambled;
                    minComparingFringe = minFringeScrambled;
                }
                else
                {
                    seenExtending      = seenScrambled;
                    fringeExtending    = fringeScrambled;
                    seenComparing      = seenSolved;
                    minComparingFringe = minFringeSolved;
                }

                var node     = fringeExtending.Pop();
                var distance = seenExtending[node];
                if (seenComparing.ContainsKey(node))
                {
                    // We found an intersection! Compute the total cost of the
                    // path going through this node.
                    var 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.
                var bestPossibleSolution = distance + minComparingFringe;
                if (bestPossibleSolution >= bestIntersectionCost)
                {
                    continue;
                }
                if (distance >= (n + 1) / 2)
                {
                    continue;
                }


                var movesByState = node.GetCanonicalMovesByState();
                foreach (var next in movesByState.Keys)
                {
                    var moveCost     = node.GetMoveCost(movesByState[next]);
                    var nextDistance = distance + moveCost;
                    //next = next.getNormalized();
                    var 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

            azzert(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;
                }
                azzert(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)
                    {
                        azzert(false, e.Message);
                    }
                    distanceFromScrambled = seenScrambled[state.GetNormalized()];
                    goto outer2;
                }
                azzert(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)
                    {
                        azzert(false, e.Message);
                    }
                    goto outer3;
                }
                azzert(false);
outer3:
                ;
            }

            return(solution.ToString());
        }