TweetFollow Us on Twitter

Jun 02 Challenge

Volume Number: 18 (2002)
Issue Number: 06
Column Tag: Programmer's Challenge

by Bob Boonstra, Westford, MA

Matchsticks

Some time ago, Robin Landsbert sent me a suggestion for a Challenge based on a game he called Nim. In Robin’s version of the game, matchsticks were arranged in rows forming a triangle, one matchstick in the top row, two in the next, three in the next, etc. Two players take turns removing one or more matchsticks from any single row of the board. The object is to make your opponent take the last matchstick.

A little research suggests that this version of the game might not be very difficult. So, in the tradition of the Challenge, we will add a few twists that might make the game (and the Challenge) more interesting. First, we will arrange our matchsticks in a square grid instead of a triangular one, and allow players to remove matchsticks from either a single row or a single column on a given turn. Second, we will not put a matchstick in every position in the grid, leaving a small number of positions empty, perhaps on the order of 10%. Third, we will restrict a player’s moves to removing matchsticks with no intervening holes. That is, a player can remove the n+1 matchsticks in row r located in columns c through c+n only if a matchstick is present in each of those locations. And finally, we will play two versions of the game, one where the player taking the last matchstick loses the game, and one where the player taking the last one wins the game.

The prototype for the code you should write is:

void InitMatchsticks(
   short dimension,      
      /* game is played on a square board of size dimension x dimension */
   const char *board,
      /* board[row*dimension + col] is board cell (row,col) */
      /* board[]==1 represents a matchstick, ==0 represents an empty cell */
   bool playFirst,
      /* true if you will play first in this game */
   bool lastMatchstickLoses,
      /* true if taking the last matchstick loses the game, 
         false if taking the last one wins the game */
   short opponent
      /* identifier for your opponent in this game */
);

void OpponentMove(
   bool playingRow,
      /* true if opponent played along a row, false if along a column */
   short rowOrColumnNumber,
      /* number of the (origin zero) row (playingRow==true) or 
         column (playingRow==false)
         that the opponent played */
   short startingColOrRow,
   short endingColOrRow,
      /* if playingRow==true, the opponent played from 
            (row,col)==(rowOrColumnNumber,startingColOrRow)
         to (row,col)==(rowOrColumnNumber,endingColOrRow)
         if playingRow==false, the opponent played from 
            (row,col)==(startingColOrRow,rowOrColumnNumber)
         to (row,col)==(endingColOrRow,rowOrColumnNumber)
      */
   const char *board
      /* board after your opponent's move */  
);


const char *YourMove(
   bool *playingRow,
      /* true if you played along a row, false if along a column */
   short *rowOrColumnNumber,
      /* number of the (origin zero) row (playingRow==true) or 
         column (playingRow==false)
         that you played */
   short *startingColOrRow,
   short *endingColOrRow
      /* if *playingRow==true, you played from 
            (row,col)==(*rowOrColumnNumber,*startingColOrRow)
         to (row,col)==(*rowOrColumnNumber,*endingColOrRow)
         if *playingRow==false, you played from 
            (row,col)==(*startingColOrRow,*rowOrColumnNumber)
         to (row,col)==(*endingColOrRow,*rowOrColumnNumber)
      */
   /* return value is a pointer to a board after your move */  
);

The objective of the Challenge is to win as many games as possible against your fellow contestants, while expending as little execution time as possible. Each game begins with a call to your InitMatchsticks routine, where you are given the dimension of the game, the initial board configuration, the identity of your opponent, whether or not you playFirst, and whether the objective is to take or not take the last matchstick (lastMatchstickLoses). When it is your turn to move, your YourMove routine describes the move you are making (playingRow, rowOrColumnNumber, startingColOrRow, endingColOrRow) and returns a pointer to your view of what the board looks like after your move. When your opponent moves, your OpponentMove routine is provided with a description of the opponent’s move, and the board configuration after that move.

The Challenge will be scored as a round robin tournament, or another fair scheduling mechanism. Each player will play first and play second against each scheduled opponent an equal number of times for each test case. Each player will play to win by taking the last matchstick, and play to win by making the opponent take the last matchstick, an equal number of times against each scheduled opponent for each test case. A player’s score will be dimension^2 points for each win, minus a penalty of 10 points per millisecond of execution time. You can earn a bonus of up to 25% of your score based on a subjective evaluation of the clarity of your code and commentary.

This will be a native PowerPC Carbon Challenge, using the Metrowerks CodeWarrior Pro 7.0 development environment. Please be certain that your code is carbonized, as I may evaluate this Challenge using Mac OS X. Unfortunately, this Challenge cannot accommodate alternative development environments, because pairs of solutions need to compete against one another in a single executable.

Winner of the March, 2002 Challenge

The March Challenge required contestants to solve the Megaminx, a twelve-sided puzzle in the shape of a dodecahedron. Each of the twelve faces of the Megaminx can be rotated clockwise or counter-clockwise, with five consecutive rotations of a face in the same direction bringing the face back to its original position. Each face is divided into eleven facelets, five corner facelets that each border three faces, five edge facelets that each border two faces, and one center facelet. The faces are colored with six colors, opposite faces sharing the same color. The input for the Challenge was a sequence of files that each described a scrambled Megaminx, and the required output was a sequence of rotations that solved the puzzle. Scoring was based on the execution time required to solve the scrambled puzzles. Contestants earned up to a 25% reduction in their time if they also displayed the puzzle solution.

Two contestants, Ernst Munter and Allen Stenger, submitted solutions for this Challenge. Both contestants acknowledge the information provided at two Megaminx web sites, one provided by Meffert’s Puzzles at http://www.meffertspuzzles.com/puzzles/megasol1.html, and another by W. D. Joyner. Ernst used the approach described in http://web.usna.navy.mil/~wdj/megam.htm, one that solved the problem quickly, but generated solutions with a large number of moves. Ernst first moved the corner pieces to the proper positions, then moved the edge pieces to the proper positions, then oriented the corners, then oriented the edges. Allen took the nine-step approach described at the Meffert site, augmented with a modification from http://web.usna.navy.mil/~wdj/megaminx.htm, an approach that generated shorter move sequences, but took more execution time.

Both contestants provided display options in their entries. Ernst’s program has a compile-time option to generate a two-dimensional depiction of the Megaminx as the solution is generated. He included an option to display macro moves in a single step, which made it easier to see what was going on. Allen’s entry has a separate program, written in Cocoa and using OpenGL to display a three-dimensional Megaminx. Allen included options to read in a puzzle description file and a sequence of moves, controls to rotate the viewpoint, and controls to rotate a slice of the puzzle.

By the stated rules of this contest, the solution requiring the least amount of execution time, after considering the display bonus, is the winner. Congratulations to Ernst Munter (Kanata, ON, Canada) for winning the Megaminx Challenge. I am taking the somewhat unusual step, however, of providing both solutions in the online archive, and printing the better-commented solution by Allen Stenger in this article.

The table below lists, for each of the solutions submitted, the total execution time in microseconds, the time reduction awarded for providing a display option, the net penalty points after subtracting the bonus from the execution time, and the cumulative number of moves required to solve the ten test cases used to evaluate solutions. It also lists the programming language of each entry. As usual, the number in parentheses after the entrant's name is the total number of Challenge points earned in all Challenges prior to this one.

Name Exec. Time Display Penalty Moves Language (microsecs) Bonus Points
Ernst Munter (275) 37331 25% 27998 15030 C++
Allen Stenger (39) 347335 25% 260501 6440 C++/ObjC

Top Contestants . . .

Listed here are the Top Contestants for the Programmer’s Challenge, including everyone who has accumulated 20 or more points during the past two years. The numbers below include points awarded over the 24 most recent contests, including points earned by this month's entrants.

Rank Name Points Wins Total (24 mo) (24 mo) Points
1. Munter, Ernst 275 10 862
2. Saxton, Tom 52 1 210
3. Stenger, Allen 49 1 114
4. Rieken, Willeke 46 2 134
5. Wihlborg, Claes 40 2 49
6. Taylor, Jonathan 37 1 63
9. Gregg, Xan 20 1 140
10. Mallett, Jeff 20 1 114
11. Cooper, Tony 20 1 20

. . . and the Top Contestants Looking for a Recent Win

In order to give some recognition to other participants in the Challenge, we also list the high scores for contestants who have accumulated points without taking first place in a Challenge during the past two years. Listed here are all of those contestants who have accumulated 6 or more points during the past two years.

Rank Name Points(24 mo) Total Points
7. Sadetsky, Gregory 22 24
8. Boring, Randy 21 144
13. Schotsman, Jan 16 16
14. Shearer, Rob 15 62
15. Hart, Alan 14 39
16. Nepsund, Ronald 10 57
17. Day, Mark 10 30
18. Desch, Noah 10 10
19. Flowers, Sue 10 10
20. Maurer, Sebastian 7 108
21. Leshner, Will 7 7
22. Miller, Mike 7 7

There are three ways to earn points: (1) scoring in the top 5 of any Challenge, (2) being the first person to find a bug in a published winning solution or, (3) being the first person to suggest a Challenge that I use. The points you can win are:

1st place 20 points
2nd place 10 points
3rd place 7 points
4th place 4 points
5th place 2 points
finding bug 2 points
suggesting Challenge 2 points

Here is Allen’s Megaminx solution:

CSolver.cpp

//////////////////////////////////////////////////////////////////////
//
// Megaminx (MacTech Programmer's Challenge, March 2002)
// Written by Allen Stenger, March 2002
//
// Conceptually we rotate colors rather than faces; this simplifies the problem of 
// determining the orientation of each edge and  corner piece.
//
// We follow the solution given by Meffert's Puzzles and Novelties;
// see http://www.mefferts-puzzles.com/puzzles/megasol1.html
//
// Terminology: corner piece is at a vertex of the Megaminx and has three facelets, 
// edge piece is at an edge between two faces and has two facelets. The smaller 
// pentagons in the center of each face never move away from the face and so we 
// ignore them.
//
// A vertex can be specified by its vertex number, however edges don't have numbers 
// and are usually specified by the two faces they lie on. There is a variety of constant 
// tables for walking through the faces.
// 
// COLOR AMBIGUITY
//
// Because the same colors are used for two faces, it appears that there might be some 
// ambiguity in the pieces; that is, radially-opposite corners have the same colors, and 
// and radially-opposite edges have the same colors,so how do we know whether to 
// place a corner or edge in the Northern or Southern part of the Megaminx?
//
// * The corners are actually not ambiguous because the orientations
//   are different; so for example there are two corners with
//   colors 1,2,3, but the one on the North Pole has the colors
//   1,2,3 in clockwise order and the one on the South Pole has
//   1,2,3 in counter-clockwise order. Therefore we can always
//   tell from the corner which part of the Megaminx it goes in.
// * The edges really are ambiguous. It is not necessary to put
//   each edge back in its original place, but in some situations
//   we would get to Step 8 and be unable to orient the South
//   Pole edges because of an earlier placement we made. To solve 
//   the Megaminx we must follow some parity rules; see
//
//       Coreyanne Rickwalt, "The Fundamental Theorem of the
//        Megaminx", http://web.usna.navy.mil/~wdj/megaminx.htm.
//
//   We will detect the problem case in Step 6 and take evasive action.
//
// A simple example of the problem is a Megaminx that is solved except
// for the two edges:
//     8,7,3
//     9,7,2
// This one cannot be solved by the published Meffert method because
// the South Pole edges are not correctly placed in Step 8.
//
//////////////////////////////////////////////////////////////////////

#include "CSolver.h"
#include "CMegaminx.h"
#include "CMegaminxApp.h"

#include 
#include 


// some fixed faces we use
const int kSouthPoleFace = 7;

//////////////////////////////////////////////////////////////////////

CSolver::CSolver(CMegaminx& rMega) :
fMega(rMega)
{
}

CSolver::~CSolver()
{
}

   
void CSolver::Solve()
{
   // call all the solution steps
   Step3();
   Step4();
   Step5();
   Step6();
   Step7();
   Step8();
   Step9();
   Step10();
   Step11();
}
   
void CSolver::DoLUU(CMegaminx::face_t leftFace, CMegaminx::face_t rightFace)
{
   fMega.WriteComment("DoLUU");
   fMega.Slice(leftFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCounterCW, 1);
}

void CSolver::DoRUU(CMegaminx::face_t leftFace, CMegaminx::face_t rightFace)
{
   fMega.WriteComment("DoRUU");
   fMega.Slice(rightFace, CMegaminx::eCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCW, 1);
}

void CSolver::DoRLL(CMegaminx::face_t leftFace, CMegaminx::face_t rightFace)
{
   fMega.WriteComment("DoRLL");
   fMega.Slice(rightFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCounterCW, 1);
}

void CSolver::DoLLL(CMegaminx::face_t leftFace, CMegaminx::face_t rightFace)
{
   fMega.WriteComment("DoLLL");
   fMega.Slice(leftFace, CMegaminx::eCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(leftFace, CMegaminx::eCounterCW, 1);
   fMega.Slice(rightFace, CMegaminx::eCW, 1);
}

void CSolver::VisitAllCorners(CCornerVisitor &aVisitor)
{
   for (int i = 0; i < CMegaminx::kNumVertices; i++)
      aVisitor.VisitCorner(i);
}

bool CSolver::CheckEdgeParity()
{
   // this holds the permutation of the South edges. It is
   // in two 5-edge pieces:
   // 0-4: South Equator edges, indexed same as SouthEqEdge arrays
   // 5-9: South Pole edges, indexed same as SoutPoleEdge arrays + 5
   // The entries are also these indices; perm[i] contains the edge
   // index that edge i will go to when the Megaminx becomes solved.
   // Therefore the entries in perm are the numbers 0-9 in some 
   // permuted order.
   int perm[10];
   
   for (int i = 0; i < 10; i++)
      perm[i] = 0;
      
   // South Equator edges
   for (int i = 0; i < CMegaminx::kNumSouthEqEdges; i++)
   {
      CMegaminx::color_t c0 = 
         fMega.EdgeFaceletColor(fMega.kSouthEqEdgeL[i], 
                        fMega.kSouthEqEdgeR[i]);
      CMegaminx::color_t c1 = 
         fMega.EdgeFaceletColor(fMega.kSouthEqEdgeR[i], 
                        fMega.kSouthEqEdgeL[i]);
      perm[i] = ParityLookup(c0, c1);
   }
   
   // South Pole edges
   for (int i = 0; i < CMegaminx::kNumSouthPoleEdges; i++)
   {
      CMegaminx::color_t c0 = 
         fMega.EdgeFaceletColor(fMega.kSouthPoleEdgeN[i], 
                        fMega.kSouthPoleEdgeS[i]);
      CMegaminx::color_t c1 = 
         fMega.EdgeFaceletColor(fMega.kSouthPoleEdgeS[i], 
                        fMega.kSouthPoleEdgeN[i]);
      perm[i + 5] = ParityLookup(c0, c1);
   }
   
   // Now figure out the parity of perm
   bool bVisitedPerm[10];   
         // indexed same as perm; whether we
         // have counted that transition
   int cycleLengths = 0;   // sum of (cycle length - 1)
         
   for (int i = 0; i < 10; i++)
      bVisitedPerm[i] = false;
   for (int i = 0; i < 10; i++)
   {
      if (bVisitedPerm[i])
         continue;
         
      // follow the cycle starting at perm[i]
      int next = i;
      while (!bVisitedPerm[next])
      {
         cycleLengths++;
         bVisitedPerm[next] = true;
         next = perm[next];
      }
      cycleLengths--;
   }
   
   bool bEvenParity = ((cycleLengths & 1) == 0);
   return bEvenParity;
}

// look up the correct Southern edge for these colors; returns
// index into SouthEq table, or index + 5 into SouthPole table
int CSolver::ParityLookup(CMegaminx::color_t c0, CMegaminx::color_t c1)
{
   for (int i = 0; i < CMegaminx::kNumSouthEqEdges; i++)
   {
      CMegaminx::color_t trialColor0 = 
         CMegaminx::CorrectColor(CMegaminx::kSouthEqEdgeL[i]);
      CMegaminx::color_t trialColor1 = 
         CMegaminx::CorrectColor(CMegaminx::kSouthEqEdgeR[i]);
      if ((c0 == trialColor0 && c1 == trialColor1) ||
         (c1 == trialColor0 && c0 == trialColor1))
         return i;
   }
   
   for (int i = 0; i < CMegaminx::kNumSouthPoleEdges; i++)
   {
      CMegaminx::color_t trialColor0 = 
         CMegaminx::CorrectColor(CMegaminx::kSouthPoleEdgeN[i]);
      CMegaminx::color_t trialColor1 = 
         CMegaminx::CorrectColor(CMegaminx::kSouthPoleEdgeS[i]);
      if ((c0 == trialColor0 && c1 == trialColor1) ||
         (c1 == trialColor0 && c0 == trialColor1))
         return i + 5;
   }
   
   assert(false);   // trouble, no match
   return 0;
}

#pragma mark === Solution Steps ===

//////////////////////////////////////////////////////////////////////
// Solution Steps
//////////////////////////////////////////////////////////////////////

void CSolver::Step3()
{
   Step3Edges();
   Step3Corners();
   
   Step3Verify();
}

void CSolver::Step3Edges()
{
   for (int i = 0; i < CMegaminx::kNumNorthPoleEdges; i++)
   {
      CMegaminx::face_t destFaceN = CMegaminx::kNorthPoleEdgeN[i];
      CMegaminx::face_t destFaceS = CMegaminx::kNorthPoleEdgeS[i];
      if (fMega.IsEdgeCorrect(destFaceN, destFaceS))
         continue;   // already done!
         
      // if not the correct colors, find an edge that does have
      // the correct colors and drop it to the South Pole.
      // the return value is the South Equatorial face where
      // it got dropped.
      CMegaminx::color_t c0 = fMega.CorrectColor(destFaceN);
      CMegaminx::color_t c1 = fMega.CorrectColor(destFaceS);
      CMegaminx::face_t southPoleFace = Step3_4Drop(c0, c1);
      
      // now loft it back to the North Pole; first rotate
      // the South Pole so the edge touches the "down right" face.
      CMegaminx::face_t rotToFace = 
            CMegaminx::kFaceDownRight[destFaceS];
      int dist = Distance(southPoleFace, rotToFace);
      
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, dist);
      fMega.Slice(rotToFace, CMegaminx::eCW, 2);
      fMega.Slice(destFaceS, CMegaminx::eCounterCW, 2);
      
      // if the edge is not correctly oriented we need
      // to reorient it
      if (fMega.EdgeFaceletColor(destFaceN, destFaceS) != 1)
      {
         fMega.Slice(destFaceS, CMegaminx::eCounterCW, 2);
         int nextSouthFace = fMega.NextSouthEqFace(rotToFace);
         fMega.Slice(nextSouthFace, CMegaminx::eCW, 1);
         fMega.Slice(rotToFace, CMegaminx::eCW, 1),
         fMega.Slice(destFaceS, CMegaminx::eCounterCW, 2);         
      }
   }
}

// returns face we dropped it to
CMegaminx::face_t CSolver::Step3_4Drop(CMegaminx::color_t c0, 
                     CMegaminx::color_t c1)
{
   fMega.WriteComment("Step3_4Drop");
   // search the lower edges for one having these colors
   // (in either order), and if found move it to
   // the South Pole.
   
   bool bFound = false;
   CMegaminx::face_t southPoleFace = 0;   
            // edge dropped to this face-SouthPole
   
   // search the South Pole edges, and if found we are done!
   if (!bFound)
   {
      for (int i = 0; i < CMegaminx::kNumSouthPoleEdges && !bFound;
                            i++)
      {
         CMegaminx::face_t trialFaceN = 
                  CMegaminx::kSouthPoleEdgeN[i];
         CMegaminx::face_t trialFaceS = 
                  CMegaminx::kSouthPoleEdgeS[i];
         if (fMega.EdgeHasColors(trialFaceN, trialFaceS, c0, c1))
         {
            fMega.WriteComment("Step3_4Drop found on South Pole");
            bFound = true;
            southPoleFace = trialFaceN;
         }
      }
   }
   
   // search the South Equator edges, and if found drop
   // the edge to the South Pole by rotating its left face
   // CW 1
   if (!bFound)
   {
      for (int i = 0; i < CMegaminx::kNumSouthEqEdges && !bFound; 
                        i++)
      {
         CMegaminx::face_t trialFaceL = CMegaminx::kSouthEqEdgeL[i];
         CMegaminx::face_t trialFaceR = CMegaminx::kSouthEqEdgeR[i];
         if (fMega.EdgeHasColors(trialFaceL, trialFaceR, c0, c1))
         {
            fMega.WriteComment("Step3_4Drop found on South Equator");
            bFound = true;
            fMega.Slice(trialFaceL, CMegaminx::eCW, 1);
            southPoleFace = trialFaceL;
         }
      }
   }
   
   // search the Middle Equator edges, and if found drop
   // the edge to the South Pole by rotating its S face
   // either CW 2 or CCW 2
   if (!bFound)
   {
      for (int i = 0; i < CMegaminx::kNumMiddleEqEdges && !bFound; 
                        i++)
      {
         CMegaminx::face_t trialFaceN = CMegaminx::kMiddleEqEdgeN[i];
         CMegaminx::face_t trialFaceS = CMegaminx::kMiddleEqEdgeS[i];
         if (fMega.EdgeHasColors(trialFaceN, trialFaceS, c0, c1))
         {
            fMega.WriteComment("Step3_4Drop found on Middle Equator");
            bFound = true;
            southPoleFace = trialFaceS;
            // even indices are below and right of N face,
            // therefore above and left of S face
            if ((i & 1) == 0)
            {
               // above and left, so use CCW 2
               fMega.Slice(trialFaceS, CMegaminx::eCounterCW, 2);
            }
            else
            {
               // above and right, so use CW 2
               fMega.Slice(trialFaceS, CMegaminx::eCW, 2);
            }
         }
      }
   }
   
   // search the North Equator edges, and if found there drop to the
   // South Pole. The Meffert solution uses a simple transformation
   // in case 3 and a complicated one in case 4 (where it has to avoid
   // disturbing other North Equator edges), but we will use the
   // complicated one in both cases because the implementation
   // is easier.
   if (!bFound)
   {
      for (int i = 0; i < CMegaminx::kNumNorthEqEdges; i++)
      {
         CMegaminx::face_t trialFaceL = CMegaminx::kNorthEqEdgeL[i];
         CMegaminx::face_t trialFaceR = CMegaminx::kNorthEqEdgeR[i];
         if (fMega.EdgeHasColors(trialFaceL, trialFaceR, c0, c1))
         {
            fMega.WriteComment("Step3_4Drop found on North Equator");
            bFound = true;

            // drop to down left
            southPoleFace = CMegaminx::kFaceDownLeft[trialFaceL];
            
            fMega.Slice(southPoleFace, CMegaminx::eCounterCW, 1);
            DoLUU(trialFaceL, trialFaceR);
            DoLUU(trialFaceL, trialFaceR);
            fMega.Slice(southPoleFace, CMegaminx::eCW, 1);
            DoRUU(trialFaceL, trialFaceR);
            DoRUU(trialFaceL, trialFaceR);
            
            // at this point the edge is at the upper left of 
            // southPoleFace, so rotate it to put it on the
            // South Pole
            fMega.Slice(southPoleFace, CMegaminx::eCounterCW, 2);
         }      
      }
   }

   // search the North Pole edges, and if found drop to the 
   // South Pole. (This code should only be execute for Step 3,
   // because in Step 4 the North Pole edges have already been
   // set and we should have found the desired edge before now.)
   if (!bFound)
   {
      for (int i = 0; i < CMegaminx::kNumNorthPoleEdges; i++)
      {
         CMegaminx::face_t trialFaceN = 
               CMegaminx::kNorthPoleEdgeN[i];
         CMegaminx::face_t trialFaceS = 
               CMegaminx::kNorthPoleEdgeS[i];
         if (fMega.EdgeHasColors(trialFaceN, trialFaceS, c0, c1))
         {
            fMega.WriteComment("Step3_4Drop found on North Pole");
            bFound = true;
            
            southPoleFace = CMegaminx::kFaceDownRight[trialFaceS];
            fMega.Slice(trialFaceS, CMegaminx::eCW, 2);
            fMega.Slice(southPoleFace, CMegaminx::eCounterCW, 2);
         }
      }
   }

   assert(bFound);

   return southPoleFace;
}


void CSolver::Step3Corners()
{
   for (CMegaminx::vertex_t destCorner = 0; destCorner < 5; 
                        destCorner++)
   {
      // maybe corner is already done!
      if (fMega.IsCornerCorrect(destCorner))
         continue;
         
      fMega.WriteComment("Step3Corners");
      // find the corner that should be here, and drop
      // it to the South Pole and move it into place.
      CMegaminx::color_t destc0 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destCorner][0]);
      CMegaminx::color_t destc1 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destCorner][1]);
      CMegaminx::color_t destc2 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destCorner][2]);
      CMegaminx::vertex_t srcCorner =    
         fMega.CornerHavingColors(destc0, destc1, destc2);
         
      // special transformation if the src is at the
      // North Pole
      if (fMega.IsNorthPoleVertex(srcCorner))
      {
         fMega.WriteComment("Step3Corners drop North Pole corner");
         CMegaminx::face_t faceL = 
                        CMegaminx::kCornerFaces[srcCorner][1];
         CMegaminx::face_t faceR = 
                        CMegaminx::kCornerFaces[srcCorner][2];
         DoRUU(faceL, faceR);
         srcCorner += 5;   // corner has dropped to North Equator
      }
      
      // drop the corner to the South Pole (if it is not
      // already there)
      if (fMega.IsNorthEquatorVertex(srcCorner))
      {
         fMega.Slice(CMegaminx::kFaceBelow[srcCorner], 
                     CMegaminx::eCW, 2);
         srcCorner += 10;   // corner has dropped to South Pole
      }
      else if (fMega.IsSouthEquatorVertex(srcCorner))
      {
         fMega.Slice(CMegaminx::kFaceBelow[srcCorner], 
                     CMegaminx::eCW, 1);
         srcCorner += 5;
      }
      
      // rotate the vertex into place
      int moveToCorner = destCorner + 15;
      int dist = Distance(srcCorner, moveToCorner);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCW, dist);
      
      // lift the vertex into place on the North Equator
      int bottomFace = CMegaminx::kFaceBelow[destCorner + 5];
      fMega.Slice(bottomFace, CMegaminx::eCounterCW, 2);
      
      // figure out the orientation and apply the correct
      // transformation to lift it to the North Pole
      CMegaminx::face_t leftFace = 
                     CMegaminx::kFaceBelow[destCorner];
      CMegaminx::face_t rightFace = 
                     CMegaminx::PrevNorthEqFace(leftFace);
      if (fMega.CornerFaceletColor(leftFace, rightFace, bottomFace) 
                        == 1)
      {
         // top color at left
         // NOTE: Meffert solution wrongly states to use
         // LUU in this case.
         DoRUU(leftFace, rightFace);
      }
      else if (fMega.CornerFaceletColor(rightFace, leftFace, 
                     bottomFace) == 1)
      {
         // top color at right
         DoLUU(leftFace, rightFace);      
      }
      else
      {
         // top color at bottom
         DoLUU(leftFace, rightFace);
         DoLUU(leftFace, rightFace);
         DoLUU(leftFace, rightFace);
      }
   }
}

//////////////////////////////////////////////////////////////////////
// Step 4. Setting the northern equatorial edges
//////////////////////////////////////////////////////////////////////
//
// Very similar to Step 3 edge case; the common 3_4 routine does
// most of the work.

void CSolver::Step4()
{
   for (int i = 0; i < CMegaminx::kNumNorthEqEdges; i++)
   {
      CMegaminx::face_t destFaceL = CMegaminx::kNorthEqEdgeL[i];
      CMegaminx::face_t destFaceR = CMegaminx::kNorthEqEdgeR[i];
      if (fMega.IsEdgeCorrect(destFaceL, destFaceR))
         continue;   // already done!
         
      // if not the correct colors, find an edge that does have
      // the correct colors and drop it to the South Pole.
      // the return value is the South Equatorial face where
      // it got dropped.
      CMegaminx::color_t c0 = fMega.CorrectColor(destFaceL);
      CMegaminx::color_t c1 = fMega.CorrectColor(destFaceR);
      CMegaminx::face_t southPoleFace = Step3_4Drop(c0, c1);
      
      // now loft it back to the North Equator
      // figure the face we want to be under, and
      // rotate the South Pole to get there. The
      // desired face lies directly underneath the 
      // desired edge, and therefore below and left of
      // the destfaceR.
      CMegaminx::face_t rotToFace = 
                           CMegaminx::kFaceDownLeft[destFaceR];
      int dist = Distance(southPoleFace, rotToFace);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, dist);
      
      // rotate rotToFace either CW 2 or CCW 2 to bring
      // the edge adjacent faceL or faceR; we pick the
      // rotation so that the facelet on the face
      // has the face color. This facelet is currently
      // on the South Pole face.
      // Finally we'll move it into the correct edge.
      //
      // We combine the CW 2 and CCW 1 to get CW 1, and
      // similarly.
      int faceletColor = fMega.EdgeFaceletColor(kSouthPoleFace, 
                  rotToFace);
      if (faceletColor == destFaceL)
      {
         fMega.Slice(rotToFace, CMegaminx::eCW, 1);   // = CW 2 and CCW 1
         DoLUU(destFaceL, destFaceR);
         DoLUU(destFaceL, destFaceR);
         fMega.Slice(rotToFace, CMegaminx::eCW, 1);
         DoRUU(destFaceL, destFaceR);
         DoRUU(destFaceL, destFaceR);
      }
      else
      {
         fMega.Slice(rotToFace, CMegaminx::eCounterCW, 1);
               // = CCW 2 and CW 1
         DoRUU(destFaceL, destFaceR);
         DoRUU(destFaceL, destFaceR);
         fMega.Slice(rotToFace, CMegaminx::eCounterCW, 1);
         DoLUU(destFaceL, destFaceR);
         DoLUU(destFaceL, destFaceR);
      }
   }
   
   Step4Verify();
}



//////////////////////////////////////////////////////////////////////
// Step 5. Setting the northern equatorial corners
//////////////////////////////////////////////////////////////////////
//
// We step through the vertices, finding the correctly-oriented
// corner that belongs there. To transfer the corner, drop it to
// the South Pole, rotate, then rotate up to the North Equator.
void CSolver::Step5()
{
   for (int destVertex = CMegaminx::kFirstNorthEqVertex; 
         destVertex <= CMegaminx::kLastNorthEqVertex; destVertex++)
   {
      if (fMega.IsCornerCorrect(destVertex))
         continue;   // already OK, skip this one
      
      // Find the corner whose colors should be moved here. This 
      // may be the same corner, if it is not oriented correctly.
      int c0 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destVertex][0]);
      int c1 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destVertex][1]);
      int c2 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[destVertex][2]);
      int srcVertex = fMega.CornerHavingColors(c0, c1, c2);
      if (srcVertex != destVertex)
         Step5PlaceVertex(srcVertex, destVertex);
      Step5OrientVertex(destVertex);
   }
   Step5Verify();
}

// place and position the srcVertex into the destVertex
void CSolver::Step5PlaceVertex(int srcVertex, int destVertex)
{
   // drop the src to the South Pole if needed
   int southPoleFromVertex = srcVertex;
   if (fMega.IsNorthEquatorVertex(srcVertex))
   {
      fMega.WriteComment("Step5PlaceVertex from North Equator");
      southPoleFromVertex = srcVertex + 10;
      fMega.Slice(CMegaminx::kFaceBelow[srcVertex], 
               CMegaminx::eCW, 2);
   }
   else if (fMega.IsSouthEquatorVertex(srcVertex))
   {
      // moving this vertex also disturbs the North Equator
      // vertex on this face, which might already be correctly
      // placed, so we must rotate the face back after all
      // movements are done. We will handle this by
      // rotating the South Pole face CounterCW by 1 and
      // then reversing the face rotation.
      fMega.WriteComment("Step5PlaceVertex from South Equator");
      southPoleFromVertex = srcVertex + 5;
      int rot2Face = CMegaminx::kFaceBelow[srcVertex];
      fMega.Slice(rot2Face, CMegaminx::eCW, 1);
      fMega.Slice(kSouthPoleFace,CMegaminx::eCounterCW, 1);
      fMega.Slice(rot2Face, CMegaminx::eCounterCW, 1);
   }
   
   // figure where we need to rotate South Pole to, and the
   // face to rotate CounterCW to loft to final position
   fMega.WriteComment("Step5PlaceVertex move vertex into place");
   int southPoleToVertex = destVertex + 10;
   
   // rotate the South Pole CW into position
   int dist = Distance(southPoleFromVertex, southPoleToVertex);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCW, dist);
   
   // raise the src into the dest
   int homeFace = CMegaminx::kFaceBelow[destVertex];
   fMega.Slice(homeFace, CMegaminx::eCounterCW, 2);
}

void CSolver::Step5OrientVertex(int destVertex)
{
   fMega.WriteComment("Step5OrientVertex");
   // orient the corner, if needed
   // figure the colors of the corner facelets and see if
   // we need to rotate the corner
   CMegaminx::face_t belowFace = 
            CMegaminx::kFaceBelow[destVertex];

   CMegaminx::color_t belowColor = fMega.CorrectColor(belowFace);
      // color of bottom face
   CMegaminx::face_t rightFace = 
            CMegaminx::kFaceAbove[destVertex];
   CMegaminx::face_t leftFace = 
            CMegaminx::NextNorthEqFace(rightFace);
   if (fMega.CornerFaceletColor(leftFace, rightFace, belowFace) ==
                   belowColor)
   {
      fMega.Slice(belowFace, CMegaminx::eCW, 2);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
      fMega.Slice(belowFace, CMegaminx::eCW, 2);
   }
   else if (fMega.CornerFaceletColor(rightFace, belowFace, 
                     leftFace) == belowColor)
   {
      fMega.Slice(belowFace, CMegaminx::eCounterCW, 2);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
      fMega.Slice(belowFace, CMegaminx::eCounterCW, 2);   
   }
}

//////////////////////////////////////////////////////////////////////
// Step 6. Setting the middle equatorial edges

//////////////////////////////////////////////////////////////////////
//
// We step through the middle equatorial edges, checking to see if
// each already has the correctly positioned and placed edge, and if
// not then searching the South Pole edges, the South Equatorial
// edges, and finally the middle equatorial edges (after this one)
// for the needed edge. Note that each combination of colors has
// two edges with this combination, and (I think) they are
// interchangeable; this is unlike the situation for corners, where
// there are also two corners with a given combination, but they
// have opposite orientations and are not interchangeable.
void CSolver::Step6()
{
   for (int destFaceIndex = 0; 
         destFaceIndex < CMegaminx::kNumMiddleEqEdges;
         destFaceIndex++)
   {
      CMegaminx::face_t faceS = 
                  CMegaminx::kMiddleEqEdgeS[destFaceIndex];
      CMegaminx::face_t faceN = 
                  CMegaminx::kMiddleEqEdgeN[destFaceIndex];
      if (fMega.IsEdgeCorrect(faceS, faceN))
         continue;   // already correctly placed and positioned
      CMegaminx::color_t neededColorS = fMega.CorrectColor(faceS);
      CMegaminx::color_t neededColorN = fMega.CorrectColor(faceN);
      bool bFound = false;

      // search the polar edges
      for (int i = 0; 
            i < CMegaminx::kNumSouthPoleEdges && !bFound; i++)
      {
         CMegaminx::face_t searchPoleFace = 
                  CMegaminx::kSouthPoleEdgeN[i];
         if (fMega.EdgeHasColors(kSouthPoleFace, searchPoleFace, 
                        neededColorS, neededColorN))
         {
            bFound = true;
            fMega.WriteComment("Step6 move from South Pole");
            Step6PlacePoleEdge(searchPoleFace, destFaceIndex);
         }
      }
      
      // search the Southern Equatorial edges
      for (int i = 0;
            i < CMegaminx::kNumSouthEqEdges && !bFound; i++)
      {
         CMegaminx::face_t searchEqFaceL = 
                  CMegaminx::kSouthEqEdgeL[i];
         CMegaminx::face_t searchEqFaceR = 
                  CMegaminx::kSouthEqEdgeR[i];
         if (fMega.EdgeHasColors(searchEqFaceL, searchEqFaceR, 
               neededColorS, neededColorN))
         {
            bFound = true;
            fMega.WriteComment("Step6 move from South Equatorial");
            DoRLL(searchEqFaceR, searchEqFaceL);
            Step6PlacePoleEdge(searchEqFaceR, destFaceIndex);
         }
      }
      
      // search the (this or later) middle equatorial edges
      // we don't search earlier ones because they are already
      // correctly placed and we don't want to steal from them;
      // we do need to search the edge itself because it might
      // have the correct colors but wrongly placed.
      for (int searchIndex = destFaceIndex; 
            searchIndex < CMegaminx::kNumMiddleEqEdges && !bFound; 
                  searchIndex++)
      {
         CMegaminx::face_t mFaceS = 
                     CMegaminx::kMiddleEqEdgeS[searchIndex];
         CMegaminx::face_t mFaceN = 
                     CMegaminx::kMiddleEqEdgeN[searchIndex];
         if (fMega.EdgeHasColors(mFaceS, mFaceN, neededColorS, 
                     neededColorN))
         {
            bFound = true;
            fMega.WriteComment("Step6 move from Middle Equatorial");
            // lift the found edge, either right or left.
            // Lifting uses the same transformations as
            // dropping, however the lifted edge goes to the
            // adjoining face.
            if ((searchIndex & 1) == 0)
            {
               // even index, so edge is below and to right,
               // and will be lifted to next face
               CMegaminx::face_t nextMFace = 
                  fMega.NextSouthEqFace(mFaceS);
               DoLUU(mFaceS, nextMFace);
               DoLLL(mFaceS, nextMFace);
               DoRUU(mFaceS, nextMFace);
               Step6PlacePoleEdge(nextMFace, destFaceIndex);
            }
            else
            {
               // odd index, so edge is below and to left,
               // and will be lifted to previous face
               CMegaminx::face_t prevFace = 
                  fMega.PrevSouthEqFace(mFaceS);
               DoRUU(prevFace, mFaceS);
               DoRLL(prevFace, mFaceS);
               DoLUU(prevFace, mFaceS);
               Step6PlacePoleEdge(prevFace, destFaceIndex);
            }
         }         
      }
      assert(bFound);
   }
   
   bool bEdgeParityOK = CheckEdgeParity();
   if (!bEdgeParityOK)
   {
      // take evasive action; we will swap two same-colored
      // edges in the equator. This is a transposition, so
      // it should cause the edges in the South half to
      // switch to even parity. We'll somewhat arbitrarily
      // swap the two 2-4 color edges, located at 2-10
      // and 4-8. Just as in earlier Step 6 work we move one
      // edge to the South Pole, place it correctly which
      // moves the other edge to the South Pole, then place
      // that edge.
      
      fMega.WriteComment("Step6 evasive action to fix parity");
      DoLUU(10, 11);      // move 2-10 to South Pole
      DoLLL(10, 11);
      DoRUU(10, 11);   
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 2);
            // position      
      DoRUU(12, 8);      // move 2-10 to equator, 4-8 to South Pole
      DoRLL(12, 8);
      DoLUU(12, 8);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 2);   // position
      DoLUU(10, 11);      // move 4-8 to equator
      DoLLL(10, 11);
      DoRUU(10, 11);   
   }

   Step6Verify();
}

void CSolver::Step6PlacePoleEdge(int fromSFace, int toEdgeIndex)
{
   // rotate the edge CounterCW to the correct position
   fMega.WriteComment("Step6PlacePoleEdge");
   CMegaminx::face_t toSFace = 
                  CMegaminx::kMiddleEqEdgeS[toEdgeIndex];
   int dist = Distance(fromSFace, toSFace);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, dist);
   
   // flip the edge if it is wrongly oriented
   CMegaminx::face_t nextFace = fMega.NextSouthEqFace(toSFace);
   if (fMega.EdgeFaceletColor(toSFace, kSouthPoleFace) != 
         fMega.CorrectColor(toSFace))
   {
      fMega.WriteComment("Step6PlacePoleEdge flip edge");
      DoRLL(toSFace, nextFace);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
   }
   
   // now drop it into position, either on left or right
   CMegaminx::face_t prevFace = fMega.PrevSouthEqFace(toSFace);
   if ((toEdgeIndex & 1) == 0)
   {
      // even index, so edge is below and to right
      DoLUU(toSFace, nextFace);
      DoLLL(toSFace, nextFace);
      DoRUU(toSFace, nextFace);
   }
   else
   {
      // odd index, so edge is below and to left
      DoRUU(prevFace, toSFace);
      DoRLL(prevFace, toSFace);
      DoLUU(prevFace, toSFace);
   }
}

//////////////////////////////////////////////////////////////////////
// Step 7. Setting the Southern Equatorial Edges
//////////////////////////////////////////////////////////////////////
//
void CSolver::Step7()
{
   for (int i = 0; i < CMegaminx::kNumSouthEqEdges; i++)
   {
      CMegaminx::face_t destFaceL = CMegaminx::kSouthEqEdgeL[i];
      CMegaminx::face_t destFaceR = CMegaminx::kSouthEqEdgeR[i];
      if (fMega.IsEdgeCorrect(destFaceL, destFaceR))
         continue;
         
      CMegaminx::color_t destColorL = fMega.CorrectColor(destFaceL);
      CMegaminx::color_t destColorR = fMega.CorrectColor(destFaceR);
      
      // check to see if needed color is on South Pole
      bool bFound = false;
      for (int j = 0; j < CMegaminx::kNumSouthPoleEdges && !bFound; 
                  j++)
      {
         CMegaminx::face_t srcFaceN = CMegaminx::kSouthPoleEdgeN[j];
         CMegaminx::face_t srcFaceS = CMegaminx::kSouthPoleEdgeS[j];
         if (fMega.EdgeHasColors(srcFaceN, srcFaceS, destColorL, 
                     destColorR))
         {
            bFound = true;
            Step7PlacePoleEdge(srcFaceN, destFaceL, destFaceR);
         }
      }
      
      // check if needed color is on South Equator; do not
      // check already-placed edges
      if (!bFound)
      {
         for (int j = i; j < CMegaminx::kNumSouthEqEdges && !bFound; 
                  j++)
         {
            int srcFaceL = CMegaminx::kSouthEqEdgeL[j];
            int srcFaceR = CMegaminx::kSouthEqEdgeR[j];
            if (fMega.EdgeHasColors(srcFaceL, srcFaceR, destColorL, 
                     destColorR))
            {
               // loft the edge using RLL, so it goes above srcFaceR,
               // then move to correct place (remember that we are
               // looking at the Megaminx upside down, so the
               // left face is srcFaceR)
               bFound = true;
               fMega.WriteComment("Step7 loft edge");
               DoRLL(srcFaceR, srcFaceL);
               Step7PlacePoleEdge(srcFaceR, destFaceL, destFaceR);      
            }
         }
      }
      assert(bFound);
   }
   Step7Verify();
}

// place an equatorial edge that is currently on the pole;
// eqFace is the equatorial face it is below.
void CSolver::Step7PlacePoleEdge(CMegaminx::face_t srcFaceN, 
                        CMegaminx::face_t destFaceL,
                        CMegaminx::face_t destFaceR)
{
   // find the face it belongs to and rotate it there;
   // find the CW distance we should move; we move it so
   // its equatorial color matches the destination face color. Then
   // lift it into position using RLL or LLL. Remember we measure
   // right and left with the Megaminx right-side up.
   fMega.WriteComment("Step7PlacePoleEdge");
   CMegaminx::color_t destFaceColor = 
      fMega.EdgeFaceletColor(srcFaceN, kSouthPoleFace);
   bool bLiftFromLeft = 
      (destFaceColor == fMega.CorrectColor(destFaceL));
   CMegaminx::face_t destFace = 
            bLiftFromLeft ? destFaceL : destFaceR;
   int dist = Distance(destFace, srcFaceN);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCW, dist);
   if (bLiftFromLeft)
      DoRLL(destFaceR, destFaceL);
   else
      DoLLL(destFaceR, destFaceL);
}

//////////////////////////////////////////////////////////////////////
// Step 8. Setting the South Pole edges
//////////////////////////////////////////////////////////////////////
//
// This step both positions and orients the South Pole edges.
//
// We pick a fixed orientation to make the rotation calculations easy.
// The parked edge in on faces 8 and 9, and we rotate it for lofting
// to be on faces 7 and 8, so we'll use LLL to loft it. 
// The reference edge is on faces 7 and 10, the second edge is on faces
// 7 and 11, and the third edge is on faces 7 and 12.
//
// NOTE: All edge operations must be be done using the fixed
// edge 8-9, otherwise things won't be properly aligned
// after setting the first 3 edges.
//
// We don't have to return the South Pole after each move.

void CSolver::Step8()
{
   Step8ReferenceEdge();
   Step8SecondEdge();
   Step8ThirdEdge();
   Step8RestoreEquator();
   Step8OrientFourFive();
   
   Step8Verify();
}

void CSolver::Step8ReferenceEdge()
{
   if (fMega.IsEdgeCorrect(7, 10))
      return;   // already correct, no action needed
      
   fMega.WriteComment("Step8ReferenceEdge");
   // place and orient the reference edge
   
   // locate the correctly colored edge
   bool bFound = false;
   CMegaminx::face_t srcFace = 0;
   for (int i = 0; i < CMegaminx::kNumSouthPoleEdges && !bFound; 
                     i++)
   {
      srcFace = CMegaminx::kSouthPoleEdgeN[i];
      bFound = fMega.EdgeHasColors(srcFace, kSouthPoleFace, 1, 4);
   }
   assert(bFound);
   
   if (fMega.EdgeFaceletColor(kSouthPoleFace, srcFace) != 1)
   {
      // need to orient edge
      // first move the edge over to flipping area, at face 9
      int dist = Distance(9, srcFace);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCW, dist);
      
      // now flip the edge; it will go to face 8
      DoLLL(8, 9);
      srcFace = 8;   // pretend it was here all along
   }
   
   // move the edge into position at edge 10
   int dist = Distance(10, srcFace);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCW, dist);
}

void CSolver::Step8SecondEdge()
{
   if (fMega.IsEdgeCorrect(7, 11))
      return;   // already correct, no action needed
      
   // place and orient the second edge
   // For this operation we have to return the South Pole face
   // to its original position so that the reference edge will
   // be in place.
      
   // locate the correct color
   bool bFound = false;
   CMegaminx::face_t srcFace = 0;
   for (int i = 0; i < CMegaminx::kNumSouthPoleEdges && !bFound; 
                  i++)
   {
      srcFace = CMegaminx::kSouthPoleEdgeN[i];
      bFound = fMega.EdgeHasColors(srcFace, kSouthPoleFace, 1, 5);
   }
   // if not found, the desired edge is already parked, so
   // skip the parking

   if(bFound)
   {
      // we will loft using faces 8 and 9, so the South Pole
      // face must be rotated to place aFace in one of these
      // positions, but such that the reference edge (face 10)
      // does not go to either; this means our CW rotations must
      // be something other than 1 and 2.
      bool bUseRLL = true;
      int dist = Distance(9, srcFace);
      if (dist == 2)   // dist == 1 is impossible because that moves 10 to 9
      {
         dist = 3;   // rotate to 10 instead
         bUseRLL = false;
      }
         
      fMega.WriteComment("Step8SecondEdge parking");
      {
         CTempRotate rot1(fMega, kSouthPoleFace, dist, 
                     CMegaminx::eCW);
         if (bUseRLL)      // park edge 2
            DoRLL(8, 9);
         else
            DoLLL(8, 9);
      }
   }
   
   // now move the edge from the parked position to the South Pole
   fMega.WriteComment("Step8SecondEdge placing");
   {
      CTempRotate rot2(fMega, kSouthPoleFace, 2, 
                     CMegaminx::eCounterCW);
      DoRLL(8, 9);   // places the edge
      
      // if the edge is not oriented correctly, re-orient it
      if (fMega.EdgeFaceletColor(kSouthPoleFace, 8) != 1)
      {
         fMega.WriteComment("Step8SecondEdge re-orienting");
         DoRLL(8, 9);
         fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
         DoLLL(8, 9);
         fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
         DoRLL(8, 9);
      }
   }
}

void CSolver::Step8ThirdEdge()
{
   if (fMega.IsEdgeCorrect(7, 12))
      return;   // already correct, no action needed
      
   // place and orient the third edge
   // For this operation we have to return the South Pole face
   // to its original position so that the reference edge will
   // be in place.
   
   // locate the correct color
   bool bFound = false;
   CMegaminx::face_t srcFace = 0;
   for (int i = 0; i < CMegaminx::kNumSouthPoleEdges && !bFound; 
                  i++)
   {
      srcFace = CMegaminx::kSouthPoleEdgeN[i];
      bFound = fMega.EdgeHasColors(srcFace, kSouthPoleFace, 1, 6);
   }
   
   // if not found, the desired edge is already parked, so
   // skip the parking

   if(bFound)
   {
      // we will loft using faces 8 and 9, so the South Pole
      // face must be rotated to place aFace in one of these
      // positions, but such that the reference edge (face 10)
      // and second edge do not go there either.
      bool bUseRLL = true;
      int dist = 0;
      switch (srcFace)
      {
         case 12:
         {
            // rotate CounterCW 1 to face 8, use LLL
            dist = 1;
            bUseRLL = false;
         }
         break;
         
         case 8:
         {
            // already in place on face 8, use LLL
            dist = 0;
            bUseRLL = false;
         }
         break;
         
         case 9:
         {
            // already in place on face 9, use RLL
            dist = 0;
            bUseRLL = true;      
         }
         break;
         
         default:
         {
            assert(false);
         }
         break;
      }
         
      fMega.WriteComment("Step8ThirdEdge parking");
      {
         CTempRotate rot1(fMega, kSouthPoleFace, dist, 
                  CMegaminx::eCounterCW);
         if (bUseRLL)
            DoRLL(8, 9);
         else
            DoLLL(8, 9);
      }
   }
      
   // now move the edge from the parked position to the South Pole
   fMega.WriteComment("Step8ThirdEdge placing");
   {
      CTempRotate rot2(fMega, kSouthPoleFace, 1, 
                  CMegaminx::eCounterCW);
      DoRLL(8, 9);   // places the edge
      
      // if the edge is not oriented correctly, re-orient it
      if (fMega.EdgeFaceletColor(kSouthPoleFace, 8) != 1)
      {
         DoRLL(8, 9);
         fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
         DoLLL(8, 9);
         fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
         DoRLL(8, 9);
      }
   }
}

void CSolver::Step8RestoreEquator()
{
   // figure out which South Pole edge has the equatorial edge
   // and restore it to its correct place
   
   fMega.WriteComment("Step8RestoreEquator");
   if (fMega.EdgeFaceletColor(kSouthPoleFace, 8) != 1 &&
      fMega.EdgeFaceletColor(8, kSouthPoleFace) != 1)
      DoLLL(8, 9);   // 7-8 edge should be on equator
   else if (fMega.EdgeFaceletColor(kSouthPoleFace, 9) != 1 &&
      fMega.EdgeFaceletColor(9, kSouthPoleFace) != 1)
      DoRLL(8, 9);   // 7-9 edge should be on equator

}

void CSolver::Step8OrientFourFive()
{
   // according to the Meffert solution, 4 and 5 will have
   // the correct colors but might be oriented incorrectly.
   // check that they have the correct colors.
   assert(fMega.EdgeHasColors(kSouthPoleFace, 8, 1, 2));
   assert(fMega.EdgeHasColors(kSouthPoleFace, 9, 1, 3));
   // check that everything is correctly oriented
   bool b78OK = fMega.IsEdgeCorrect(kSouthPoleFace, 8);
   bool b79OK = fMega.IsEdgeCorrect(kSouthPoleFace, 9);
   if (b78OK && b79OK)
      return;   // we're done!
      
   // if only one is bad, then the equator is also bad, so
   // loft it to the pole first
   fMega.WriteComment("Step8OrientFourFive lofting");
   enum Lofting {eNothing = 1, eLLL, eRLL};
   Lofting whichLoft = eNothing;
   if (b78OK && !b79OK)
   {
      whichLoft = eLLL;   // loft to left
      DoLLL(8, 9);
   }
   else if (!b78OK && b79OK)
   {
      whichLoft = eRLL;   // loft to right;
      DoRLL(8, 9);
   }
      
   // now the mis-oriented edges are on the South Pole;
   // apply the special operation to re-orient them
   fMega.WriteComment("Step8OrientFourFive placing");
   for (int i = 1; i <= 4; i++)
   {
      DoRLL(8, 9);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
   }
   fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
   for (int i = 1; i <= 4; i++)
   {
      DoRLL(8, 9);
      fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
   }
   fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
   
   // now return the equatorial edge if needed
   // NOTE: we do the operation twice;
   // the published Meffert solution incorrectly shows it
   // only once.
   fMega.WriteComment("Step8OrientFourFive returning equator");
   if (whichLoft == eLLL)
   {
      DoLLL(8, 9);
      DoLLL(8, 9);
   }
   else if (whichLoft == eRLL)
   {
      DoRLL(8, 9);
      DoRLL(8, 9);
   }
}

//////////////////////////////////////////////////////////////////////
// Step 9. Placing the southern equatorial corners
//////////////////////////////////////////////////////////////////////
//
// We swap corners to get the southern equatorial corners correctly
// placed. Our basic case is when one corner is on the South Pole
// and one is on the South Equator; we convert the other case
// (both on South Equator) to the first case by lofting one of the
// corners to the South Pole.
void CSolver::Step9()
{
   for (CMegaminx::vertex_t dst = CMegaminx::kFirstSouthEqVertex; 
         dst <= CMegaminx::kLastSouthEqVertex; dst++)
   {
      if (fMega.IsCornerCorrectlyPlaced(dst))
         continue;   // already OK, so skip

      // find src, the vertex holding the corner that should be here
      // src is either on the South Pole or on the Southern Equator
      //
      CMegaminx::color_t c0 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[dst][0]);
      CMegaminx::color_t c1 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[dst][1]);
      CMegaminx::color_t c2 = 
         fMega.CorrectColor(CMegaminx::kCornerFaces[dst][2]);
      CMegaminx::vertex_t src = 
                  fMega.CornerHavingColors(c0, c1, c2);
      if (src <= CMegaminx::kLastSouthEqVertex)
      {
         // src is on South Equator, so we must loft it
         // to the South Pole
         fMega.WriteComment("Step 9 lofting");
         CMegaminx::face_t leftFace = 
            CMegaminx::kCornerFaces[src][2];
         CMegaminx::face_t rightFace = 
            CMegaminx::kCornerFaces[src][1];
         DoRLL(leftFace, rightFace);
         DoRLL(leftFace, rightFace);
         DoRLL(leftFace, rightFace);
         src += 5;   // src is now on the South Pole
      }
      Step9EquatorAndPole(dst, src);
   }
   
   Step9Verify();
}

void CSolver::Step9EquatorAndPole(CMegaminx::vertex_t dst, 
                  CMegaminx::vertex_t src)
{
   // rotate the South Pole CCW so src is directly above dst;
   // we need to rotate it back when we are finished to avoid disturbing
   // the South Pole edges.
   int dist = Distance(dst + 5, src);
   fMega.WriteComment("Step9EquatorAndPole rotating pole");
   CTempRotate rot1(fMega, kSouthPoleFace, dist, 
            CMegaminx::eCounterCW);
      
   // now swap the vertices
   fMega.WriteComment("Step9EquatorAndPole swapping");
   CMegaminx::face_t leftFace = CMegaminx::kCornerFaces[dst][2];
   CMegaminx::face_t rightFace = CMegaminx::kCornerFaces[dst][1];
   DoRLL(leftFace, rightFace);
   DoRLL(leftFace, rightFace);
   DoRLL(leftFace, rightFace);
}

//////////////////////////////////////////////////////////////////////
// Step 10. Placement Of the South Pole corners
//////////////////////////////////////////////////////////////////////
//
void CSolver::Step10()
{
   for (CMegaminx::vertex_t destCorner = CMegaminx::kFirstSouthPoleVertex; 
         destCorner <= CMegaminx::kLastSouthPoleVertex; destCorner++)
   {
      if (fMega.IsCornerCorrectlyPlaced(destCorner))
         continue;
         
      // find the corner that belongs here; do not check
      // already-placed corners. Do not check the srcCorner
      // because we already know it is not correctly placed
      // (we don't do orientation until Step 11).
      bool bFound = false;
      for (CMegaminx::vertex_t srcCorner = destCorner + 1;
            srcCorner <= CMegaminx::kLastSouthPoleVertex && !bFound;
                   srcCorner++)
      {
         if (destCorner == fMega.CorrectSouthernVertex(srcCorner))
         {
            bFound = true;
            // now move everybody; we alway rotate to vertex 15,
            // and the left and right faces are 12 and 8.
            // We use two blocks so the CTempRotate destructors will
            // rotate back to the original position in between 
            // transformations
            fMega.WriteComment("Step10");
            {
               // swap srcCorner and 10
               CTempRotate rot1(fMega, kSouthPoleFace, 
                           srcCorner - 15, CMegaminx::eCounterCW);
               DoRUU(12, 8);
               DoRUU(12, 8);
               DoRUU(12, 8);
            }
            
            {
               // swap destCorner and srcCorner (which is now in 10)
               CTempRotate rot2(fMega, kSouthPoleFace, 
                           destCorner - 15, CMegaminx::eCounterCW);
               DoRUU(12, 8);
               DoRUU(12, 8);
               DoRUU(12, 8);
            }
            
            {
               // restore 10 to its original position
               CTempRotate rot1(fMega, kSouthPoleFace, 
                           srcCorner - 15, CMegaminx::eCounterCW);
               DoRUU(12, 8);
               DoRUU(12, 8);
               DoRUU(12, 8);
            }
         
         }
      }
      assert(bFound);
   }
   Step10Verify();
}

//////////////////////////////////////////////////////////////////////
// Step 11. Orientation Of the southern equatorial and South Pole corners
//////////////////////////////////////////////////////////////////////
//
// We find pairs of oppositely-oriented corner pieces that are not
// correctly oriented, drop them to the South Pole, then
// swap them and return them to their original position. They
// have to be dropped such that they are next to each other.
// We treat the case "neither on South Pole" as the basic case
// and transform all others to that:
// (1) if both are on the South Pole, we pick two separate faces,
//     one holding each corner, and rotate those CCW to drop them
//     to the Southern Equatorial belt.
// (2) if one is on the South Pole and one not, we rotate the
//     South Pole so that corner is not touched the face the other is
//     on, then rotate a face the South Pole corner is on.

class CStep11CornerVisitor : public CCornerVisitor
{
public:
   CStep11CornerVisitor(CMegaminx& rMega) :
      fMega(rMega),
      fNeedsCounterCWCorner1(-1), fNeedsCounterCWCorner2(-1),
      fNeedsCWCorner1(-1), fNeedsCWCorner2(-1)
      {};
   ~CStep11CornerVisitor() {};
   
   virtual void VisitCorner(int cornerIndex);
   
   // member variables - these are the vertex indices
   // of corners that need 1 turn CCW or CW to be
   // correctly oriented
   CMegaminx::vertex_t fNeedsCounterCWCorner1;
   CMegaminx::vertex_t fNeedsCounterCWCorner2;
   CMegaminx::vertex_t fNeedsCWCorner1;
   CMegaminx::vertex_t fNeedsCWCorner2;
   
   CMegaminx& fMega;
};

void CStep11CornerVisitor::VisitCorner(int cornerIndex)
{
   // maybe we are already done
   if ((fNeedsCounterCWCorner1 >= 0) &&
         (fNeedsCWCorner1 >= 0) )
      return;

   // check whether the corner is correctly oriented,
   // and if not, which direction it should be turned
   
   // face numbers in CCW order
   CMegaminx::face_t f0 = CMegaminx::kCornerFaces[cornerIndex][0];
   CMegaminx::face_t f1 = CMegaminx::kCornerFaces[cornerIndex][1];
   CMegaminx::face_t f2 = CMegaminx::kCornerFaces[cornerIndex][2];
   
   // facelet color for facelet on face f0
   CMegaminx::color_t c0 = fMega.CornerFaceletColor(f0, f1, f2);

   if (c0 == CMegaminx::CorrectColor(f1))
   {
      // should turn CCW
      if (fNeedsCounterCWCorner1 < 0)
         fNeedsCounterCWCorner1 = cornerIndex;
      else if (fNeedsCounterCWCorner2 < 0)
         fNeedsCounterCWCorner2 = cornerIndex;
   }
   else if (c0 == CMegaminx::CorrectColor(f2))
   {
      // should turn CW
      if (fNeedsCWCorner1 < 0)
         fNeedsCWCorner1 = cornerIndex;
      else if (fNeedsCWCorner2 < 0)
         fNeedsCWCorner2 = cornerIndex;
   }
   // otherwise is correctly oriented, do nothing
}

void CSolver::Step11()
{
   // the transformation turns one corner CW and one corner CounterCW,
   // so ideally we would pick corners that need this to be correctly
   // positioned; however if we don't have such a pair we can pick
   // two with the same positioning, and then one will become correctly
   // positioned and one will be switched to the opposite positioning.
   for (int i = 0; i < 100; i++)   // break out if stuck in loop
   {
      CStep11CornerVisitor aVisitor(fMega);
      VisitAllCorners(aVisitor);
      int vCounterCW = aVisitor.fNeedsCounterCWCorner1;
      int vCW = aVisitor.fNeedsCWCorner1;
      if ((vCounterCW < 0) && (vCW < 0))
         break;
      
      // check that we the ideal pairing, and if not double up
      // on the other orientation
      if (vCounterCW < 0)
         vCounterCW = aVisitor.fNeedsCWCorner2;
      else if (vCW < 0)
         vCW = aVisitor.fNeedsCounterCWCorner2;
      assert(vCW >= 0 && vCounterCW >= 0);
         
      bool bCounterCWIsOnEquator = 
            fMega.IsSouthEquatorVertex(vCounterCW);
      bool bCWIsOnEquator = fMega.IsSouthEquatorVertex(vCW);

      if (bCounterCWIsOnEquator && bCWIsOnEquator)
      {
         Step11BothEquators(vCounterCW, vCW);
      }
      else
      {
         // pick two non-adjacent faces for dropping the vertices
         // to the South Equator. If one is already on the equator
         // we don't have to move it. If the vertices are directly
         // above each other (one on pole and one on equator), we need
         // to rotate the South Pole first so they can be on
         // non-adjacent faces.
         
         // first check for possibly needed pole rotation
         int spCount = 0;
         if (std::abs(vCounterCW - vCW) == 5)
         {
            // the vertices are above each other, so we'll
            // rotate the pole 1 CCW
            spCount = 1;
            if (!bCounterCWIsOnEquator)
               vCounterCW = fMega.NextCounterCWVertex(kSouthPoleFace, 
                  vCounterCW);
            else
               vCW = fMega.NextCounterCWVertex(kSouthPoleFace, vCW);
         }
         
         // now do any necessary dropping of vertices to the South Equator
         
         // check counterCW vertex
         int faceCounterCW = 0, faceCW = 0;
         fMega.FindNonAdjacentSouthFaces(vCounterCW, vCW, 
                  &faceCounterCW, &faceCW);
            
         int counterCWCount = 0, cwCount = 0;
         int nextCounterCW = vCounterCW, nextCW = vCW;
         CMegaminx::Direction directionCounterCW = CMegaminx::eCW, 
                  directionCW = CMegaminx::eCW;
         if (!bCounterCWIsOnEquator)
         {
            counterCWCount = 1;
            nextCounterCW = fMega.NextCWVertex(faceCounterCW, 
                  vCounterCW);
            directionCounterCW = CMegaminx::eCW;
            if (!fMega.IsSouthEquatorVertex(nextCounterCW))
            {
               // wrong direction, go in other direction
               nextCounterCW = fMega.NextCounterCWVertex(faceCounterCW, 
                  vCounterCW);
               directionCounterCW = CMegaminx::eCounterCW;
            }
         }
         
         // check CW vertex
         if (!bCWIsOnEquator)
         {
            cwCount = 1;
            nextCW = fMega.NextCWVertex(faceCW, vCW);
            directionCW = CMegaminx::eCW;
            if (!fMega.IsSouthEquatorVertex(nextCW))
            {
               // wrong direction, go in other direction
               nextCW = fMega.NextCounterCWVertex(faceCW, vCW);
               directionCW = CMegaminx::eCounterCW;
            }
         }
         
         fMega.WriteComment("Step11 non-equator case");
         CTempRotate rotSouthPole(fMega, kSouthPoleFace, spCount, 
               CMegaminx::eCounterCW);
         CTempRotate rotCounterCW(fMega, faceCounterCW, 
                              counterCWCount, directionCounterCW);
         CTempRotate rotCW(fMega, faceCW, cwCount, directionCW);
         Step11BothEquators(nextCounterCW, nextCW);
      
      }
   }
}

void CSolver::Step11BothEquators(int vCounterCW, int vCW)
{
   // we will loft the colors of both vertices to the South Pole;
   // need to figure out which direction to rotate their faces,
   // and what rotation is needed for the South Pole to have the
   // lofted corners together.
   
   // pick two non-adjacent faces for lofting the vertices
   int faceCounterCW, faceCW;   // faces to rotate
   fMega.FindNonAdjacentSouthFaces(vCounterCW, vCW, 
         &faceCounterCW, &faceCW);   
   
   // figure out the direction to rotate each face, and which vertex 
   // the corner will loft to
   // We will position the CCW corner 1 vertex CW of the CW face,
   // and rotate the CW face to put the CW vertex next to the CCW 
   // vertex. The right face will then be the CW face.
   CMegaminx::Direction dCounterCW, dCW;   // directions to loft
   int loftedCounterCW1, loftedCW1;
   
   loftedCounterCW1 = fMega.NextCounterCWVertex(faceCounterCW, 
         vCounterCW);
   if (fMega.IsSouthPoleVertex(loftedCounterCW1))
   {
      dCounterCW = CMegaminx::eCounterCW;
   }
   else
   {
      // wrong direction, go back in other direction
      dCounterCW = CMegaminx::eCW;
      loftedCounterCW1 = fMega.NextCWVertex(faceCounterCW, 
         vCounterCW);
   }
   int cwClicks = 0;
   loftedCW1 = fMega.NextCWVertex(faceCW, vCW);
   if (fMega.IsSouthPoleVertex(loftedCW1))
   {
      dCW = CMegaminx::eCW;   // OK, at left edge of face
      cwClicks = 1;
   }
   else
   {
      // wrong direction, go two vertices in other direction
      dCW = CMegaminx::eCounterCW;
      loftedCW1 = fMega.NextCounterCWVertex(faceCW, vCW);
      loftedCW1 = fMega.NextCounterCWVertex(faceCW, loftedCW1);
      cwClicks = 2;
   }
   
   // now figure out the South Pole rotation
   // we want CCW to be on the left of CW
   // as a simplification we will always rotate the South Pole CCW
   int iCounterCW = -1, iCW = -1;
   for (int i = 0; i < 5; i++)
   {
      if (CMegaminx::kFaceVertices[kSouthPoleFace][i] == 
                        loftedCounterCW1)
         iCounterCW = i;
      else if (CMegaminx::kFaceVertices[kSouthPoleFace][i] == 
                        loftedCW1)
         iCW = i;
   }
   int distToRotate = (iCW - 1) - iCounterCW;
   if (distToRotate < 0)
      distToRotate += 5;
   if (distToRotate >= 5)
      distToRotate -= 5;
      
   // OK, now we are ready to rotate everything!
   int rightFace = faceCW;
   int leftFace = faceCW - 1;
   if (leftFace <= kSouthPoleFace)
      leftFace += 5;
   
   // rotate the corners into place
   fMega.WriteComment("Step11BothEquators lofting");
   CTempRotate loftCounterCW(fMega, faceCounterCW, 1, dCounterCW);
   CTempRotate southPoleRotate(fMega, kSouthPoleFace, 
            distToRotate, CMegaminx::eCounterCW);
   CTempRotate loftCW(fMega, faceCW, cwClicks, dCW);
   
   // do the corner swap   
   fMega.WriteComment("Step11BothEquators corner swap");
   DoRUU(leftFace, rightFace);
   DoRUU(leftFace, rightFace);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCounterCW, 1);
   DoLUU(leftFace, rightFace);
   DoLUU(leftFace, rightFace);
   fMega.Slice(kSouthPoleFace, CMegaminx::eCW, 1);
}

#pragma mark === Verification Routines ===
//////////////////////////////////////////////////////////////////////
// Verification Routines
//////////////////////////////////////////////////////////////////////

// see online code archive
CMegaminxApp.cpp
// see online code archive
CMegaminx.cpp
#include "CMegaminx.h"
#include "CMegaminxApp.h"

#include 

/////////////////////////////////////////////////////////////////
// initialization of tables

const CMegaminx::vertex_t CMegaminx::kFaceVertices[13][5] =
{
   // dummy face for 0
   {0, 0, 0, 0, 0},
   
    // North Pole face
    {0, 1, 2, 3, 4},

    // Northern Equatorial faces
    {3, 2, 7, 12, 8},
    {2, 1, 6, 11, 7},
    {1, 0, 5, 10, 6},
    {0, 4, 9, 14, 5},
    {4, 3, 8, 13, 9},
    
    // South Pole face
    {19, 18, 17, 16, 15},
    
    // Southern Equatorial faces
    {19, 15, 10, 5, 14},
    {18, 19, 14, 9, 13},
    {17, 18, 13, 8, 12},
    {16, 17, 12, 7, 11},
    {15, 16, 11, 6, 10}
    
};

const CMegaminx::face_t CMegaminx::kCornerFaces[20][3] = 
{
   // North Pole
   {1, 5, 4},
   {1, 4, 3},
   {1, 3, 2},
   {1, 2, 6},
   {1, 6, 5},
   
   // Northern Equatorial
   {4, 5, 8},
   {3, 4, 12},
   {2, 3, 11},
   {2, 10, 6},
   {5, 6, 9},
   
   // Southern Equatorial
   {4, 8, 12},
   {3, 12, 11},
   {2, 11, 10},
   {6, 10, 9},
   {5, 9, 8},
   
   // South Pole
   {7, 12, 8},
   {7, 11, 12},
   {7, 10, 11},
   {7, 9, 10},
   {7, 8, 9}
};

// list of adjacent face numbers, indexed by face number.
// each item lists the faces adjacent to this one, 
// in counterclockwise order as viewed from above this face.
// This list must be coordinated with the vertex list so that
// face[1] touches vertices [0] and [1].
const CMegaminx::face_t CMegaminx::kAdjacentFaces[13][5] =
{
   {0, 0, 0, 0, 0},   // dummy for face 0
   
    {4, 3, 2, 6, 5},
    {1, 3, 11, 10, 6},
    {1, 4, 12, 11, 2},
    {1, 5, 8, 12, 3},
    {1, 6, 9, 8, 4},
    {1, 2, 10, 9, 5},
    
    {9, 10, 11, 12, 8},
    {7, 12, 4, 5, 9},
    {7, 8, 5, 6, 10},
    {7, 9, 6, 2, 11},
    {7, 10, 2, 3, 12},
    {7, 11, 3, 4, 8}
};

const CMegaminx::face_t CMegaminx::kFaceBelow[20] =
{5, 4, 3, 2, 6, 8, 12, 11, 10, 9, 8, 12, 11, 10, 9, 0, 0, 0, 0, 0};
const CMegaminx::face_t CMegaminx::kFaceAbove[20] =
{0, 0, 0, 0, 0, 4, 3, 2, 6, 5, 4, 3, 2, 6, 5, 12, 11, 10, 9, 8};

const CMegaminx::face_t CMegaminx::kFaceDownRight[13] = 
{0, 0, 10, 11, 12, 8, 9, 0, 0, 0, 0, 0, 0};
const CMegaminx::face_t CMegaminx::kFaceDownLeft[13] =
{0, 0, 11, 12, 8, 9, 10, 0, 0, 0, 0, 0, 0};
const CMegaminx::face_t CMegaminx::kFaceUpRight[13] =
{0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 2, 3}; 
const CMegaminx::face_t CMegaminx::kFaceUpLeft[13] =
{0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 2, 3, 4}; 

// list of North Pole edges
const  CMegaminx::face_t CMegaminx::kNorthPoleEdgeN[kNumNorthPoleEdges] = 
{1, 1, 1, 1, 1};
const CMegaminx::face_t CMegaminx::kNorthPoleEdgeS[kNumNorthPoleEdges] = 
{2, 3, 4, 5, 6};

// list of North Equator edges.
const CMegaminx::face_t CMegaminx::kNorthEqEdgeL[kNumNorthEqEdges] = 
{2, 3, 4, 5, 6};
const CMegaminx::face_t CMegaminx::kNorthEqEdgeR[kNumNorthEqEdges] =
{6, 2, 3, 4, 5};

// list of middle equatorial edges. Ordered in two arrays,
// the first giving the south face and the second the corresponding
// north face. For even indices the edge is below and right of the
// north face, for odd indices it is below and to the left.
const CMegaminx::face_t CMegaminx::kMiddleEqEdgeN[kNumMiddleEqEdges] = 
{5, 4, 4, 3, 3, 2, 2, 6, 6, 5};
const CMegaminx::face_t CMegaminx::kMiddleEqEdgeS[kNumMiddleEqEdges] =
{8, 8, 12, 12, 11, 11, 10, 10, 9, 9};

// list of Southern Equator edges
const CMegaminx::face_t CMegaminx::kSouthEqEdgeL[kNumSouthEqEdges] = 
{8, 9, 10, 11, 12};
const CMegaminx::face_t CMegaminx::kSouthEqEdgeR[kNumSouthEqEdges] =
{12, 8, 9, 10, 11};

// list of South Pole edges
const CMegaminx::face_t CMegaminx::kSouthPoleEdgeN[kNumSouthPoleEdges] =
{8, 9, 10, 11, 12};
const CMegaminx::face_t CMegaminx::kSouthPoleEdgeS[kNumSouthPoleEdges] =
{7, 7, 7, 7, 7};


//////////////////////////////////////////////////////////////////////

CMegaminx::CMegaminx(const string& testNumberString)
{
   // clear all facelets
    std::memset(fEdgeFacelets, 0, sizeof(fEdgeFacelets));
    std::memset(fCornerFacelets, 0, sizeof(fCornerFacelets));

   // open correct rotations file
   string rotationsName = string("rotations") + 
                     testNumberString + ".txt";
   fRotationsStream.open(rotationsName.c_str());
   if (!fRotationsStream.is_open())
   {
      CMegaminxApp::SayFileError(rotationsName);
      return;
   }
}

CMegaminx::~CMegaminx()
{
   fRotationsStream.close();
}
   
void CMegaminx::LoadCornerFacelet(face_t faceNumber1, face_t faceNumber2,
                     face_t faceNumber3, color_t colorNumber)
{
   assert(1 <= faceNumber1 && faceNumber1 <= 12);
   assert(1 <= faceNumber2 && faceNumber2 <= 12);
   assert(1 <= faceNumber3 && faceNumber3 <= 12);
   
   if (faceNumber2 < faceNumber3)
      fCornerFacelets[faceNumber1][faceNumber2][faceNumber3] 
         = colorNumber;
   else
      fCornerFacelets[faceNumber1][faceNumber3][faceNumber2] 
         = colorNumber;
   
}

                     
void CMegaminx::LoadEdgeFacelet(face_t faceNumber1, face_t faceNumber2,
                     color_t colorNumber)
{
   assert(1 <= faceNumber1 && faceNumber1 <= 12);
   assert(1 <= faceNumber2 && faceNumber2 <= 12);

   fEdgeFacelets[faceNumber1][faceNumber2] = colorNumber;
}

void CMegaminx::SliceImp(CMegaminx::face_t faceNumber, 
            Direction direction)
{
    short *pFaceletColors1[5], *pFaceletColors2[5],
            *pFaceletColors3[5];   // pointers to colors to move, listed CCW
    int i;
    
    // rotate the edge facelets
    for (i = 0; i < 5; i++)
    {
        int adj = kAdjacentFaces[faceNumber][i];
        pFaceletColors1[i] = &fEdgeFacelets[adj][faceNumber];
                     // adjacent face
        pFaceletColors2[i] = &fEdgeFacelets[faceNumber][adj];
                     // this face
    }
    RotateFacelets(pFaceletColors1, direction);
    RotateFacelets(pFaceletColors2, direction);

    // rotate the corner facelets
    for (i = 0; i < 5; i++)
    {
        int adjRight = 
                     kAdjacentFaces[faceNumber][(i == 0) ? 4 : i - 1];
        int adjLeft = kAdjacentFaces[faceNumber][i];
        int low, high;
        
        low = (adjRight < adjLeft) ? adjRight : adjLeft;
        high = (adjRight > adjLeft) ? adjRight : adjLeft;
        pFaceletColors1[i] = 
                  &fCornerFacelets[faceNumber][low][high];   // this face
        
        low = (faceNumber < adjLeft) ? faceNumber : adjLeft;
        high = (faceNumber > adjLeft) ? faceNumber : adjLeft;
        pFaceletColors2[i] = 
                  &fCornerFacelets[adjRight][low][high];   // right face
        
        low = (faceNumber < adjRight) ? faceNumber : adjRight;
        high = (faceNumber > adjRight) ? faceNumber : adjRight;
        pFaceletColors3[i] = 
                  &fCornerFacelets[adjLeft][low][high];   // left face
    }
    RotateFacelets(pFaceletColors1, direction);
    RotateFacelets(pFaceletColors2, direction);
    RotateFacelets(pFaceletColors3, direction);
    
    // write out this rotation to the file
    fRotationsStream << faceNumber << ",";
   fRotationsStream << ((direction == eCW) ? '+' : '-');
   fRotationsStream << std::endl;
}

void CMegaminx::Slice(face_t faceNumber, Direction direction, 
                     int clicks)
{
   assert(clicks >= 0);
   for (int i = 0; i < clicks; i++)
      SliceImp(faceNumber, direction);
}

bool CMegaminx::IsSolved()
{
   bool bSolved = true;
   for (int i = 1; i <= 12; i++)
   {
      int rightColor = CorrectColor(i);
      for (int j = 1; j <= 12; j++)
      {
         // check edges
         bSolved = bSolved && (fEdgeFacelets[i][j] == 0 || 
               fEdgeFacelets[i][j]== rightColor);
            
         // check corners
         for (int k = j + 1; k <= 12; k++)
         {
            bSolved = bSolved && (fCornerFacelets[i][j][k] == 0 ||
                  fCornerFacelets[i][j][k] == rightColor);
         }
      }
   }
   return bSolved;
}

void CMegaminx::RotateFacelets(short **ppColor, Direction direction)
{
    // rotate a list of 5 facelet colors
    // the pList is an array of 5 pointer to the color entries
    // in either fEdgeFacelets or fCornerFacelets, in counterclockwise
    // order. For CCW direction we shift the array right, and
    // for CW direction we shift it left.
    short saveColor = 0;
    int i;
    if (direction == eCounterCW)
    {
        // shift right
       saveColor = **(ppColor + 4);
        for (i = 3; i >= 0; i--)
            **(ppColor + i + 1) = **(ppColor + i);
        **(ppColor + 0) = saveColor;
    }
    else
    {
        // shift left
       saveColor = **(ppColor + 0);
        for (i = 0; i < 4; i++)
            **(ppColor + i) = **(ppColor + i + 1);
        **(ppColor + 4) = saveColor;
    }
}

bool CMegaminx::IsCornerCorrectlyPlaced(vertex_t vertex)
{
   // get the actual colors and the correct colors;
   // the item is correctly placed if these lists are
   // rotations of each other.
   int f0, f1, f2;
   int actualColors[5];
   int correctColors[3];
   
   f0 = kCornerFaces[vertex][0];
   f1 = kCornerFaces[vertex][1];
   f2 = kCornerFaces[vertex][2];
   actualColors[0] = actualColors[3] = 
            CornerFaceletColor(f0, f1, f2);
   actualColors[1] = actualColors[4] = 
            CornerFaceletColor(f1, f2, f0);
   actualColors[2] = CornerFaceletColor( f2, f0, f1);
   correctColors[0] = kCornerFaces[vertex][0];
   correctColors[1] = kCornerFaces[vertex][1];
   correctColors[2] = kCornerFaces[vertex][2];
   if (correctColors[0] > 6)
      correctColors[0] -= 6;
   if (correctColors[1] > 6)
      correctColors[1] -= 6;
   if (correctColors[2] > 6)
      correctColors[2] -= 6;
      
   bool bHaveMatch = false;
   for (int i = 0; (i < 3) && !bHaveMatch; i++)
   {
      bHaveMatch = true;
      for (int j = 0; j< 3; j++)
      {
         if (actualColors[i+ j] != correctColors[j])
            bHaveMatch = false;
      }
   }
   
   return bHaveMatch;
}

bool CMegaminx::IsCornerCorrect(vertex_t vertex)
{
   int f0 = kCornerFaces[vertex][0];
   int f1 = kCornerFaces[vertex][1];
   int f2 = kCornerFaces[vertex][2];
   
   bool bOK =    
            CornerFaceletColor(f0, f1, f2) == CorrectColor(f0) &&
            CornerFaceletColor(f1, f2, f0) == CorrectColor(f1) &&
            CornerFaceletColor(f2, f0, f1) == CorrectColor(f2);
            
   return bOK;
}

CMegaminx::vertex_t 
      CMegaminx::FacesToVertex(CMegaminx::face_t f0, 
            CMegaminx::face_t f1, CMegaminx::face_t f2)
{
   // find the lowest face number, then search the table of corner faces
   // for a match. It is an error if we don't find a match.
   int holdFace = 0;
   
   if (f1 < f0)
   {
      holdFace = f0;
      f0 = f1;
      f1 = holdFace;
   }
   if (f2 < f0)
   {
      holdFace = f0;
      f0 = f2;
      f2 = holdFace;
   }
   
   for (int i = 0; i < 20; i++)
   {
      if (f0 == kCornerFaces[i][0])
      {
         int trialFace1 = kCornerFaces[i][1];
         int trialFace2 = kCornerFaces[i][2];
         if ( (f1 == trialFace1 && f2 == trialFace2) ||
             (f1 == trialFace2 && f2 == trialFace1))
         {
            return i;
         }
      }
   }
   
   // trouble, did not find a match
   ::SysBeep(1);
   assert(false);
   return 0;
}

CMegaminx::vertex_t CMegaminx::CorrectSouthernVertex(CMegaminx::vertex_t vertex)
{
   // find where v1 should be;
   // first read out its current colors, then 
   // figure its correct faces
   int oldf0, oldf1, oldf2;   // old face numbers
   int c0, c1, c2;                     // corresponding current colors
   int newf0, newf1, newf2;   // new face numbers
   oldf0 = kCornerFaces[vertex][0];
   oldf1 = kCornerFaces[vertex][1];
   oldf2 = kCornerFaces[vertex][2];
   c0 = CornerFaceletColor(oldf0, oldf1, oldf2);
   c1 = CornerFaceletColor(oldf1, oldf2, oldf0);
   c2 = CornerFaceletColor( oldf2, oldf0, oldf1);
   
   // in general we can find the face numbers by adding 6
   // to each color; however for Southern Equatorial
   // vertices there is one color that should not have 6
   // added, and that is the "non-contiguous" color.
   newf0 = c0 + 6;
   newf1 = c1 + 6;
   newf2 = c2 + 6;
   if (c0 != 1 && c1 != 1 && c2 != 1)
   {
      // not pole, so correct one face
      if (std::abs(newf0 - newf1) == 1 || 
               std::abs(newf0 - newf1) == 4)
         newf2 -= 6;
      else if (std::abs(newf1 - newf2) == 1 || 
                        std::abs(newf1 - newf2) == 4)
         newf0 -= 6;
      else
         newf1 -= 6;
   }
   return FacesToVertex(newf0, newf1, newf2);
}

   
CMegaminx::vertex_t CMegaminx::CornerHavingColors(CMegaminx::color_t c0, 
                  CMegaminx::color_t c1, CMegaminx::color_t c2)
{
   int desiredColors[5];
   desiredColors[0] = desiredColors[3] = c0;
   desiredColors[1] = desiredColors[4] = c1;
   desiredColors[2] = c2;
   int trialVertex;
   for (trialVertex = 0; trialVertex < 20; trialVertex++)
   {
      // get the actual colors and the correct colors;
      // the item is correctly placed if these lists are
      // rotations of each other.
      int f0, f1, f2;
      f0 = kCornerFaces[trialVertex][0];
      f1 = kCornerFaces[trialVertex][1];
      f2 = kCornerFaces[trialVertex][2];
      int trialColors[3];
      trialColors[0] = CornerFaceletColor(f0, f1, f2);
      trialColors[1] = CornerFaceletColor(f1, f2, f0);
      trialColors[2] = CornerFaceletColor(f2, f0, f1);

      bool bHaveMatch = false;
      for (int i = 0; (i < 3) && !bHaveMatch; i++)
      {
         bHaveMatch = true;
         for (int j = 0; j< 3; j++)
         {
            if (desiredColors[i+ j] != trialColors[j])
               bHaveMatch = false;
         }
      }
      
      if (bHaveMatch)
         break;
   }

   return trialVertex;
}

CMegaminx::vertex_t CMegaminx::NextCounterCWVertex(CMegaminx::face_t faceNumber, 
            CMegaminx::vertex_t vertexNumber)
{
   for (int i = 0; i < 5; i++)
   {
      if (kFaceVertices[faceNumber][i] == vertexNumber)
         return kFaceVertices[faceNumber][(i == 4) ? 0 : i + 1];
   }
   
   ::SysBeep(1);   // trouble, vertex not on face
   assert(false);
   return -1;
}

CMegaminx::vertex_t CMegaminx::NextCWVertex(CMegaminx::face_t faceNumber, 
                  CMegaminx::face_t vertexNumber)
{
   for (int i = 0; i < 5; i++)
   {
      if (kFaceVertices[faceNumber][i] == vertexNumber)
         return kFaceVertices[faceNumber][(i == 0) ? 4 : i - 1];
   }
   
   ::SysBeep(1);   // trouble, vertex not on face
   assert(false);
   return -1;

}

void CMegaminx::FindNonAdjacentSouthFaces(CMegaminx::vertex_t v1,
               CMegaminx::vertex_t v2, 
               CMegaminx::face_t *pf1, CMegaminx::face_t *pf2)
{
   int f1[2], f2[2];   // candidate faces
   
   f1[0] = kCornerFaces[v1][1];
   f1[1] = kCornerFaces[v1][2];
   f2[0] = kCornerFaces[v2][1];
   f2[1] = kCornerFaces[v2][2];
   
   for (int i = 0; i < 2; i++)
   {
      for (int j = 0; j < 2; j++)
      {
         int dist = f1[i] - f2[j];
         if (dist < 0)
            dist = -dist;
         if (dist == 2 || dist == 3)
         {
            *pf1 = f1[i];
            *pf2 = f2[j];
            return;
         }
      }
   }
   
   ::SysBeep(1);   // trouble, couldn't find suitable faces
   assert(false);
}

#ifndef NDEBUG
void CMegaminx::WriteComment(const char *pComment)
{
    fRotationsStream << "* " << pComment << std::endl;
}
#else
void CMegaminx::WriteComment(const char * /* pComment */)
{
   // nothing
}
#endif

bool CMegaminx::EdgeHasColors(CMegaminx::face_t f0, CMegaminx::face_t f1, 
               CMegaminx::color_t c0, CMegaminx::color_t c1)
{
   int actualc0 = fEdgeFacelets[f0][f1];
   int actualc1 = fEdgeFacelets[f1][f0];
   bool bMatches = ((actualc0 == c0) && (actualc1 == c1)) ||
               ((actualc0 == c1) && (actualc1 == c0));
   return bMatches;
}

#pragma mark === CTempRotate ===
//////////////////////////////////////////////////////////////////////
CTempRotate::CTempRotate(CMegaminx& rMega, CMegaminx::face_t faceNumber, 
            int clicks, CMegaminx::Direction direction) :
fMega(rMega),
fFaceNumber(faceNumber),
fClicks(clicks),
fDirection(direction)
{
   assert(fClicks >= 0);
   fMega.WriteComment("CTempRotate ctor");
   fMega.Slice(fFaceNumber, direction, fClicks);
}

CTempRotate::~CTempRotate()
{
   fMega.WriteComment("CTempRotate dtor");
   fMega.Slice(fFaceNumber, 
         fDirection == CMegaminx::eCW ? 
               CMegaminx::eCounterCW : CMegaminx::eCW, 
         fClicks);
}

CMegaminxApp.h

// see online code archive
CMegaminx.h
//////////////////////////////////////////////////////////////////////
//
// The CMegaminx class holds the state of the Megaminx. There's only
// one operation for changing state, the Slice function. This class
// also handles writing out the rotations files; this placement is 
// somewhat arbitrary, but is useful because it ensures that changing
// the Megaminx state will always cause the correct movements to be
// written out.
//
//////////////////////////////////////////////////////////////////////

#pragma once

#include 
#include 
using std::string;

class CMegaminx
{
public:
   /////////////////////////////////////////////////////////////////
   // various types and tables describing the Megaminx
   
   // enum for rotation direction; always measured looking
   // down on a face.
   // We also use this for an orientation of a corner; if
   // the color number increase CCW we call it CCW, and if
   // they increase CW we call it CW.
   enum Direction
   {
      eCounterCW = 1,
      eCW = 2
   };
   
   // typedefs for face number, color number, vertex number
   typedef int face_t;
   typedef int color_t;
   typedef int vertex_t;
   
   // vertices are numbered 0-19; these equates give the ranges
   static const int kNumVertices = 20;
   static const vertex_t kFirstNorthPoleVertex = 0;
   static const vertex_t kLastNorthPoleVertex = 4;
   static const vertex_t kFirstNorthEqVertex = 5;
   static const vertex_t kLastNorthEqVertex = 9;
   static const vertex_t kFirstSouthEqVertex = 10;
   static const vertex_t kLastSouthEqVertex =14;
   static const vertex_t kFirstSouthPoleVertex = 15;
   static const vertex_t kLastSouthPoleVertex = 19;
   
   // vertex numbers of each face, indexed 0 through 19,
   // counterclockwise looking at the face from outside
   static const vertex_t kFaceVertices[13][5];

   // list of all vertices and the adjoining faces. Faces are listed
   // in CCW order started with the lowest-numbered.
   static const face_t kCornerFaces[20][3];

   // list of adjacent face numbers, indexed by face number.
   // each item lists the faces adjacent to this one, 
   // in counterclockwise order as viewed from above this face.
   // This list must be coordinated with the vertex list so that
   // face[1] touches vertices [0] and [1].
   static const face_t kAdjacentFaces[13][5];
   
   // list of faces below or below left of a vertex, indexed
   // by vertex number. For North Equatorial vertices the face is
   // below (the vertex is its top vertex) and for North Pole
   // and South Equatorial vertices the face is below and
   // to the left (the vertix is its upper right vertex).
   static const face_t kFaceBelow[20];

   // list of faces above or above right of a vertex, indexed
   // by vertex number. For South Equatorial vertices the face is
   // above (the vertex is its bottom vertex) and for South Pole
   // and North Equatorial vertices the face is above and
   // to the right (the vertix is its lower left vertex).
   static const face_t kFaceAbove[20];
   
   // for equatorial faces, the faces above and below them.
   //
   // faces below and to left or right of given North Equatorial
   // face; indexed by face number
   static const face_t kFaceDownRight[13];
   static const face_t kFaceDownLeft[13];
   // faces above and to left or right of given South Equatorial 
   // face; indexed by face number
   static const face_t kFaceUpRight[13];
   static const face_t kFaceUpLeft[13];
   
   // list of North Pole edges
   static const int kNumNorthPoleEdges = 5;
   static const face_t kNorthPoleEdgeN[kNumNorthPoleEdges];
   static const face_t kNorthPoleEdgeS[kNumNorthPoleEdges];

   // list of North Equator edges.
   static const face_t kNumNorthEqEdges = 5;
   static const face_t kNorthEqEdgeL[kNumNorthEqEdges];
   static const face_t kNorthEqEdgeR[kNumNorthEqEdges];

   // list of middle equatorial edges. Ordered in two arrays,
   // the first giving the north face and the second the corresponding
   // south face. For even indices the edge is below and right of the
   // north face, for odd indices it is below and to the left.
   static const int kNumMiddleEqEdges = 10;
   static const face_t kMiddleEqEdgeN[kNumMiddleEqEdges];
   static const face_t kMiddleEqEdgeS[kNumMiddleEqEdges];

   // list of Southern Equator edges
   static const int kNumSouthEqEdges = 5;
   static const face_t kSouthEqEdgeL[kNumSouthEqEdges];
   static const face_t kSouthEqEdgeR[kNumSouthEqEdges];

   // list of South Pole edges
   static const int kNumSouthPoleEdges = 5;
   static const face_t kSouthPoleEdgeN[kNumSouthPoleEdges];
   static const face_t kSouthPoleEdgeS[kNumSouthPoleEdges];


   /////////////////////////////////////////////////////////////////
   // public functions
   
   CMegaminx(const string& testNumberString);
   ~CMegaminx();
   
   
   // load a corner facelet;
   // faceNumber1 is the face it is on (numbered 1..12),
   // faceNumber2 and faceNumber3 are neighboring faces,
   // and colorNumber is the facelet's color (numbered 1..6).
   void LoadCornerFacelet(face_t faceNumber1, face_t faceNumber2,
                     face_t faceNumber3, color_t colorNumber);
                     
   // similarly, load an edge facelet (no face 3 for these)
   void LoadEdgeFacelet(face_t faceNumber1, face_t faceNumber2,
                     color_t colorNumber);
                     
   // slice operation; rotate face one click in given direction;
   // changes our internal state and writes a line to
   // the rotations file.
   // This is a public function so that our helper classes
   // can get to it.
   void Slice(face_t faceNumber, Direction direction, int clicks);

   // check that the Megaminx is correctly solved
   bool IsSolved();
   
   // check whether a corner is correctly placed, based
   // on its colors. The first checks only placement, not 
   // orientation.
   bool IsCornerCorrectlyPlaced(vertex_t vertex);
   bool IsCornerCorrect(vertex_t vertex);
   
   // check whether an edge is correctly placed and positioned
   bool IsEdgeCorrect(face_t faceL, face_t faceR)
   { return (fEdgeFacelets[faceL][faceR] == CorrectColor(faceL) &&
            fEdgeFacelets[faceR][faceL] == CorrectColor(faceR));};
   
   
   // look up the vertex having these faces
   vertex_t FacesToVertex(face_t f0, face_t f1, face_t f2);
   
   // find correct location of the colors at a vertex,
   // assuming they should be in the Southern
   // half. Returns the correct vertex number.
   // We assume the colors actually belong in the
   // specified Southern half, so caller
   // must check this. "Southern" means South Pole
   // or South Equatorial.
   // Corners with same colors have opposite orientations
   // in the Northern and Southern halves.
   vertex_t CorrectSouthernVertex(vertex_t vertex);
   
   // find the corner having these colors in this order
   // (with rotations allowed). This means the corner that
   // is currently colored with these corners.
   color_t CornerHavingColors(color_t c0, color_t c1, color_t c2);
   
   // return facelet color of face f0 that borders f1 and f2
   color_t CornerFaceletColor(face_t f0, face_t f1, face_t f2)
      { if (f1 < f2) return fCornerFacelets[f0][f1][f2];
         else return fCornerFacelets[f0][f2][f1];};
         
   // return color of edge on f0 that borders f1
   color_t EdgeFaceletColor(face_t f0, face_t f1)
   { return fEdgeFacelets[f0][f1]; };
      
   // return correct color of solved Megaminx face
   static color_t CorrectColor(face_t faceNumber)
      { return (faceNumber <= 6) ? faceNumber : faceNumber - 6;};
   
   // find next vertex on a face
   static vertex_t NextCounterCWVertex(face_t faceNumber, vertex_t vertexNumber);
   static vertex_t NextCWVertex(face_t faceNumber, vertex_t vertexNumber);
   
   // find next or previous (numerically) South Equatorial faces
   static face_t NextSouthEqFace(face_t faceNumber)
      { return (faceNumber == 12) ? 8 : faceNumber + 1; };
   static face_t PrevSouthEqFace(face_t faceNumber)
      { return (faceNumber == 8) ? 12 : faceNumber - 1; };
   
   // find next or previous (numerically) North Equatorial faces
   static face_t NextNorthEqFace(face_t faceNumber)
      { return (faceNumber == 6) ? 2 : faceNumber + 1; };
   static face_t PrevNorthEqFace(face_t faceNumber)
      { return (faceNumber == 2) ? 6 : faceNumber - 1; };
   
   // tell which region a vertex is in
   static bool IsNorthPoleVertex(vertex_t vertexNumber)
      { return (vertexNumber <= 4);};
   static bool IsNorthEquatorVertex(vertex_t vertexNumber)
      { return (vertexNumber > 4 && vertexNumber <= 9);};
   static bool IsSouthEquatorVertex(vertex_t vertexNumber)
      { return (vertexNumber > 9 && vertexNumber <= 14);};
   static bool IsSouthPoleVertex(vertex_t vertexNumber)
      { return (vertexNumber > 14);};
      
   // tell which region a face is in
   static bool IsNorthPoleFace(face_t faceNumber)
      { return (faceNumber == 1); };
   static bool IsNorthEquatorFace(face_t faceNumber)
      { return (faceNumber > 1 && faceNumber <= 6); };
   static bool IsSouthEquatorFace(face_t faceNumber)
      { return (faceNumber > 6 && faceNumber <= 11); };
   static bool IsSouthPoleFace(face_t faceNumber)
      { return (faceNumber == 12); };
      
   // find non-adjacent South Equatorial faces holding
   // these vertices. This is useful for lofting because
   // we can rotate these two faces independently without
   // affected the other face's vertex.
   // The vertices v1 and v2 can be on the South Equator,
   // the South Pole, or a mixture; except that they cannot
   // be on the same vertical line (because they touch the
   // same faces then); the caller must detect this and not
   // call this routine in this case.
   static void FindNonAdjacentSouthFaces(vertex_t v1, 
            vertex_t v2, face_t *pf1, face_t *pf2);
      
   // write a comment line to the rotations file telling
   // what we are doing (debug only)
   void WriteComment(const char *pComment);
   
   // check whether an edge has the given colors (in either order)
   bool EdgeHasColors(face_t f0, face_t f1, color_t c0, 
            color_t c1);
      

private:

   /////////////////////////////////////////////////////////////////
   // used in implementation
   
   void SliceImp(face_t faceNumber, Direction direction);
            // slice one turn

   // utility for rotating part of a face
   static void RotateFacelets(short **ppColor, 
            Direction direction);

   

   /////////////////////////////////////////////////////////////////
   // member variables
   
   // colors for edge and corner facelets
   // colors are numbered 1 through 6; we use 0 for an invalid entry.
   //
   // indices are the face number (and so run from 1 through 12).
   // edge facelets are indexed by the two faces they touch, with
   // the first index being the face they are on.
   // corner facelets are indexed by the three faces they touch,
   // with the first index being the face they are on,
   // with second index < third index. 
   //
   // We allocate many more items than are actually valid.
   short fEdgeFacelets[13][13];
   short fCornerFacelets[13][13][13];

   // output stream for the answer
   std::ofstream fRotationsStream;

};

// class to temporarily rotate a face; when destructed,
// it rotates back in the other direction. The clicks
// can be 0, meaning no rotation.

class CTempRotate
{
public:
   CTempRotate(CMegaminx& rMega, CMegaminx::face_t faceNumber,
            int clicks, CMegaminx::Direction direction);
            
   ~CTempRotate();
private:
   CMegaminx& fMega;
   CMegaminx::face_t fFaceNumber;
   int fClicks;
   CMegaminx::Direction fDirection;
};

CSolver.h

//////////////////////////////////////////////////////////////////////
//
// This class contains all the algorithms for solving Megaminx.
//
//////////////////////////////////////////////////////////////////////

#pragma once

#include 
#include 
using std::string;

#include "CMegaminx.h"

class CCornerVisitor;
class CMegaminx;

class CSolver
{
public:
   CSolver(CMegaminx& rMega);
   ~CSolver();
   
   // solve the Megaminx and write out the solution
   void Solve();
   
   // call visitor   
   void VisitAllCorners(CCornerVisitor &aVisitor);
   
private:
   /////////////////////////////////////////////////////////////////
   // used in implementation
   
   // double operations from Meffert
   // RUU = R upper star upper star, and so on
   void DoLUU(CMegaminx::face_t leftFace, 
            CMegaminx::face_t rightFace);
   void DoRUU(CMegaminx::face_t leftFace, 
            CMegaminx::face_t rightFace);
   void DoRLL(CMegaminx::face_t leftFace, 
            CMegaminx::face_t rightFace);
   void DoLLL(CMegaminx::face_t leftFace, 
            CMegaminx::face_t rightFace);
   
   // distance along either the North or South pole vertices, measured
   // in the direction of increasing vertex number and wrapping around.
   // We use this when we are going to rotate in this direction.
   // Also: distance along an equator from one face to another.
   // This calculates (to - from) mod 5.
   int Distance(int from, int to)
   { int dist = to - from; if (dist < 0) dist += 5; return dist;};
   
   // this checks that the South half edges are an even permutation
   // of the solved position. It returns true if the permutation
   // is even and false if it is odd.
   bool CheckEdgeParity();
   static int ParityLookup(CMegaminx::color_t c0, 
            CMegaminx::color_t c1);
   
   // solution steps
   void Step3();
   void Step3Edges();
   CMegaminx::face_t Step3_4Drop(CMegaminx::color_t c0, 
                     CMegaminx::color_t c1);
   void Step3Corners();
   void Step4();
   void Step5();
   void Step5PlaceVertex(CMegaminx::vertex_t srcVertex, 
                     int destVertex);
   void Step5OrientVertex(int destVertex);
   void Step6();
   void Step6PlacePoleEdge(CMegaminx::face_t fromSFace, 
                  CMegaminx::face_t toEdgeIndex);
   void Step7();
   void Step7PlacePoleEdge(CMegaminx::face_t srcFaceN, 
                  CMegaminx::face_t destFaceL, 
                  CMegaminx::face_t destFaceR);
   void Step8();
   void Step8ReferenceEdge();
   void Step8SecondEdge();
   void Step8ThirdEdge();
   void Step8RestoreEquator();
   void Step8OrientFourFive();
   void Step9();
   void Step9EquatorAndPole(CMegaminx::vertex_t dst, 
                  CMegaminx::vertex_t src);
   void Step10();
   void Step11();
   void Step11BothEquators(CMegaminx::vertex_t vCounterCW, 
                  CMegaminx::vertex_t vCW);
   
   // verification steps (these run only in debug mode);
   // they check that various things are correctly positioned
   // at the end of step n, and assert if they are not.
   // Each step calls the preceding step, so all earlier
   // verifications are re-performed too.
   void Step3Verify();
   void Step4Verify();
   void Step5Verify();
   void Step6Verify();
   void Step7Verify();
   void Step8Verify();
   void Step9Verify();
   void Step10Verify();
   void Step11Verify();
   

   /////////////////////////////////////////////////////////////////
   // member variables
   CMegaminx& fMega;   // Megaminx being solved
};

// CCornerVisitor Class
class CCornerVisitor
{
public:
   CCornerVisitor() {};
   virtual ~CCornerVisitor() {};
   
   virtual void VisitCorner(int cornerIndex) = 0;
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more

Jobs Board

Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Apr 20, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.