Only one who wanted to find the Stone -- find it, but not use it -- would be able to get it.
-- Albus Dumbledore
Simply skimming through the project, it is pretty obvious that all the core functions are in src/com/superliminal/magiccube4d
directory, where the following java files lie:
Audio.java MacroManager.java MC4DSwing.java ProgressManager.java Vec_h.java
Congratulations.java MagicCube.java MC4DView.java PuzzleDescription.java
History.java Math4d.java NdSolve.java PuzzleManager.java
MacroControls.java MC4DApplet.java PipelineUtils.java RotationHandler.java
Macro.java MC4DLauncher.java PolytopePuzzleDescription.java Sort.java
I will just skip setting up development environment because it is tedious. MC4D is a Java project so sure you can use Eclipse to compile a jar file. Choosing MC4DLauncher as the entry class works perfect for me.
MC4DLauncher
looks entry-ish and it really is. After preliminary setup, the control is passed to MC4DSwing
. Swing is a classic Java GUI framework. There are many more details to draw the MC4D window. The hypercubes are called puzzles in this project, so we can search this term in the MC4DSwing file. There may be graphic-related results mostly, but that's fine as well.
isSolved
methodWhen tracing the file with our vague goal, I find this function interesting:
private void updateEditMenuItems() {
saveitem.setEnabled(true);
cheatitem.setEnabled(hist.hasPreviousMove());
solveitem.setEnabled(!puzzleManager.isSolved());
}
There is a item (EditMenuItem
?) named solveitem
. To enable it or not, the condition is the truth value of puzzleManager.isSolved
. Intuitively, to tell if a puzzle is solved, the state of the puzzle must be checked. It is worth digging deeper.
The function is defined in PuzzleManager
class, part 1 being
public boolean isSolved()
{
int nFaces = puzzleDescription.nFaces();
int faceState[] = new int[nFaces];
VecMath.fillvec(faceState, -1);
From its naming, faceState
array looks like what we expect at first, but soon we realize that it is a local variable, and being intitialized with -1. The size of this array represents the number of the faces. Still worth noting that, the term "face" here stands for "cell" or polyhedra. A simple way to understand this is, each face of a 3D object has 2 dimensions, so naturally each "face" of a 4D object (hypercubes or other 4D puzzles of our interests later) is 3D.
Personally I prefer the term cell, which is suggested by wikipedia: 4-polytope as well, and we should stick to it so that we won't mess up things, especially when we have to frequently revisit the concept or analogy in 3D space. However, the reason they chose face as the default term is still understandable, because human brain can quickly establish the link between the term face and the (n-1)-D boundaries of a n-D object. It is merely an anology, yet the usage can be seen all over the place. The intrinsic power of natural language is amazingly strong
.
puzzleDescription
is a predefined object that we can perceive as the description of the puzzle object. It has a method nFaces()
. For a 2x2x2x2 settings, since it is a regular tesseract, I believe the nFace
method returns 8.
Part 2:
// Cycle through all the stickers.
for(int s = 0; s < puzzleState.length; s++)
{
int faceIndex = puzzleDescription.getSticker2Face()[s];
if(faceState[faceIndex] == -1)
{
faceState[faceIndex] = puzzleState[s];
continue;
}
The term sticker is also a 3D analogy, representing the color attribute of each face of a cubie. Now it refers to the same thing for a 4D hypercube's 3D faces. Predefined variable puzzleState
is used to retrieve the total number of stickers.
In part 1, we see the initialization of faceState
array, and now in part 2, puzzleState[s]
are assigned to each faceState, if it hasn't been assgined before. After part 3, we will get the whole picture of this algorithm,
// Check for multiple colors on a single face.
if(puzzleState[s] != faceState[faceIndex])
return false;
} // end for
return true;
} // end isSolved()
The first-line comment explains the trick. If the puzzle is not solved yet, which means at least some "face" of the puzzle is still a colorful mess. For such a face, after its value is assigned, eventually some s
will exist such that the color of the sticker is different from itself. Thus, return false immediately in this case. Otherwise, when all the stickers are checked and false is never returned, true can be returned, because the puzzle is solved.
Well, the approach is different from what we expected, but not hard to understand. The approach naturally implements the net representation of a puzzle like we saw at ruwix.
During the tracing, I find evidences that draw me back on implementing hyperskewb as one of the puzzles in MC4D. While I will mention what I have found in later episodes, simply put, the core of MC4D project is not designed for puzzles like hyperskewb. I know I havn't even explain what hyperskewb is, but the day will come, and soon.
Today is the end of PART I: Basics. I expect that readers now already equipped with mental tools to imagine 4D puzzles. Even though I don't think in the rest of this series we will trace any more Java code, we will keep playing with MC4D in PART II: 2x2x2x2.
沒有在 MC4D 找到想要參考的資料結構,但仍然可以作為一個第一部的收尾。