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;
};

 
AAPL
$524.94
Apple Inc.
+5.93
MSFT
$40.01
Microsoft Corpora
-0.39
GOOG
$536.10
Google Inc.
-20.44

MacTech Search:
Community Search:

Software Updates via MacUpdate

Tweetbot 1.5.1 - Popular iOS twitter cli...
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Mac DVDRipper Pro 4.1.7 - Copy, backup,...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more
PDFpenPro 6.2 - Advanced PDF toolkit for...
PDFpenPro allows users to edit PDF's easily. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Create... Read more
PDFpen 6.2 - Edit and annotate PDFs with...
PDFpen allows users to easily edit PDF's. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Features... Read more
Monolingual 1.5.9 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more
Maya 2015 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
Starcraft II: Wings of Liberty 1.1.1.180...
Download the patch by launching the Starcraft II game and downloading it through the Battle.net connection within the app. Starcraft II: Wings of Liberty is a strategy game played in real-time. You... Read more
Sibelius 7.5.0 - Music notation solution...
Sibelius is the world's best-selling music notation software for Mac. It is as intuitive to use as a pen, yet so powerful that it does most things in less than the blink of an eye. The demo includes... Read more
Typinator 5.9 - Speedy and reliable text...
Typinator turbo-charges your typing productivity. Type a little. Typinator does the rest. We've all faced projects that require repetitive typing tasks. With Typinator, you can store commonly used... Read more
MYStuff Pro 2.0.16 - Create inventories...
MYStuff Pro is the most flexible way to create detail-rich inventories for your home or small business. Add items to MYStuff by dragging and dropping existing information, uploading new images, or... Read more

Latest Forum Discussions

See All

Have a Special Dead Trigger 2 Easter Bas...
Have a Special Dead Trigger 2 Easter Basket Full of Goodies, Courtesy of Madfinger Games Posted by Rob Rich on April 18th, 2014 [ permalink ] Dead Trigger 2 | Read more »
Zynga Launches Brand New Farmville Exper...
Zynga Launches Brand New Farmville Experience with Farmville 2: Country Escape Posted by Tre Lawrence on April 18th, 2014 [ permalink ] | Read more »
David. Review
David. Review By Cata Modorcea on April 18th, 2014 Our Rating: :: MINIMALISTIC IN A DIFFERENT WAYUniversal App - Designed for iPhone and iPad David is a minimalistic game wrapped inside of a soothing atmosphere in which the hero... | Read more »
Eyefi Unveils New Eyefi Cloud Service Th...
Eyefi Unveils New Eyefi Cloud Service That Allows Users to Share Media Across Personal Devices Posted by Tre Lawrence on April 18th, 2014 [ permalink ] | Read more »
Tales from the Dragon Mountain: The Lair...
Tales from the Dragon Mountain: The Lair Review By Jennifer Allen on April 18th, 2014 Our Rating: :: STEADY ADVENTURINGiPad Only App - Designed for the iPad Treading a safe path, Tales from the Dragon Mountain: The Lair is a... | Read more »
Yahoo Updates Flickr App with Advanced E...
Yahoo Updates Flickr App with Advanced Editing Features and More Posted by Tre Lawrence on April 18th, 2014 [ permalink ] | Read more »
My Incredible Body - A Kid's App to...
My Incredible Body - A Kid's App to Learn about the Human Body 1.1.00 Device: iOS Universal Category: Education Price: $2.99, Version: 1.1.00 (iTunes) Description: Wouldn’t it be cool to look inside yourself and see what was going on... | Read more »
Trials Frontier Review
Trials Frontier Review By Carter Dotson on April 18th, 2014 Our Rating: :: A ROUGH LANDINGUniversal App - Designed for iPhone and iPad Trials Frontier finally brings the famed stunt racing franchise to mobile, but how much does its... | Read more »
Evernote Business Notebook by Moleskin I...
Evernote Business Notebook by Moleskin Introduced – Support Available in Evernote for iOS Posted by Tre Lawrence on April 18th, 2014 [ permalink ] | Read more »
Sparkle Unleashed Review
Sparkle Unleashed Review By Jennifer Allen on April 18th, 2014 Our Rating: :: CLASSY MARBLE FLINGINGUniversal App - Designed for iPhone and iPad It’s a concept we’ve seen before, but Sparkle Unleashed is a solidly enjoyable orb... | Read more »

Price Scanner via MacPrices.net

iMacs on sale for up to $160 off MSRP this we...
Best Buy has iMacs on sale for up to $160 off MSRP for a limited time. Choose free home shipping or free instant local store pickup (if available). Prices are valid for online orders only, in-store... Read more
iPad Airs on sale this weekend for up to $100...
Best Buy has WiFi iPad Airs on sale for $50 off MSRP and WiFi + Cellular iPad Airs on sale for $100 off MSRP on their online store for a limited time, with prices now starting at $449. Choose free... Read more
Apple restocks refurbished Mac minis starting...
The Apple Store has restocked Apple Certified Refurbished Mac minis for up to $150 off the cost of new models. Apple’s one-year warranty is included with each mini, and shipping is free: - 2.5GHz Mac... Read more
Hyundai Brings Apple CarPlay To The 2015 Sona...
Hyundai Motor America has announced it will bring Apple CarPlay functionality to the 2015 Sonata. CarPlay is pitched as a smarter, safer and easier way to use iPhone in the car and gives iPhone users... Read more
Updated iPads Coming Sooner Than We Had Thoug...
MacRumors, cites KGI securities analyst Ming Chi Kuo, well-respected as an Apple product prognisticator, saying that Apple will introduce an upgraded iPad Air and iPad mini in 2014/Q3, meaning the... Read more
Toshiba Unveils New High And Low End Laptop M...
Toshiba has announced new laptop models covering both the high-end and low-end of the notebook computer spectrum. Toshiba 4K Ultra HD Laptop Toshiba’s new Satellite P55t features one of the world’s... Read more
Save up to $270 with Apple refurbished 13-inc...
The Apple Store has Apple Certified Refurbished October 2013 13″ Retina MacBook Pros available starting at $1099, with models up to $270 off MSRP. Apple’s one-year warranty is standard, and shipping... Read more
Apple now offering refurbished iPad mini with...
The Apple Store has Certified Refurbished 2nd generation iPad minis with Retina Displays now available starting at $339. Apple’s one-year warranty is included with each model, and shipping is free.... Read more
Microsoft Blinks – Drops Microsoft Office 365...
Microsoft has dropped the annual subscription fee for Microsoft Office 365 Personal – which is needed in order to create and edit documents in Microsoft Office for iPad. However, Apple’s iOS and OS X... Read more
New AVG Vault Apps for iOS and Android Help K...
AVG Technologies N.V. an online security company for 177 million active users, has announced the launch of its latest mobile application, AVG Vault. The free app introduces an innovative user... Read more

Jobs Board

*Apple* Automotive Parts Department position...
Apple Automotive is one of the fastest growing dealer…and it shows. Consider making the switch to the Apple Automotive Group today! At Apple Automotive, we Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Retail - Manager - Holyoke - Apple I...
Job Summary Keeping an Apple Store thriving requires a diverse set of leadership skills, and as a Manager, you’re a master of them all. In the store’s fast-paced, Read more
*Apple* Retail - Manager - Apple (United Sta...
Job SummaryKeeping an Apple Store thriving requires a diverse set of leadership skills, and as a Manager, you're a master of them all. In the store's fast-paced, dynamic Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.