Wenn ich versuche, einen Zauberwürfel zu simulieren , wie würden Sie eine Datenstruktur erstellen, um den Zustand des Würfels im Speicher mit einer X-Anzahl von Kacheln pro Seite zu speichern?
Dinge, die man beachten muss:
- Der Würfel kann von beliebiger Größe sein
- Es ist ein Zauberwürfel, sodass Ebenen gedreht werden können
Antworten:
Was ist los mit einem einfachen alten Array von Größe
[6X][X]
? Sie müssen nichts über innere Mini-Würfel wissen , da Sie sie nicht sehen. Sie gehören nicht zum Status des Würfels. Verstecken Sie zwei hässliche Methoden hinter einer gut aussehenden und einfach zu bedienenden Oberfläche, testen Sie sie bis zum Tod, und fertig!quelle
As long as you know how the six surfaces are "threaded" together
Welches ist genau das, was eine robustere Datenstruktur Ihnen geben wird. Ich denke, wir streiten uns für dasselbe. Eine Array-Seite und eine Seite ist ein Array von Blöcken. Es gibt jedoch viele interessante Eigenschaften von Seiten und Blöcken, die helfen, dieses "Threading" herauszufinden. Mag diesen Begriff nicht wirklich, weil er mit Multithreading verwechselt werden kann. )Es sollte beachtet werden, dass ich ein begeisterter Geschwindigkeitsknacker bin, aber ich habe nie versucht, einen Zauberwürfel in einem Algorithmus oder einer Datenstruktur programmgesteuert darzustellen.
Ich würde wahrscheinlich separate Datenstrukturen erstellen, um die einzigartigen Aspekte jedes Blocks in einem Cube zu erfassen.
Es gibt drei verschiedene Arten von Blöcken auf einem Würfel:
Eckblock - Er hat drei Farbflächen und drei benachbarte Teile, mit denen er jederzeit eine Seite teilt.
Kantenblock - Er hat zwei Farbflächen und 4 benachbarte Teile, mit denen er jederzeit eine Seite teilt. In 3x3 Blöcken hat es immer 2 Mittelstücke und 2 Eckstücke.
Zentralblock - In einem 3x3-Würfel ist dieses Teil nicht beweglich, kann jedoch gedreht werden. Es wird immer 4 benachbarte Kantenblöcke haben. In größeren Würfeln gibt es mehrere Mittelblöcke, die sich mit einem anderen Mittelblock oder einem Randstück teilen können. Mittelblöcke grenzen niemals an einen Eckblock.
Wenn Sie dies wissen, kann ein Block eine Liste von Verweisen auf andere Blöcke haben, die er berührt. Ich würde eine andere Liste von Listen führen, die eine Liste von Blöcken ist, die eine einzelne Würfelfläche darstellen, und eine Liste, die Verweise auf jede Würfelfläche enthält.
Jedes Würfelgesicht würde als ein einzigartiges Gesicht dargestellt.
Mit diesen Datenstrukturen wäre es ziemlich einfach, einen Algorithmus zu schreiben, der eine Rotationstransformation für jede Fläche ausführt und die entsprechenden Blöcke in die entsprechenden Listen und aus diesen heraus verschiebt.
EDIT: Wichtiger Hinweis, diese Listen müssen natürlich bestellt werden, aber ich habe vergessen, das zu erwähnen. Wenn ich zum Beispiel die rechte Seite drehe, bewegt sich der rechte Block der linken Ecke in die rechte Ecke der rechten Seite und wird im Uhrzeigersinn gedreht.
quelle
list of lists
. Vielleicht ist es besser, nur eine ungeordnete Liste von Blöcken zu haben, die Sie abfragen können. und Sie aktualisieren nur die angrenzenden Blockreferenzen, wenn Sie eine Transformation durchführen. Wenn Sie eine Liste aller Blöcke in einer Fläche möchten, können Sie Ihre Liste nach allen benachbarten Blöcken für die mittleren Blöcke abfragen, oder?Wenn ich an dieses Problem denke, denke ich an einen statischen Würfel, über den sich die Farben in bekannten Mustern bewegen. Damit....
Ein Cube-Objekt enthält 6 Side-Objekte, die mit einem festen Index von 0-5 versehen bleiben. Jede Seite enthält 9 Positionsobjekte, die mit einem festen Index von 0-8 versehen bleiben. Jede Position enthält eine Farbe.
Der Einfachheit halber sollten Sie jede Aktion in Vierteldrehungsschritten ausführen. Es gibt 3 Rotationsachsen in jeweils 2 möglichen Richtungen für insgesamt 6 mögliche Aktionen auf dem Würfel. Mit diesen Informationen wird es zu einer relativ einfachen Aufgabe, die 6 möglichen Aktionen auf dem Würfel abzubilden.
Die Farbe Grün in Seite 6, Position 3, kann sich unter anderem in Abhängigkeit von der durchgeführten Aktion zu Seite 1, Position 3 oder Seite 2, Position 7 bewegen. Ich habe dies nicht genug untersucht, um mathematische Übersetzungen zu finden, aber es werden wahrscheinlich Muster auftauchen, die Sie im Code nutzen können.
Beginnen Sie dazu niemals mit einem zufälligen Würfelzustand. Beginnen Sie stattdessen mit einem gelösten Status und führen Sie n Aktionen programmgesteuert aus, um den Cube in einen zufälligen Startstatus zu versetzen. Da Sie nur rechtliche Schritte unternommen haben, um zum aktuellen Status zu gelangen, muss der Cube lösbar sein.
quelle
Ich fand, dass ein xyz-Koordinatensystem eine einfache Möglichkeit ist, einen Rubik-Würfel zu adressieren, und Rotationsmatrizen eine einfache, generische Möglichkeit, die Rotationen zu implementieren.
Ich habe eine Piece-Klasse erstellt, die einen Positionsvektor enthält
(x, y, z)
. Ein Stück kann gedreht werden, indem eine Rotationsmatrix auf seine Position angewendet wird (eine Matrix-Vektor-Multiplikation). Das Teil zeichnet auch die Farben in einem Tupel auf(cx, cy, cz)
, wobei die Farben entlang jeder Achse ausgerichtet sind. Eine kleine Menge Logik stellt sicher, dass diese Farben während einer Drehung entsprechend aktualisiert werden: Eine 90-Grad-Drehung in der XY-Ebene bedeutet, dass die Werte voncx
und vertauscht werdency
.Da die gesamte Rotationslogik in der Piece-Klasse gekapselt ist, kann der Cube eine ungeordnete Liste von Pieces speichern, und Rotationen können auf generische Weise durchgeführt werden. Um die linke Fläche zu drehen, wählen Sie alle Teile mit einer x-Koordinate von -1 aus und wenden Sie die entsprechende Drehmatrix auf jedes Teil an. Um den gesamten Würfel zu drehen, wenden Sie auf jedes Teil dieselbe Rotationsmatrix an.
Diese Implementierung ist einfach und hat ein paar Feinheiten:
(-1, 1, 1)
), eine Kante hat genau eine Null ((1, 0, -1)
) und ein Mittelstück hat zwei Nullen ((-1, 0, 0)
).Nachteile:
quelle
Sie können ein einfaches Array verwenden (jedes Element hat eine 1: 1-Zuordnung zu einem Quadrat auf einer Fläche) und jede Drehung mit einer bestimmten Permutation simulieren
Sie können mit nur 3 wesentlichen Permutationen davonkommen: Drehen Sie eine Scheibe mit der Achse durch die Vorderseite, drehen Sie den Würfel um die vertikale Achse und drehen Sie den Würfel über die horizontale Achse durch die linke und rechte Seite. Alle anderen Züge können durch eine Verkettung dieser drei ausgedrückt werden.
Die einfachste Methode, um festzustellen, ob ein Würfel lösbar ist, besteht darin, ihn zu lösen (finden Sie eine Reihe von Permutationen, die den Würfel lösen) 2 getauschte Ecken haben Sie einen unlösbaren Würfel
quelle
the most straightforward way of know whether a cube is solvable is to solve it
. Nun, wenn Sie das Modell verwenden, das Sie vorschlagen, denke ich, ist das wahr. Wenn Sie jedoch ein Modell verwenden, das näher an der Rotation von @ maple_shaft liegt, und die Rotation verfolgen, können Sie schnell testen, ob ein 3x3x3-Würfel lösbar ist, indem Sie überprüfen, ob die Summe der Kantenumkehrungen mod 2 0 und die Eckendrehung mod 3 0 beträgt. Überprüfen Sie dann die Parität der Permutation mit Zählt man Edge-Swaps und Corner-Swaps (um wieder gelöst zu werden), muss deren Summe Mod 2 0 sein (total parity even). Dies sind die notwendigen und ausreichenden Tests, um zu beweisen, dass der Würfel lösbar ist.Die erste Bedingung, dass es lösbar ist, wäre, dass jedes Stück vorhanden ist und dass die Farben auf jedem Stück verwendet werden können, um einen "souveränen" Würfel zusammenzusetzen. Dies ist eine relativ triviale Bedingung, deren Wahrheit mit einer einfachen Checkliste ermittelt werden kann. Das Farbschema auf einem "Standard" -Würfel ist definiert , aber auch wenn Sie sich nicht mit Standardwürfeln beschäftigen, gibt es nur 6! mögliche Kombinationen von gelösten Gesichtern.
Wenn Sie alle Teile und Farben richtig eingestellt haben, ist es eine Frage, ob eine bestimmte physikalische Konfiguration lösbar ist. Nicht alle von ihnen sind. Die einfachste Möglichkeit, dies zu überprüfen, besteht darin, einen Algorithmus zum Lösen von Würfeln auszuführen und zu prüfen, ob er mit einem gelösten Würfel endet. Ich weiß nicht, ob es ausgefallene kombinatorische Techniken gibt, um die Lösbarkeit zu bestimmen, ohne tatsächlich zu versuchen, den Würfel zu lösen.
Was die Datenstruktur angeht, spielt das fast keine Rolle. Der knifflige Teil besteht darin, die Transformationen richtig zu machen und den Würfelzustand so darzustellen, dass Sie mit den verfügbaren Algorithmen in der Literatur ordentlich arbeiten können. Wie Maple-Shaft angibt, gibt es drei Arten von Stücken. Literatur über das Lösen von Rubiks Würfeln bezieht sich immer auf Stücke nach ihrer Art. Transformationen werden auch auf übliche Weise dargestellt (siehe Singmaster-Notation ). Außerdem beziehen sich alle Lösungen, die ich gesehen habe, immer auf ein Stück als Bezugspunkt (normalerweise wird das weiße Mittelstück auf den Boden gelegt).
quelle
Da Sie bereits gute Antworten erhalten haben, möchte ich nur ein Detail hinzufügen.
Unabhängig von Ihrer konkreten Darstellung ist zu beachten, dass Linsen ein sehr gutes Werkzeug zum "Einzoomen" der verschiedenen Teile eines Würfels sind. Schauen Sie sich zum Beispiel die Funktion
cycleLeft
in diesem Haskell-Code an . Es ist eine generische Funktion, die zyklisch jede Liste der Länge 4 durchläuft. Der Code zum Ausführen der L-Bewegung sieht folgendermaßen aus:So
cycleLeft
arbeitet auf der Ansicht , gegeben durchleftCols
. In ähnlicher WeiserotateSideCW
funktioniert die generische Funktion, die eine Seite zu einer gedrehten Version davon darstellt, mit der Ansicht vonleftSide
. Die anderen Schritte können auf ähnliche Weise implementiert werden.Das Ziel dieser Haskell-Bibliothek ist es, hübsche Bilder zu erstellen. Ich denke es ist gelungen:
quelle
Sie scheinen zwei getrennte Fragen zu stellen.
Wenn Sie einen Rubik-Würfel aus der realen Welt simulieren möchten, haben alle Rubik-Würfel 6 Seiten. Ich denke, was Sie meinen, ist "X Anzahl der Fliesen pro Dimension pro Seite". Jede Seite des ursprünglichen Rubic-Würfels ist 3x3. Andere Größen sind 4x4 (Professor's Cube), 5x5 und 6x6.
Ich würde die Daten mit 6 Seiten darstellen, unter Verwendung der "Standard" -Würfel-Lösungsnotation:
Jede Seite ist ein 2-D-Array von X mal X.
quelle
Ich mag die Idee von @maple_shaft, verschiedene Teile (Mini-Würfel) unterschiedlich darzustellen: Mittel-, Rand- und Eckteile haben jeweils 1, 2 oder 3 Farben.
Ich würde die Beziehungen zwischen ihnen als (bidirektionales) Diagramm darstellen, wobei Kanten benachbarte Teile verbinden. Jedes Stück hätte eine Reihe von Schlitzen für Kanten (Verbindungen): 4 Schlitze in zentralen Stücken, 4 Schlitze in Kantenstücken, 3 Schlitze in Eckstücken. Alternativ können Mittelstücke 4 Verbindungen zu Randstücken und 4 für Eckstücke separat aufweisen, und / oder Randstücke können 2 Verbindungen zu Mittelstücken und 2 zu Eckstücken separat aufweisen.
Diese Arrays sind so angeordnet, dass die Iteration über die Graphenkanten immer "dieselbe" Rotation darstellt, die die Rotation des Würfels moduliert. Das heißt, wenn Sie z. B. für ein Mittelstück den Würfel so drehen, dass seine Fläche oben liegt, ist die Reihenfolge der Verbindungen immer im Uhrzeigersinn. Ähnlich für Kanten- und Eckstücke. Diese Eigenschaft gilt nach Gesichtsrotationen (oder so scheint es mir jetzt).
Die Erkennung eindeutig unlösbarer Zustände (vertauschte / umgedrehte Kanten, vertauschte Ecke) ist hoffentlich auch einfach, da es einfach ist, Teile eines bestimmten Typs und ihrer Ausrichtung zu finden.
quelle
Wie wäre es mit Knoten und Zeigern?
Angenommen, es gibt immer 6 Flächen und 1 Knoten repräsentiert 1 Quadrat auf 1 Fläche:
Ein Knoten hat einen Zeiger auf jeden Knoten daneben. Bei einer Kreisrotation wird der Zeiger (Anzahl der Knoten / Anzahl der Flächen) um -1 Knoten verschoben, in diesem Fall um 2. Da alle Rotationen Kreisrotationen sind, wird nur eine
rotate
Funktion erstellt. Es ist rekursiv, verschiebt jeden Knoten um ein Feld und prüft, ob er sie genug verschoben hat, da es die Anzahl der Knoten gesammelt hat und es immer vier Flächen gibt. Ist dies nicht der Fall, erhöhen Sie die Anzahl der Verschiebungen und rufen Sie "Rotation" erneut auf.Vergessen Sie nicht, dass es doppelt verknüpft ist. Aktualisieren Sie daher auch die neu zugewiesenen Knoten. Es wird immer eine Höhe * Breite-Anzahl von Knoten verschoben, wobei ein Zeiger pro Knoten aktualisiert wird. Daher sollte eine Höhe * Breite * 2-Anzahl von Zeigern aktualisiert werden.
Da alle Knoten aufeinander zeigen, gehen Sie einfach im Kreis herum und aktualisieren Sie jeden Knoten, wenn Sie zu ihm kommen.
Dies sollte für Würfel jeder Größe ohne Kantenfälle oder komplexe Logik funktionieren. Es ist nur ein Zeigerlauf / Update.
quelle
Aus persönlicher Erfahrung funktioniert es gut, ein Set zu verwenden, um jeden rotierenden Teil des Würfels im Auge zu behalten. Jeder Subwürfel besteht aus drei Sätzen, unabhängig von der Größe des Rubikwürfels. Um einen Unterwürfel zu finden, in dem sich der Zauberwürfel befindet, nehmen Sie einfach den Schnittpunkt der drei Mengen (das Ergebnis ist ein Unterwürfel). Um einen Zug auszuführen, entfernen Sie die betroffenen Sub-Cubs aus den Sätzen, die am Zug beteiligt sind, und legen Sie sie dann in die Sätze zurück, die sie als Ergebnis des Zuges aufnehmen.
Der 4 mal 4 Würfel wird 12 Sätze haben. 6 Sätze für die 6 Gesichter und 6 Sätze für die sechs Bänder, die um den Würfel herumlaufen. Die Gesichter haben jeweils 16 Unterwürfel und die Bänder haben jeweils 12 Unterwürfel. Es gibt insgesamt 56 Unterwürfel. Jeder Unterwürfel enthält Informationen über die Farbe und die Richtung der Farben. Der Rubik-Würfel selbst ist ein 4 × 4 × 4-Array, wobei jedes Element Informationen enthält, die aus den drei Mengen bestehen, die den Sub-Würfel an dieser Stelle definieren.
Im Gegensatz zu den anderen 11 Antworten haben Sie in dieser Datenstruktur den Schnittpunkt von Mengen verwendet, um die Position der einzelnen Unterblöcke im Cube zu definieren. Dies erspart die Arbeit, die Near-Sub-Blöcke aktualisieren zu müssen, wenn eine Änderung vorgenommen wird.
quelle