/** * 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()); }
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()); }
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()); }
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); } }
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()); }
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()); }