private BlockSet ExploreNewCoset(BlockSet startState, CubeAction generator) { bool foundNew = false; var newState = new BlockSet(startState); generator.Act(newState.State); // To verify generators truely stablizes Stablized BlockSet if (Utils.ShouldVerify()) { foreach (var stablePos in Stablized.Indexes) { if (!newState.State.Blocks[stablePos] .Equals(startState.State.Blocks[stablePos])) { throw new ArgumentException(); } } } if (!OrbitToCoset.ContainsKey(newState)) { foundNew = true; var startCoset = OrbitToCoset[startState]; Utils.DebugAssert(startCoset != null); var newCoset = generator.Mul(startCoset); newCoset.Simplify(CubeAction.SimplifyLevel.Level0); OrbitToCoset.Add(newState, newCoset); } return(foundNew ? newState : null); }
private HashSet <BlockSet> ExploreExistingCosetsByNewGenerator(CubeAction newGenerator) { var newStates = new HashSet <BlockSet>(); int walkedCount = 0; var needWalkStates = new HashSet <BlockSet>(OrbitToCoset.Keys); foreach (var startState in needWalkStates) { walkedCount++; var newState = ExploreNewCoset(startState, newGenerator); if (newState != null) { newStates.Add(newState); Console.WriteLine( $"Stablized[{Stablized.Indexes.Count}] " + $"ExploreNewGeneratorOnExistingCosets: foundCount/needWalk/total=" + $"{newStates.Count}/{needWalkStates.Count - walkedCount}/{OrbitToCoset.Count} " + $"newState=[{newState}] newCoset=[{OrbitToCoset[newState].Count()}] " + $"startState=[{startState}] generator=[{newGenerator.Count()}]"); } } return(newStates); }
private HashSet <CubeAction> ObtainGeneratorsOfStablizerSubgroupIncrementally( CubeAction newGenerator, HashSet <BlockSet> newStates) { var newSubgroupGenerators = new HashSet <CubeAction>(); foreach (var generator in Generators) { bool isNewGenerator = generator.Equals(newGenerator); foreach (var state in OrbitToCoset.Keys) { var leftCoset = OrbitToCoset[state]; Utils.DebugAssert(leftCoset != null); if (!isNewGenerator && !newStates.Contains(state)) { // Old generator, old coset state continue; } var subgroupGenerator = ObtainGeneratorOfStablizerSubgroup(generator, leftCoset); if (!subgroupGenerator.Equals(new CubeAction()) && !newSubgroupGenerators.Contains(subgroupGenerator)) { Utils.DebugAssert(ToStablize.IsStablizedBy(subgroupGenerator)); newSubgroupGenerators.Add(subgroupGenerator); /* No need to print because we don't know whether the generator is redundant here. */ } } } return(newSubgroupGenerators); }
public static void VerifyGroupActionAssociaty() { for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeAction a = CubeAction.Random(Utils.GlobalRandom.Next(1, 20)); CubeAction b = CubeAction.Random(Utils.GlobalRandom.Next(1, 15)); CubeAction c = CubeAction.Random(Utils.GlobalRandom.Next(1, 10)); CubeAction ab = a.Mul(b); CubeAction bc = b.Mul(c); CubeState origState = CubeAction.RandomCube(Utils.GlobalRandom.Next(1, 20)); CubeState a_b_c_state = new CubeState(origState); c.Act(a_b_c_state); b.Act(a_b_c_state); a.Act(a_b_c_state); CubeState ab_c_state = new CubeState(origState); c.Act(ab_c_state); ab.Act(ab_c_state); CubeState a_bc_state = new CubeState(origState); bc.Act(a_bc_state); a.Act(a_bc_state); Utils.DebugAssert(a_b_c_state.Equals(ab_c_state)); Utils.DebugAssert(a_b_c_state.Equals(a_bc_state)); } }
public override LocalAction checkMutation(LocalApplication app, Index p, Vector3 diff, float voxelSize) { CubeApp cApp = (CubeApp)app; CubeAction action = new CubeAction(); if (p.depth >= app.tree.maximumDetail) voxelSize *= 0.5f; action.percentInside = 1; bool outside = false; bool inside = true; action.percentInside *= 1 - (2 - percentOverlapping(diff.x, cApp.halfDimension.x, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.x, cApp.halfDimension.x, voxelSize, ref outside, ref inside)); if (outside) return action; action.percentInside *= 1 - (2 - percentOverlapping(diff.y, cApp.halfDimension.y, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.y, cApp.halfDimension.y, voxelSize, ref outside, ref inside)); if (outside) return action; action.percentInside *= 1 - (2 - percentOverlapping(diff.z, cApp.halfDimension.z, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.z, cApp.halfDimension.z, voxelSize, ref outside, ref inside)); if (outside) return action; action.modify = true; if (!overwriteShape || !inside) action.doTraverse = true; return action; }
public static void VerifyOpCountForAccelerationMap() { CubeState state = new CubeState(); CubeAction actionNoAcc = CubeAction.Random(CubeAction.OpCountForAccelerationMap - 1); CubeAction actionAcc = CubeAction.Random(CubeAction.OpCountForAccelerationMap); Console.WriteLine( "Expecting timeNoAcc is similar to timeAcc, " + "otherwise adjust CubeAction.OpCountForAccelerationMap"); for (int i = 0; i < 5; i++) { long timeStart = Utils.CurrentTimeMillis(); for (int j = 0; j < 500; j++) { actionNoAcc.Act(state); } long timeNoAcc = Utils.CurrentTimeMillis(); for (int j = 0; j < 500; j++) { actionAcc.Act(state); } long timeAcc = Utils.CurrentTimeMillis(); Console.WriteLine($"timeNoAcc={timeNoAcc - timeStart}ms, timeAcc={timeAcc - timeNoAcc}ms"); } }
// Returns (i, j) that, i is the first element that the action doesn't stablize. // j is what it maps to at i. Obviously j > i. Note: returning (-1, -1) means the // action stablizes all public Tuple <int, int> GetActionPair(CubeAction action) { int pair_i = -1; int pair_j = -1; var actionMap = action.GetAccelerationMap(); for (int idxInStablizerOrder = 0; idxInStablizerOrder < StablizingOrder.Count; idxInStablizerOrder++) { int idxInActionMap = StablizingOrder[idxInStablizerOrder]; if (actionMap.ColorMap[idxInActionMap] == idxInActionMap) { continue; } pair_i = idxInStablizerOrder; int pair_j_inActionMap = actionMap.ColorMap[idxInActionMap]; pair_j = IdxInStablizingOrder(pair_j_inActionMap); Utils.DebugAssert(pair_j > pair_i); break; } return(new Tuple <int, int>(pair_i, pair_j)); }
public bool IsStablizedBy(CubeAction action) { var actionCopy = new BlockSet(this); action.Act(actionCopy.State); return(this.Equals(actionCopy)); }
public static void VerifyCubeTurnAround() { for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeState cubeState = CubeAction.RandomCube(Utils.GlobalRandom.Next(1, 30)); foreach (CubeOp.Type op in Enum.GetValues(typeof(CubeOp.Type))) { var action = new CubeAction(new int[] { (int)op }); CubeState newCubeState = new CubeState(cubeState); for (int i = 0; i < CubeState.TurnAround; i++) { action.Act(newCubeState); } Utils.DebugAssert(newCubeState.Equals(cubeState)); } } for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeState cubeState = CubeAction.RandomCube(Utils.GlobalRandom.Next(1, 30)); foreach (CubeOp.Type op in Enum.GetValues(typeof(CubeOp.Type))) { var action = new CubeAction(new int[] { (int)op, (int)op, (int)op }); CubeState newCubeState = new CubeState(cubeState); for (int i = 0; i < CubeState.TurnAround; i++) { action.Act(newCubeState); } Utils.DebugAssert(newCubeState.Equals(cubeState)); } } for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeState cubeState = CubeAction.RandomCube(Utils.GlobalRandom.Next(1, 30)); foreach (CubeOp.Type op in Enum.GetValues(typeof(CubeOp.Type))) { var action = new CubeAction(new int[] { (int)op, (int)op }); CubeState newCubeState = new CubeState(cubeState); for (int i = 0; i < CubeState.TurnAround / 2; i++) { action.Act(newCubeState); } Utils.DebugAssert(newCubeState.Equals(cubeState)); } } }
public void UndoLastMove() { if (currentAction == null && actionList.Count != 0) { //Debug.Log("Trying Replay"); CubeAction lastAction = actionList[actionList.Count - 1]; actionList.Remove(lastAction); lastAction.GetUndoAction().StartAction(); currentAction = lastAction.GetUndoAction(); } processUndoRedoPossible(actionList.Count > 0); }
public static void VerifyIdentity() { for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeAction a = CubeAction.Random(Utils.GlobalRandom.Next(1, 10)); CubeAction id = new CubeAction(); CubeAction aid = a.Mul(id); CubeAction ida = id.Mul(a); Utils.DebugAssert(a.Equals(aid)); Utils.DebugAssert(a.Equals(ida)); } }
public static void VerifyAssociaty() { for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeAction a = CubeAction.Random(Utils.GlobalRandom.Next(1, 10)); CubeAction b = CubeAction.Random(Utils.GlobalRandom.Next(1, 15)); CubeAction c = CubeAction.Random(Utils.GlobalRandom.Next(1, 20)); CubeAction ab_c = a.Mul(b).Mul(c); CubeAction a_bc = a.Mul(b.Mul(c)); Utils.DebugAssert(ab_c.Equals(a_bc)); Utils.DebugAssert(a_bc.Equals(ab_c)); } }
public static void VerifyReverse() { for (int caseIdx = 0; caseIdx < 100; caseIdx++) { CubeAction a = CubeAction.Random(Utils.GlobalRandom.Next(1, 20)); CubeAction ra = a.Reverse(); CubeAction id = new CubeAction(); CubeAction ara = a.Mul(ra); CubeAction raa = ra.Mul(a); Utils.DebugAssert(id.Equals(ara)); Utils.DebugAssert(id.Equals(raa)); } }
public override Voxel mutate(LocalApplication app, Index p, LocalAction action, Voxel original) { CubeAction cAction = (CubeAction)action; byte newOpacity = (byte)((original.averageOpacity() * (1 - cAction.percentInside) + value.averageOpacity() * (cAction.percentInside))); byte newSubstance = original.averageMaterialType(); if (newOpacity >= 2 * original.averageOpacity() || (overwriteSubstance && cAction.percentInside > 0.5)) { newSubstance = value.averageMaterialType(); } if (!overwriteShape) { newOpacity = original.averageOpacity(); } return(new Voxel(newSubstance, newOpacity)); }
private void ForEachInCube(CubeAction action) { for (var y = 0; y < _area.Height; y++) { for (var x = 0; x < _area.Width; x++) { var offset = _area.GetOffset(x, y); var plantInfo = _plantInfos[offset]; var blockInfo = _blockInfos[offset]; action(x, y, ref plantInfo, ref blockInfo); _plantInfos[offset] = plantInfo; _blockInfos[offset] = blockInfo; } } }
/// <summary> /// By (slightly changed version of) Prop 4.7 in Group Theory J.S. Milne, we know the /// orbit of the blocks we are observing, is 1-on-1 mapping to the set of *right* cosets /// divided by stablizer subgroup of the blocks we are observing. /// /// See: https://www.jmilne.org/math/CourseNotes/GT310.pdf /// /// In this way, by traversal through each possible state of the the blocks we are observing, /// we can discover each of the cosets of the stablizer subgroup, which will later be input /// into Schreier subgroup lemma to obtain the stablizer subgroup's generators. /// </summary> private HashSet <BlockSet> ExploreOrbitToCosetIncrementally(CubeAction newGenerator) { if (null == OrbitToCoset) { OrbitToCoset = new Dictionary <BlockSet, CubeAction>() { { new BlockSet(ToStablize), new CubeAction() } }; } var newStates = new HashSet <BlockSet>(); var fullyWalkedStates = new HashSet <BlockSet>(OrbitToCoset.Keys); while (true) { int foundCount = 0; if (!Generators.Contains(newGenerator)) { var localNewStates = ExploreExistingCosetsByNewGenerator(newGenerator); foundCount += localNewStates.Count; newStates.UnionWith(localNewStates); Generators.Add(newGenerator); } { var localNewStates = ExploreNewCosetsByExistingGenerator(fullyWalkedStates); foundCount += localNewStates.Count; newStates.UnionWith(localNewStates); fullyWalkedStates = new HashSet <BlockSet>(OrbitToCoset.Keys); } if (foundCount <= 0) { break; } } return(newStates); }
private CubeAction DetermineBelongingCoset(CubeAction e) { var eState = new BlockSet(ToStablize); e.Act(eState.State); if (!OrbitToCoset.ContainsKey(eState)) { return(null); } var cosetRepresentative = OrbitToCoset[eState]; if (Utils.ShouldVerify()) { { var cosetReprState = new BlockSet(ToStablize); cosetRepresentative.Act(cosetReprState.State); Utils.DebugAssert(cosetReprState.Equals(eState)); } { // States in orbit 1-to-1 maps to each *left* coset (gH). I.e. // iff. e^(-1) * cosetRepresentative stablizes the BlockSet being // observed. This deduces that, group actions in same *left* // coset, always act the BlockSet being observed to the same state. var reCosetRep = e.Reverse().Mul(cosetRepresentative); Utils.DebugAssert(Stablized.IsStablizedBy(reCosetRep)); } { // Iff. e * cosetRepresentative^(-1) stablizes the BlockSet being // observed. This is the condition for *right* coset. It is not what // we need here, and group actions in same *right* coset, may act the // BlockSet being observed to different states. var eRCosetRep = e.Mul(cosetRepresentative.Reverse()); // Utils.DebugAssert(observed.IsStablizedBy(eRCosetRep)); // Doesn't hold } } return(cosetRepresentative); }
/// <summary> /// To obtain the generators of stablizer subgroup, by Schreier subgroup lemma as stated /// at https://www.jaapsch.net/puzzles/schreier.htm. We need to input the generators of /// group, and the sets of cosets of the stablizer subgroup. /// </summary> private CubeAction ObtainGeneratorOfStablizerSubgroup( CubeAction generator, CubeAction leftCoset) { // To match naming in Schreier subgroup lemma; var s = generator; var r = leftCoset; var sr = s.Mul(r); // In theory, sr's coset should always be already known. Because each known // coset representative is generated by permutations of known generators. // And we have already explore that. So permutations of known generators, // i.e. sr, should never give us any new coset. var cosetReprSr = DetermineBelongingCoset(sr); Utils.DebugAssert(cosetReprSr != null); var rCosetReprSr = cosetReprSr.Reverse(); var subgroupGenerator = rCosetReprSr.Mul(sr); return(subgroupGenerator); }
public override LocalAction checkMutation(LocalApplication app, Index p, Vector3 diff, float voxelSize) { CubeApp cApp = (CubeApp)app; CubeAction action = new CubeAction(); if (p.depth >= app.tree.maxDetail) { voxelSize *= 0.5f; } action.percentInside = 1; bool outside = false; bool inside = true; action.percentInside *= 1 - (2 - percentOverlapping(diff.x, cApp.halfDimension.x, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.x, cApp.halfDimension.x, voxelSize, ref outside, ref inside)); if (outside) { return(action); } action.percentInside *= 1 - (2 - percentOverlapping(diff.y, cApp.halfDimension.y, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.y, cApp.halfDimension.y, voxelSize, ref outside, ref inside)); if (outside) { return(action); } action.percentInside *= 1 - (2 - percentOverlapping(diff.z, cApp.halfDimension.z, voxelSize, ref outside, ref inside) - percentOverlapping(-diff.z, cApp.halfDimension.z, voxelSize, ref outside, ref inside)); if (outside) { return(action); } action.modify = true; if (!inside) { action.doTraverse = true; } return(action); }
public CubeAction generateRandomAction() { int random = UnityEngine.Random.Range(0, 5); CubeAction action = null; if (random == 0) { action = new ActionRotateZXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } else if (random == 1) { action = new ActionRotateZXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } else if (random == 2) { action = new ActionRotateZYClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } else if (random == 3) { action = new ActionRotateZYCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } else if (random == 4) { action = new ActionRotateYXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } else if (random == 5) { action = new ActionRotateYXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTiles[UnityEngine.Random.Range(0, cubeTiles.Count - 1)])), true); } if (action == null) { Debug.Log("ERROR"); } action.SetTimeToComplete(0.3f); return(action); }
public static ActionSaveData CreateSaveData(CubeAction cubeAction) { ActionSaveData savedata = new ActionSaveData(); if (cubeAction.GetType() == typeof(ActionRotateZXClockWise)) { savedata.action = (int)ActionType.ZXClock; } else if (cubeAction.GetType() == typeof(ActionRotateZXCounterClockWise)) { savedata.action = (int)ActionType.ZXCounterClock; } else if (cubeAction.GetType() == typeof(ActionRotateZYClockWise)) { savedata.action = (int)ActionType.ZYClock; } else if (cubeAction.GetType() == typeof(ActionRotateZYCounterClockWise)) { savedata.action = (int)ActionType.ZYCounterClock; } else if (cubeAction.GetType() == typeof(ActionRotateYXClockWise)) { savedata.action = (int)ActionType.YXClock; } else if (cubeAction.GetType() == typeof(ActionRotateYXCounterClockWise)) { savedata.action = (int)ActionType.YXCounterClock; } foreach (var cubeTile in cubeAction.GetCubeTileListReference()) { savedata.cubeCollection.Add(cubeTile.GetComponent <CubeTileInfo>().GetID()); } return(savedata); }
internal void LoadGame(MagicCubeSaveData savedata) { bIsLoading = true; foreach (var item in cubeTiles) { UnityEngine.Object.Destroy(item); } cubeTiles.Clear(); collectionHelper = new CubeCollectionHelper(0); for (int i = 0; i < savedata.positions.Count; i++) { var temp = GameObject.Instantiate(cubeTileRef, savedata.positions[i].ToVector3() , savedata.rotations[i].ToQuaternion()); collectionHelper.ProcessCubeTile(temp, temp.transform.position); temp.transform.SetParent(ownHolder.transform); cubeTiles.Add(temp); temp.GetComponent <CubeTileInfo>().SetID(savedata.cubeTileIDs[i]); } actionList.Clear(); for (int i = 0; i < savedata.undoActions.Count; i++) { CubeAction action = null; List <GameObject> cubeTilesForAction = new List <GameObject>(); foreach (var cubeID in savedata.undoActions[i].cubeCollection) { cubeTilesForAction.Add(cubeTiles.Find(ct => ct.GetComponent <CubeTileInfo>().GetID() == cubeID)); } switch (savedata.undoActions[i].action) { case (int)ActionSaveData.ActionType.ZXClock: action = new ActionRotateZXClockWise(ownHolder, this, cubeTilesForAction, true); break; case (int)ActionSaveData.ActionType.ZXCounterClock: action = new ActionRotateZXCounterClockWise(ownHolder, this, cubeTilesForAction, true); break; case (int)ActionSaveData.ActionType.YXClock: action = new ActionRotateYXClockWise(ownHolder, this, cubeTilesForAction, true); break; case (int)ActionSaveData.ActionType.YXCounterClock: action = new ActionRotateYXCounterClockWise(ownHolder, this, cubeTilesForAction, true); break; case (int)ActionSaveData.ActionType.ZYClock: action = new ActionRotateZYClockWise(ownHolder, this, cubeTilesForAction, true); break; case (int)ActionSaveData.ActionType.ZYCounterClock: action = new ActionRotateZYCounterClockWise(ownHolder, this, cubeTilesForAction, true); break; } if (action == null) { actionList.Clear(); break; } action.CompleteActionNoAnimation(); actionList.Add(action); } timePassed = savedata.time; seconds = savedata.time; updateTime(seconds); processUndoRedoPossible(actionList.Count > 0); bIsLoading = false; }
public void QuerySliceRotation(GameObject cubeTile, Collider startFace, MagicCubeBehaviour.SwipeDragDirection direction) { MagicCubeSide side = GetWhichSide(startFace); switch (side) { case MagicCubeSide.PosX: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateYXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateYXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; default: break; } break; case MagicCubeSide.NegX: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateYXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateYXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; default: break; } break; case MagicCubeSide.PosY: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateYXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateYXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZYClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZYCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; default: break; } break; case MagicCubeSide.NegY: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateYXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateYXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnZTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZYClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZYCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; default: break; } break; case MagicCubeSide.PosZ: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateZYClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateZYCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; default: break; } break; case MagicCubeSide.NegZ: switch (direction) { case MagicCubeBehaviour.SwipeDragDirection.UP: currentAction = new ActionRotateZYClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.DOWN: currentAction = new ActionRotateZYCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetColumnXTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.LEFT: currentAction = new ActionRotateZXClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; case MagicCubeBehaviour.SwipeDragDirection.RIGHT: currentAction = new ActionRotateZXCounterClockWise(ownHolder, this, new List <GameObject>(collectionHelper.GetRowTiles(cubeTile)), true); break; default: break; } break; default: break; } if (currentAction != null) { AddActionToUndoList(currentAction); } }
static void Main(string[] args) { string CONSOLE_OUTPUT_FILE = "console.out.txt"; using (var fileWriter = new StreamWriter(File.Create(CONSOLE_OUTPUT_FILE))) { // // Setup // fileWriter.AutoFlush = true; var consoleMirrorFile = new Utils.MirroredWriter(fileWriter, Console.Out); Console.SetOut(consoleMirrorFile); // // Verifying basics // GroupTests.VerifyAll(); // // Calculating the map to solve Rubik's Cube // CubeSolution cs = new CubeSolution(); cs.SolveCosetMap(); Console.Out.Flush(); // // Dump the solved coset map of the cube // cs.DumpGSteps(); Console.Out.Flush(); // // Solve a Rubik's Cube // const int SETUP_ACTION_LENGTH_LIMIT = 1000; const int CASE_COUNT = 2; for (int caseIdx = 0; caseIdx < CASE_COUNT; caseIdx++) { var setupAction = CubeAction.Random( Utils.GlobalRandom.Next(SETUP_ACTION_LENGTH_LIMIT)); Console.WriteLine( $"SolvingCube[case={caseIdx}]: " + $"setupAction=[Size={setupAction.Count()}, Action=[{setupAction}]]"); CubeState setupState = new CubeState(); setupAction.Act(setupState); CubeState solvingState = new CubeState(setupState); var steps = cs.SolveCube(solvingState); int stepIdx = 0; foreach (var step in steps) { string actionStr = step.Item1.ToStringWithFormula(); Console.WriteLine( $"SolvingCube[case={caseIdx}]: stepIdx={stepIdx} " + $"stepAction=[Size={step.Item1.Count()}, Action=[{actionStr}]] " + $"cubeState=["); Console.WriteLine($"{step.Item2}]"); stepIdx++; } Console.Out.Flush(); } // // Simplify the generate cosets // cs.SimplifyCosets(); Console.Out.Flush(); } }
public CubeAction FilterGeneratorIncrementally(CubeAction newGenerator) { if (AcceptedGeneratorCount >= GeneratorCountLimit) { Utils.DebugAssert(AcceptedGeneratorCount == GeneratorCountLimit); if (!Utils.ShouldVerify()) { // To verify if generator limit reached, we won't be able to // add more generators return(null); } } var pair = GetActionPair(newGenerator); if (pair.Item1 < 0) { if (Utils.ShouldVerify()) { Utils.DebugAssert(newGenerator.Equals(new CubeAction())); } return(null); } if (pair.Item1 <= StablizedIdx) { // This means the newGenerator didn't stablize the required cube blocks throw new ArgumentException(); } CubeAction replacedGenerator; var existingGenerator = ActionGrid[pair.Item1, pair.Item2]; if (null == existingGenerator) { Utils.DebugAssert(null == ActionGrid[pair.Item2, pair.Item1]); var cyclePath = DetectCycle( pair.Item1, pair.Item1, pair.Item2, new List <Tuple <int, int> >() { new Tuple <int, int>(pair.Item1, pair.Item2) }); if (null == cyclePath) { // Note: g's ActionPair is (i, j) doesn't means g^(-1) ActionPair is (j, i). // We store g^(-1) here just for convenience. But g^(-1) must map j to i. ActionGrid[pair.Item1, pair.Item2] = newGenerator; ActionGrid[pair.Item2, pair.Item1] = newGenerator.Reverse(); Utils.DebugAssert(AcceptedGeneratorCount < GeneratorCountLimit); AcceptedGeneratorCount++; return(newGenerator); } else { // // Temporarily put the newGenerator in. We will remove another generator // to break the cycle. // ActionGrid[pair.Item1, pair.Item2] = newGenerator; ActionGrid[pair.Item2, pair.Item1] = newGenerator.Reverse(); cyclePath = RearrangeCycleFromSmallest(cyclePath); replacedGenerator = CalculateCyclePathProduct(cyclePath); ActionGrid[cyclePath[0].Item1, cyclePath[0].Item2] = null; ActionGrid[cyclePath[0].Item2, cyclePath[0].Item1] = null; if (Utils.ShouldVerify()) { var replacedPair = GetActionPair(replacedGenerator); if (replacedPair.Item1 >= 0) { // This ensures the recursive call will end Utils.DebugAssert(replacedPair.Item1 > cyclePath[0].Item1); } } } } else { var reversedExistingGenerator = ActionGrid[pair.Item2, pair.Item1]; if (Utils.ShouldVerify()) { Utils.DebugAssert(reversedExistingGenerator.Equals(existingGenerator.Reverse())); } replacedGenerator = reversedExistingGenerator.Mul(newGenerator); if (Utils.ShouldVerify()) { var replacedPair = GetActionPair(replacedGenerator); if (replacedPair.Item1 >= 0) { // This ensures the recursive call will end Utils.DebugAssert(replacedPair.Item1 > pair.Item1); } } } JumpCount++; return(FilterGeneratorIncrementally(replacedGenerator)); }
public void AddActionToUndoList(CubeAction action) { actionList.Add(currentAction); processUndoRedoPossible(actionList.Count > 0); }
/// <summary> /// Stablizer chain algorithm templated from https://www.jaapsch.net/puzzles/schreier.htm. /// /// __Basic stablizer chain algorithm__ /// /// Each GStep in the stablizer chain corresponds to gradually more blocks were rotated to /// the ideal cube position. Since the next GStep stablizes more blocks, it's the subgroup /// of the previous GStep. /// /// To obtain the next GStep, we obtain its generators. Subgroup generators can be obtained /// by Schreier subgroup lemma. It needs the set of cosets, i.e. coset representatives, and /// the parent group generators. /// /// Coset representatives can be obtained by repeating permutation of parent group generators. /// We know coset representatives are 1-on-1 mapping to block states we are trying to stablize. /// So we can find whether coset representatives are equal, or whether we walked all of them. /// /// So, giving parent group generators, we can obtain subgroup generators. Recursively, we /// walk along the stablizer chain of GSteps, until we solved all blocks. /// /// __Incremental stablizer chain algorithm__ /// /// The problem is, the count of generators grow exponentially along the stablizer chain. /// We want to know whether a generator is not necessary, i.e. this generator and all descendants /// generators discovered by it, won't help us find any new cosets. /// /// The cosets at each GStep stablizer chain are the final results we want. Because given a cube /// state, we use 1-on-1 mapping to know its coset representative, We use the reverse of the coset /// representative to rotate the cube to ideal position. We do it along the stablizer chain, we /// then solve the cube. /// /// That's why, if a generator discovers no new coset, the generator is not necessary. We can then /// rewrite the algorithm in the new way, to incrementally add generators one by one. If a generator /// is found not necessary, we then discard it, so that it won't further exponentially increase our /// computation overhead. /// </summary> public int AddGeneratorIncrementally(CubeAction newGenerator, List <ProgressInfo> progressInfoList) { if (null == Generators) { Generators = new HashSet <CubeAction>(); } if (null == RejectedGenerators) { RejectedGenerators = new HashSet <CubeAction>(); } if (RejectedGenerators.Contains(newGenerator)) { return(0); } if (PrintProgress) { foreach (var p in progressInfoList) { Console.Write($"{p.StablizedCount}:{p.CompletedWork}/{p.TotalWork} "); } Console.WriteLine(); Console.WriteLine( $"{new string(' ', Stablized.Indexes.Count)}" + $"{Stablized.Indexes.Count} - G:{newGenerator.Count()} " + $"FC:{GeneratorFilter.JumpCount} GC:{Generators.Count} " + $"CC:{(OrbitToCoset != null ? OrbitToCoset.Count : 0)} RJ:{RejectedGenerators.Count}"); } var filteredGenerator = GeneratorFilter.FilterGeneratorIncrementally(newGenerator); if (filteredGenerator != null) { newGenerator = filteredGenerator; } else { RejectedGenerators.Add(newGenerator); return(0); } if (Generators.Contains(newGenerator)) { Utils.DebugAssert(false); return(0); } ProgressInfo progressInfo = null; int foundStateCount = 0; { var newStates = ExploreOrbitToCosetIncrementally(newGenerator); foundStateCount += newStates.Count; var newSubgroupGenerators = ObtainGeneratorsOfStablizerSubgroupIncrementally( newGenerator, newStates); if (Next != null) { progressInfo = new ProgressInfo(Stablized.Indexes.Count, newSubgroupGenerators.Count); progressInfoList.Add(progressInfo); foreach (var subgroupGenerator in newSubgroupGenerators) { foundStateCount += Next.AddGeneratorIncrementally(subgroupGenerator, progressInfoList); progressInfo.CompletedWork++; } } } Utils.DebugAssert(GeneratorFilter.AcceptedGeneratorCount == Generators.Count); Console.WriteLine( $"Stablized[{Stablized.Indexes.Count}] " + $"AddGeneratorIncrementally: Accepted new generator: " + $"foundStateCount={foundStateCount} Generators={Generators.Count} " + $"Cosets={OrbitToCoset.Count} FilterCount={GeneratorFilter.JumpCount} " + $"newGenerator=[{newGenerator.Count()}]"); if (progressInfo != null) { progressInfoList.RemoveAt(progressInfoList.Count - 1); } return(foundStateCount); }
public void Update() { if (bProcessingRandomActions) { randomActionsTimePassed += Time.deltaTime; if (randomActionsTimePassed > randomActionTimer) { bProcessingRandomActions = false; return; } if (currentAction != null) { if (!currentAction.IsActionDone()) { currentAction.Update(); } else { currentAction = null; } } if (currentAction == null) { currentAction = generateRandomAction(); } return; } if (currentAction != null) { if (!currentAction.IsActionDone()) { currentAction.Update(); } else { currentAction = null; //After a move is fully completed check whether the game is finished or not if (CheckIfGameIsFinished()) { FinishGame(); } } } if (!bGameIsFinished) { timePassed += Time.deltaTime; if ((int)timePassed > seconds) { seconds = (int)timePassed; updateTime(seconds); } } bool bIsPinching = Input.touchCount >= 2; if (Input.GetMouseButtonDown(1) && !bIsPinching) { MainGameLogic.GetMainCamera().GetComponent <UIManager>().OpenGameMenu(); } }