/// <summary> /// Grabs a set of undo twists we want to apply all at once. /// </summary> private void ApplyUndoBlockInstantaneously(SingleTwist start) { m_currentTwist = start; FinishRotate(updateStatus: false); if (!start.MacroStart) { SingleTwist undo; while (m_twistHistory.GetUndoTwist(out undo)) { m_currentTwist = undo; FinishRotate(updateStatus: false); if (undo.MacroStart) { break; } } } // ZZZ - could be smarter and invalidate less. InvalidateAllAndUpdateStatus(); // Bump the solve to continue. if (this.Solving) { Solve(); } }
private void FinishRotate(bool updateStatus = true) { //Trace.WriteLine( "FinishRotate " ); m_timer.Enabled = false; m_rotation = 0; // Update the state. m_puzzle.UpdateState(m_currentTwist); BeepIfSolved(); // Track the history. // NOTE: This needs to happen after the 'Undoing' check in BeepIfSolved method above. m_twistHistory.Update(m_currentTwist); m_setupMoves.Update(m_currentTwist); m_workingMacro.Update(m_currentTwist); m_currentTwist.IdentifiedTwistData.EndTwist(m_currentTwist.SliceMask, m_puzzle.IsSpherical); if (m_currentTwist.IdentifiedTwistDataEarthquake != null) { m_currentTwist.IdentifiedTwistDataEarthquake.EndTwist(m_currentTwist.SliceMask, m_puzzle.IsSpherical); } m_currentTwist = null; if (updateStatus) { m_status(); } }
/// <summary> /// NOTE: This is not meant to be called when undoing/redoing. /// </summary> private void ApplyMacroTwistsInstantaneously(SingleTwist[] twists) { for (int i = 0; i < twists.Length; i++) { m_currentTwist = twists[i]; // Don't mark as macro if there is only one twist in the macro. if (twists.Length > 1) { if (i == 0) { m_currentTwist.MacroStart = true; } if (i == twists.Length - 1) { m_currentTwist.MacroEnd = true; } } FinishRotate(updateStatus: false); } // ZZZ - could be smarter and invalidate less. InvalidateAllAndUpdateStatus(); }
/// <summary> /// Compare two twists. /// NOTE: This ignores the MacroStart/MacroEnd properties. /// </summary> public bool Compare(SingleTwist other) { return (this.IdentifiedTwistData == other.IdentifiedTwistData && this.LeftClick == other.LeftClick && this.SliceMask == other.SliceMask); }
/// <summary> /// Adds a twist to our history. /// </summary> public void Update(SingleTwist twist) { if (m_undoMode) { // Remove from twist list. m_twists.RemoveAt(m_twists.Count - 1); // Save in our redo list. SingleTwist temp = twist.Clone(); temp.ReverseTwist(); m_redoTwists.Add(temp); m_undoMode = false; return; } if (m_redoMode) { m_redoTwists.RemoveAt(m_redoTwists.Count - 1); m_redoMode = false; } else { m_redoTwists.Clear(); } // This block should apply to normal twists and redo twists. m_twists.Add(twist); }
public void PuzzleUpdated(Puzzle puzzle) { m_puzzle = puzzle; m_twistHistory = m_puzzle.TwistHistory; m_setupMoves.Reset(); m_workingMacro.Reset(); m_currentTwist = null; }
public void Update(SingleTwist twist) { if (m_recordingSetup) { m_setupMoves.Update(twist); } if (m_recordingCommutator) { m_commutatorMoves.Update(twist); } }
/// <summary> /// Checks if we are an undo of another twist. /// </summary> public bool IsUndo(SingleTwist other) { if (other == null) { return(false); } SingleTwist clone = this.Clone(); clone.ReverseTwist(); return(clone.Compare(other)); }
public SingleTwist Clone() { SingleTwist newTwist = new SingleTwist(); newTwist.IdentifiedTwistData = this.IdentifiedTwistData; newTwist.IdentifiedTwistDataEarthquake = this.IdentifiedTwistDataEarthquake; newTwist.LeftClick = this.LeftClick; newTwist.SliceMask = this.SliceMask; newTwist.SliceMaskEarthquake = this.SliceMaskEarthquake; newTwist.MacroStart = this.MacroStart; newTwist.MacroEnd = this.MacroEnd; return(newTwist); }
/// <summary> /// Get redo rotation parameters. /// Calling this will set us in the "redo" state for the next rotation. /// Returns false if there are no more twists to redo. /// </summary> public bool GetRedoTwist(out SingleTwist twist) { twist = null; if (0 == m_redoTwists.Count) { return(false); } twist = m_redoTwists[m_redoTwists.Count - 1]; m_redoMode = true; return(true); }
/// <summary> /// Transforms us into a new macro based on a different click location. /// </summary> public Macro Transform(Cell clickedCell, Vector3D clickedPoint, Puzzle puzzle, bool mouseMotionReflected) { Macro m = this.CloneAllButTwists(); m.SetupMobius(clickedCell, clickedPoint, puzzle, mouseMotionReflected); // Did we have an odd number of view reflections? bool viewReflected = this.ViewReflected ^ m.ViewReflected; Isometry iso1 = new Isometry(m.Mobius, null); Isometry iso2 = new Isometry(this.Mobius, null); if (viewReflected) { iso1 = Isometry.ReflectX() * iso1; } Isometry combined = iso1.Inverse() * iso2; foreach (SingleTwist t in this.m_twists) { // Find the transformed twist data. // NOTE: We choose the one which will be closest to the origin after transformation, // which hopefully won't lead to performance problems. // I initially just used the first TwistDataForStateCalcs list item, // but that led to issues because sometimes it would get transformed // to very near the disk boundary. We'd have run out of cells to // find the correct closest, and the transformed macros got all messed up. TwistData tdOriginal = t.IdentifiedTwistData.TwistDataForStateCalcs .OrderBy(td => combined.Apply(td.Center).MagSquared()) .First(); Vector3D newCenter = combined.Apply(tdOriginal.Center); TwistData tdNew = puzzle.ClosestTwistingCircles(newCenter); SingleTwist tClone = t.Clone(); tClone.IdentifiedTwistData = tdNew.IdentifiedTwistData; // If the reverse state of our transformed twist // has changed, we may need to reverse the new twist. bool reverse = tdOriginal.Reverse ^ tdNew.Reverse; if (reverse ^ viewReflected) // NOTE: Very similar to code in Renderer. { tClone.ReverseTwist(); } m.m_twists.Add(tClone); } return(m); }
/// <summary> /// Get undo rotation parameters. /// Calling this will set us in the "undo" state for the next rotation. /// Returns false if there are no more twists to undo. /// </summary> public bool GetUndoTwist(out SingleTwist twist) { twist = null; if (0 == m_twists.Count) { return(false); } twist = m_twists[m_twists.Count - 1].Clone(); twist.ReverseTwist(); m_undoMode = true; return(true); }
public void StartRotate(SingleTwist twist) { if (this.Twisting) { return; } m_rotation = 0; m_currentTwist = twist; m_currentTwist.IdentifiedTwistData.StartTwist(m_currentTwist.SliceMask, m_puzzle.IsSpherical); if (m_currentTwist.IdentifiedTwistDataEarthquake != null) { m_currentTwist.IdentifiedTwistDataEarthquake.StartTwist(m_currentTwist.SliceMask, m_puzzle.IsSpherical); } m_timer.Enabled = true; }
public void Update(SingleTwist twist) { if (!Recording) { return; } // Check for undos. if (m_twists.Count > 0) { SingleTwist last = m_twists.Last(); if (twist.IsUndo(last)) { m_twists.RemoveAt(m_twists.Count - 1); return; } } m_twists.Add(twist); }
/// <summary> /// Grabs a set of redo twists we want to apply all at once. /// </summary> private void ApplyRedoBlockInstantaneously(SingleTwist start) { m_currentTwist = start; FinishRotate(updateStatus: false); if (!start.MacroEnd) { SingleTwist redo; while (m_twistHistory.GetRedoTwist(out redo)) { m_currentTwist = redo; FinishRotate(updateStatus: false); if (redo.MacroEnd) { break; } } } // ZZZ - could be smarter and invalidate less. InvalidateAllAndUpdateStatus(); }
public Mobius MobiusForTwist(Geometry g, SingleTwist twist, double rotation, bool earthquake, bool earthquakeData = false) { Mobius mobius = new Mobius(); if (earthquake) { int seg = earthquakeData ? (twist.SliceMaskEarthquake * 2 + 3) % 6 : (twist.SliceMask * 2 + 3) % 6; Vector3D p1 = this.Pants.Hexagon.Segments[seg].P2; Vector3D p2 = Pants.Hexagon.Segments[seg].P1; if (Pants.Isometry.Reflected) { R3.Core.Utils.SwapPoints(ref p1, ref p2); } System.Diagnostics.Debug.Assert(!Reverse); mobius.Geodesic(Geometry.Hyperbolic, p1, p2, rotation); } else { mobius.Elliptic(g, Center, Reverse ? rotation * -1 : rotation); } return(mobius); }
/// <summary> /// Scramble. /// </summary> /// <param name="numTwists"></param> public void Scramble(int numTwists) { System.Random rand = new System.Random(); List <IdentifiedTwistData> allTwistData = m_puzzle.AllTwistData; if (allTwistData.Count == 0) { return; } bool earthquake = m_puzzle.Config.Earthquake; for (int i = 0; i < numTwists; i++) { m_currentTwist = new SingleTwist(); m_currentTwist.IdentifiedTwistData = allTwistData[rand.Next(allTwistData.Count)]; m_currentTwist.LeftClick = rand.Next(2) == 1; // Try to avoid repeats of last (suggested by Melinda). IdentifiedTwistData last = m_twistHistory.AllTwists.Count == 0 ? null : m_twistHistory.AllTwists.Last().IdentifiedTwistData; if (last != null && allTwistData.Count > 2) { while (last == m_currentTwist.IdentifiedTwistData) { m_currentTwist.IdentifiedTwistData = allTwistData[rand.Next(allTwistData.Count)]; } } else { m_currentTwist.IdentifiedTwistData = allTwistData[rand.Next(allTwistData.Count)]; } TwistData td = m_currentTwist.IdentifiedTwistData.TwistDataForStateCalcs.First(); int numSlices = td.NumSlices; int randomSlice = rand.Next(numSlices); if (!earthquake) { randomSlice += 1; } m_currentTwist.SliceMask = SliceMask.SliceToMask(randomSlice); if (earthquake) { int choppedSeg = m_currentTwist.SliceMask * 2; Vector3D lookup = td.Pants.TinyOffset(choppedSeg); Vector3D reflected = td.Pants.Hexagon.Segments[choppedSeg].ReflectPoint(lookup); TwistData tdEarthQuake = m_puzzle.ClosestTwistingCircles(reflected); m_currentTwist.IdentifiedTwistDataEarthquake = tdEarthQuake.IdentifiedTwistData; // Fix scrambing here. m_currentTwist.SliceMaskEarthquake = tdEarthQuake.Pants.Closest(reflected) / 2; } // Apply the twist. FinishRotate(updateStatus: false); } m_twistHistory.Scrambles += numTwists; InvalidateAllAndUpdateStatus(); }