Rubik's Cube Solver mit 3D-Darstellung

  • C#

Es gibt 85 Antworten in diesem Thema. Der letzte Beitrag () ist von Switcherlapp97.

    So, einzelne Flächen lassen sich nun ansprechen und umfärben.

    C#-Quellcode

    1. rubikManager.setFaceColor(Cube3D.RubikPosition.TopLayer | Cube3D.RubikPosition.RightSlice | Cube3D.RubikPosition.FrontSlice, Face3D.FacePosition.Front, Color.Violet);

    macht

    Um das zu erreichen bekommt jede Fläche noch ein Attribut vom Type FacePosition das ihre Position im Würfel speichert.
    Die Methode SetFaceColor kann dann über Linq die eine Fläche ansprechen:

    C#-Quellcode

    1. public void setFaceColor(Cube3D.RubikPosition affected, Face3D.FacePosition face, Color color)
    2. {
    3. List<Cube3D> affectedCubes = RubikCube.cubes.Where(c => c.Position.HasFlag(affected)).ToList();
    4. affectedCubes.ForEach(c => c.Faces.Where(f => f.Position == face).ToList().ForEach(f => f.Color = color));
    5. }



    Weil diese Änderung einiges nach sich gezogen hat, ist das geupdatate Projekt im Anhang.


    Und btw: Den Würfel kann man jetzt mit der Maus drehen.
    Einfach klicken und bewegen
    Dateien
    • CubeGDI3D.zip

      (166,52 kB, 104 mal heruntergeladen, zuletzt: )
    SᴛᴀʀGᴀᴛᴇ01
    Hm ja die Rotation buggt noch, das liegt daran dass immer um die selben Achsen gedreht wird egal wie der Würfel liegt.
    Man müsste die Achsen halt neu berechnen.
    Und der Vertausch-Effekt tritt dann eben auf wenn man nicht mehr von vorne auf den Würfel schaut ^^
    SᴛᴀʀGᴀᴛᴇ01
    @nafets3646
    Ich habe mir deine Anwendung mal genauer angeschaut. Die Lösung des Cubes wird mit einer externen exe-File berechnet und zurückgegeben. Hast du das selbst geschrieben? Und wenn ja weißt du mit welcher Methode der Cube gelöst wird? Ich denke nicht, dass die schnellstmögliche Lösung vom Programm berechnet wird, da seit ein paar Jahren jeder Rubiks Cube in maximal 20 Zahlen gelöst werden kann (Gotteszahl) und die Lösung die, in deiner Testanwendung angezeigt wird, beträgt deutlich mehr als 20 Schritte. Vermutlich verwendest du die Anfänger-Methode oder die Fridrich-Methode.

    So jetzt noch ein bisschen was Anderes :D: Könnt ihr selbst den Rubiks Cube lösen? Wenn ja, welche Methode verwendet ihr und wie schnell seit ihr ungefähr?
    Ich verwende die Fridrich Methode, allerdings mit 2-Look-OLL (da ich zu faul bin, um 60 verschiedene Algorithmen auswendig zu lernen :P) und ich löse den Cube durchschnittlich in ca. 25 Sekunden.

    Gruß
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Switcherlapp97“ ()

    Es gab mal ne Zeit, in der ich die Cubes einigermaßen schnell lösen konnte, mittlerweile schaff ich das in 30s mit der Methode "Auf den Boden werfen und wieder zusammen bauen".
    @StarGate01
    Ich habe mich nun ein bisschen an den Code für einen Cube Solver gemacht. Ihr habt recht ich könnte einen bereits existierenden Solver verwenden, doch mir macht des Thema momentan sehr viel Spaß und ich würde gerne versuchen, ob ich selbst so etwas in der Art programmieren kann. Dafür wäre es unbedingt notwendig, eine schnelle Rotation durchzuführen, die nicht parallel zum Programmablauf verläuft, so wie die aktuelle Rotation-Methode in der RubikManager-Klasse. Mit schnellen Drehungen kann ich sofort das Ergebnis weiterverarbeiten und kann darauf reagieren und muss nicht auf den Abschluss der 3D-Drehung warten, was ja ziemlich unnötig ist, wenn der Cube in der Klasse ja nur gelöst wird, um die Schritte für die Lösung zurückzugeben. Leider verstehe ich deinen genialen Code noch zu wenig, dass ich so etwas selbst so eine Methode schreiben kann. Ich wäre also sehr froh, wenn du mir ein bisschen auf die Sprünge helfen könntest :S

    PS: Geiles Programm! Hast du echt super gemacht! :thumbsup:

    Vielen Dank
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Switcherlapp97“ ()

    @StarGate01
    Das ist kein Problem, wenn du nicht gleich Zeit findest eine schnelle Rotation zu programmieren. Vielleicht könntest du mir ein paar hilfreiche Tipps und Ansätze geben, wie ich selbst diese Methode programmieren kann. Zum Beispiel in welcher Klasse ich diese Methode programmieren soll. Falls du auch dafür keine Zeit finden solltest, ist das auch nicht weiter schlimm ;). Ich wäre allerdings sehr dankbar dafür.

    Gruß
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom
    Ich würde in der RubikManager-Klasse eine neue Methode einführen, Rotate90Syn(Cube3D.RubikPosition layer, bool direction), die die rotate90-Methode aufruft, mit steps=1, und anschließend gleich danach die Render-Methode, allerdings mit einem Parameter sodass der würfel nicht in echt gerendert wird. Dadurch sollte die Drehung sofort abgeschlossen sein.
    SᴛᴀʀGᴀᴛᴇ01
    @StarGate01
    Wie soll ich die Render-Methode aufrufen? Was muss ich für Werte für die Parameter der RenderMethode angeben? Und welche Render-Methode muss überhaupt aufgerufen werden? Es gibt ja die im RubikManager und die in der Rubik-Klasse.

    Gruß
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom
    Ich hab jetzt schon selbst etwas probiert. Es funktioniert jetzt, aber ich weiß nicht, ob ich es sauber gelöst habe :S Ich habe einfach eine neue Render-Methode ohne Argumente geschrieben. Diese wird dann im Rotate90Sync-void aufgerufen und abgearbeitet.
    Hier mein Ergebnis

    C#-Quellcode

    1. private void Render()
    2. {
    3. RubikCube.LayerRotation[rotationLayer] = rotationTarget;
    4. List<Cube3D> affected = RubikCube.cubes.Where(c => c.Position.HasFlag(rotationLayer)).ToList();
    5. if (rotationLayer == Cube3D.RubikPosition.LeftSlice || rotationLayer == Cube3D.RubikPosition.MiddleSlice_Sides || rotationLayer == Cube3D.RubikPosition.RightSlice)
    6. {
    7. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.X, rotationTarget)));
    8. }
    9. if (rotationLayer == Cube3D.RubikPosition.TopLayer || rotationLayer == Cube3D.RubikPosition.MiddleLayer || rotationLayer == Cube3D.RubikPosition.BottomLayer)
    10. {
    11. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.Y, rotationTarget)));
    12. }
    13. if (rotationLayer == Cube3D.RubikPosition.BackSlice || rotationLayer == Cube3D.RubikPosition.MiddleSlice || rotationLayer == Cube3D.RubikPosition.FrontSlice)
    14. {
    15. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.Z, rotationTarget)));
    16. }
    17. double ed = ((double)2 / (double)3);
    18. for (int i = -1; i <= 1; i++)
    19. {
    20. for (int j = -1; j <= 1; j++)
    21. {
    22. for (int k = -1; k <= 1; k++)
    23. {
    24. //Reset Flags but keep Colors
    25. Cube3D.RubikPosition flags = RubikCube.genSideFlags(i, j, k); ;
    26. Cube3D cube = RubikCube.cubes.First(c => (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.X)) / 24, 4) == Math.Round(i * ed, 4))
    27. && (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.Y)) / 24, 4) == Math.Round(j * ed, 4))
    28. && (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.Z)) / 24, 4) == Math.Round(k * ed, 4)));
    29. cube.Position = flags;
    30. cube.Faces.ToList().ForEach(f => f.MasterPosition = flags);
    31. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    32. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round((j * ed) - (ed / 2), 4))
    33. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Top;
    34. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    35. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round((j * ed) + (ed / 2), 4))
    36. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Bottom;
    37. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round((i * ed) - (ed / 2), 4))
    38. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    39. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Left;
    40. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round((i * ed) + (ed / 2), 4))
    41. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    42. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Right;
    43. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    44. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    45. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round((k * ed) - (ed / 2), 4))).Position = Face3D.FacePosition.Front;
    46. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    47. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    48. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round((k * ed) + (ed / 2), 4))).Position = Face3D.FacePosition.Back;
    49. }
    50. }
    51. }
    52. foreach (Cube3D.RubikPosition rp in (Cube3D.RubikPosition[])Enum.GetValues(typeof(Cube3D.RubikPosition))) RubikCube.LayerRotation[rp] = 0;
    53. BroadcastRotatingFinished();
    54. }
    55. public void Rotate90Sync(Cube3D.RubikPosition layer, bool direction)
    56. {
    57. Rotate90(layer, direction, 1);
    58. Render();
    59. }


    Gruß
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom
    Ich hab es ganz ähnlich gelöst, bei mir wird allerdings beim synchronen Aufruf absichtlich nicht das Event gefeuert.
    Die Methode zum Zurücksetzen der Flags habe ich ausgelagert.

    Spoiler anzeigen

    C#-Quellcode

    1. class RubikManager
    2. {
    3. public Rubik RubikCube;
    4. private Boolean Rotating;
    5. private double rotationStep;
    6. private Cube3D.RubikPosition rotationLayer;
    7. private int rotationTarget;
    8. public delegate void RotatingFinishedHandler(object sender);
    9. public event RotatingFinishedHandler OnRotatingFinished;
    10. private void BroadcastRotatingFinished()
    11. {
    12. if (OnRotatingFinished == null) return;
    13. OnRotatingFinished(this);
    14. }
    15. public struct PositionSpec
    16. {
    17. public Cube3D.RubikPosition cubePos;
    18. public Face3D.FacePosition facePos;
    19. }
    20. public RubikManager(Color cfront, Color cback, Color ctop, Color cbottom, Color cright, Color cleft)
    21. {
    22. RubikCube = new Rubik();
    23. setFaceColor(Cube3D.RubikPosition.FrontSlice, Face3D.FacePosition.Front, cfront);
    24. setFaceColor(Cube3D.RubikPosition.BackSlice, Face3D.FacePosition.Back, cback);
    25. setFaceColor(Cube3D.RubikPosition.TopLayer, Face3D.FacePosition.Top, ctop);
    26. setFaceColor(Cube3D.RubikPosition.BottomLayer, Face3D.FacePosition.Bottom, cbottom);
    27. setFaceColor(Cube3D.RubikPosition.RightSlice, Face3D.FacePosition.Right, cright);
    28. setFaceColor(Cube3D.RubikPosition.LeftSlice, Face3D.FacePosition.Left, cleft);
    29. Rotating = false;
    30. }
    31. public RubikManager() : this(Color.ForestGreen, Color.RoyalBlue, Color.White, Color.Yellow, Color.Red, Color.Orange) { }
    32. public void Rotate90(Cube3D.RubikPosition layer, bool direction, int steps)
    33. {
    34. if (!Rotating)
    35. {
    36. Rotating = true;
    37. rotationLayer = layer;
    38. rotationStep = (double)90 / (double)steps;
    39. if (direction) rotationStep *= (-1);
    40. rotationTarget = 90;
    41. if (direction) rotationTarget = -90;
    42. }
    43. }
    44. public void Rotate90Sync(Cube3D.RubikPosition layer, bool direction)
    45. {
    46. if (!Rotating)
    47. {
    48. Rotate90(layer, direction, 1);
    49. RubikCube.LayerRotation[rotationLayer] += rotationStep;
    50. resetFlags(false);
    51. }
    52. }
    53. public void setFaceColor(Cube3D.RubikPosition affected, Face3D.FacePosition face, Color color)
    54. {
    55. RubikCube.cubes.Where(c => c.Position.HasFlag(affected)).ToList().ForEach(c => c.Faces.Where(f => f.Position == face).ToList().ForEach(f => f.Color = color));
    56. }
    57. public void setFaceSelection(Cube3D.RubikPosition affected, Face3D.FacePosition face, Face3D.SelectionMode selection)
    58. {
    59. RubikCube.cubes.Where(c => c.Position.HasFlag(affected)).ToList().ForEach(c => c.Faces.Where(f => f.Position == face).ToList().ForEach(f => f.Selection = selection));
    60. }
    61. public PositionSpec Render(Graphics g, Rectangle screen, double scale, Point mousePos)
    62. {
    63. PositionSpec result = RubikCube.Render(g, screen, scale, mousePos);
    64. if (Rotating)
    65. {
    66. RubikCube.LayerRotation[rotationLayer] += rotationStep;
    67. if ((rotationTarget > 0 && RubikCube.LayerRotation[rotationLayer] >= rotationTarget) || (rotationTarget < 0 && RubikCube.LayerRotation[rotationLayer] <= rotationTarget))
    68. {
    69. resetFlags(true);
    70. }
    71. }
    72. return result;
    73. }
    74. public void resetFlags(bool fireFinished)
    75. {
    76. RubikCube.LayerRotation[rotationLayer] = rotationTarget;
    77. List<Cube3D> affected = RubikCube.cubes.Where(c => c.Position.HasFlag(rotationLayer)).ToList();
    78. if (rotationLayer == Cube3D.RubikPosition.LeftSlice || rotationLayer == Cube3D.RubikPosition.MiddleSlice_Sides || rotationLayer == Cube3D.RubikPosition.RightSlice)
    79. {
    80. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.X, rotationTarget)));
    81. }
    82. if (rotationLayer == Cube3D.RubikPosition.TopLayer || rotationLayer == Cube3D.RubikPosition.MiddleLayer || rotationLayer == Cube3D.RubikPosition.BottomLayer)
    83. {
    84. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.Y, rotationTarget)));
    85. }
    86. if (rotationLayer == Cube3D.RubikPosition.BackSlice || rotationLayer == Cube3D.RubikPosition.MiddleSlice || rotationLayer == Cube3D.RubikPosition.FrontSlice)
    87. {
    88. affected.ForEach(c => c.Faces.ToList().ForEach(f => f.Rotate(Point3D.RotationType.Z, rotationTarget)));
    89. }
    90. double ed = ((double)2 / (double)3);
    91. for (int i = -1; i <= 1; i++)
    92. {
    93. for (int j = -1; j <= 1; j++)
    94. {
    95. for (int k = -1; k <= 1; k++)
    96. {
    97. //Reset Flags but keep Colors
    98. Cube3D.RubikPosition flags = RubikCube.genSideFlags(i, j, k); ;
    99. Cube3D cube = RubikCube.cubes.First(c => (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.X)) / 24, 4) == Math.Round(i * ed, 4))
    100. && (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.Y)) / 24, 4) == Math.Round(j * ed, 4))
    101. && (Math.Round(c.Faces.Sum(f => f.Edges.Sum(e => e.Z)) / 24, 4) == Math.Round(k * ed, 4)));
    102. cube.Position = flags;
    103. cube.Faces.ToList().ForEach(f => f.MasterPosition = flags);
    104. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    105. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round((j * ed) - (ed / 2), 4))
    106. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Top;
    107. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    108. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round((j * ed) + (ed / 2), 4))
    109. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Bottom;
    110. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round((i * ed) - (ed / 2), 4))
    111. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    112. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Left;
    113. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round((i * ed) + (ed / 2), 4))
    114. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    115. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round(k * ed, 4))).Position = Face3D.FacePosition.Right;
    116. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    117. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    118. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round((k * ed) - (ed / 2), 4))).Position = Face3D.FacePosition.Front;
    119. cube.Faces.First(f => (Math.Round(f.Edges.Sum(e => e.X) / 4, 4) == Math.Round(i * ed, 4))
    120. && (Math.Round(f.Edges.Sum(e => e.Y) / 4, 4) == Math.Round(j * ed, 4))
    121. && (Math.Round(f.Edges.Sum(e => e.Z) / 4, 4) == Math.Round((k * ed) + (ed / 2), 4))).Position = Face3D.FacePosition.Back;
    122. }
    123. }
    124. }
    125. foreach (Cube3D.RubikPosition rp in (Cube3D.RubikPosition[])Enum.GetValues(typeof(Cube3D.RubikPosition))) RubikCube.LayerRotation[rp] = 0;
    126. Rotating = false;
    127. if(fireFinished) BroadcastRotatingFinished();
    128. }
    129. }



    Da hast Du dann Deine Methode Rotate90Sync()


    Damit lässt sich der Rubik dann im Handumdrehen (:D) verdrehen (50 random Züge):

    C#-Quellcode

    1. Random rnd = new Random();
    2. for (int i = 0; i < 50; i++) rubikManager.Rotate90Sync((Cube3D.RubikPosition)Math.Pow(2, rnd.Next(0, 9)), Convert.ToBoolean(rnd.Next(0, 2)));
    SᴛᴀʀGᴀᴛᴇ01

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „StarGate01“ ()

    Nun werde ich mal mit dem Code für das Lösen des Cubes starten. Bin schon gespannt, ob ich es schaffe ;) Sobald ich einen Lösungsschritt fertig gecodet habe, werde ich den Code hier posten. Ich löse den Cube vorerst noch mit der Anfänger-Methode. Falls das dann soweit steht, versuche ich mich an der Fridrich-Methode. Die letzte Ebene werde ich vielleicht sowieso schon mit der Fridrich-Methode lösen, da es dabei nur darum geht, den aktuellen Fall zu erkennen. Für alle möglichen Fälle gibt es dann Algorithmen, die den Fall lösen. Ich denke, dass das einfacher zu programmieren ist, als die Anfänger-Methode.
    Würdet ihr eine andere Lösungsmethode verwenden?

    So möchte ich vorgehen:
    1. Eingabe eines verdrehten Würfels (Möglichkeit Webcam, Möglichkeit Farben zusammenklicken)
    2. Weitergabe an mein Solver-Objekt
    3. Der Solver erstellt einen neuen virtuellen Würfel, der zu Beginn gleich, wie der eingegebene Würfel ist. Der virtuelle Würfel wird im Solver gelöst.
    4. Alle Lösungsschritte werden in einem Stack mit LayerMoves gespeichert und zurückgegeben.
    5. Die Moves werden in der 3D-Darstellung nacheinander abgearbeitet und auch in einer Listbox angezeigt.
    Wäre eine andere Vorgehensweise sinnvoller?

    Danke für die tolle Hilfe :)
    Switcherlapp97
    RubiksCubeSolver


    Jetzt im Showroom