TweetFollow Us on Twitter

Mar 96 Challenge
Volume Number:12
Issue Number:3
Column Tag:Programmer’s Challenge

Programmer’s Challenge

By Bob Boonstra, Westford, Massachusetts

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Words The Reverse

Text input, of Block given a in words of order the place (in reverse) will that routine a write to is challenge the month this. Oops, what I meant to say was: This month, the Challenge is to write a routine that will reverse (in place) the order of words in a given block of input text. The prototype for the code you should write is:

pascal void ReverseTheWords(
 const char *text, /* the words you should reverse */
 const long numCharsIn    /* length of inputText in chars */
);

Specifically, ReverseTheWords should exchange the first word in the input text with the last word, the second word with the next-to-last word, etc. For the purpose of this Challenge, a word is defined as a continuous sequence of alphanumeric characters [a..zA..Z0..9]. Any nonalphanumeric characters should remain in their original positions and in their original order with respect to the new words; that is, punctuation, white space, and other characters between the first and second input words should, on output, be located between the new first and second words. As an example, ReverseTheWords would convert:

 This, however, <-is-> a (difficult) test.

into

 Test, difficult, <-a-> is (however) this.

As you can see from the example, there is one additional requirement. Your code needs to adjust the capitalization of words so that the n-th word is capitalized on output only if the n-th word was capitalized in the input text.

There are no specific restrictions on the amount of auxiliary memory you may use (within reason), so you may allocate a few buffers of size numCharsIn should you need them. Remember, however, to deallocate any memory you allocate before returning, as I will be calling your code many times.

Note that the prototype specifies the use of Pascal calling conventions. That is because this month we are conducting

A Language Experiment

Over the past months, in response to suggestions from readers, we have made a number of changes to the Challenge, including migrating to PowerPC native code and expanding to other C compilers. Now we are experimenting with some additional changes. This month, for the first time, your solution to the Challenge can be coded in C, C++, or Pascal, using your choice among the MPW, Metrowerks, or Symantec compilers for these languages. Although either 68K or PowerPC code is permitted, I will be running your code on a PowerMac 8500, so native code is obviously recommended. The environment you choose must support linking your solution with test code written in C. Along with your solution, you should specify the intended environment, compiler and compiler options, or (better yet) provide a project file or make file that will generate a stand-alone application that calls your solution from C test code.

Two Months Ago Winner

Congratulations to Jorg “jbx” Brown and Eric Gundrum for submitting the fastest entry to the Sliding Tiles Challenge. Contestants were asked to unscramble an N¥M grid of interlocking tiles, where a tile could be moved horizontally or vertically into the empty tile location. Fourteen of the 16 entries submitted worked correctly. The table below presents selected results from the timing tests, which used puzzles ranging in size from 8¥8 to 100¥300. (Two of the entries worked too slowly to complete the entire test suite.)

The most common solution technique involved solving the puzzle row-by-row from the bottom, being careful not to disturb tiles that had already been moved into their correct positions. The top two rows were solved together, starting from the right column and working left. Several solutions, including the winners’ and the even more move-efficient second-place entry by Peter Lewis, refined this by using a “greedy” algorithm that solves the bottom row and right column first, then moves to the next row and column. Since the problem involved use of a given callback routine for each move, it was important to reduce the number of tile moves. Some solutions moved tiles primarily in horizontal and vertical directions; others (including the winners’) devised operators that moved a tile diagonally in fewer moves.

Thanks to Greg Linden for sending me a reference to an article on this subject in Information Processing Letters (Oct. 1995). In that article, Ian Parberry (University of North Texas) establishes some bounds on algorithms for the n2-1 puzzle (the square version of this Challenge). The article proves that the “greedy” algorithm requires at most 5n3 moves in the worst case, and that any algorithm must make at least n3 moves (ignoring lower-order terms for both bounds). While a better algorithm may exist, Ian speculates that the lower bound could be made tighter. Interested readers can contact the author at ian@cs.unt.edu, or browse his web page at http://hercule.csci.unt.edu/ian.

Here are the times for each of the correct entries. Numbers in parentheses after a person’s name indicate that person’s cumulative point total for all previous Challenges, not including this one.

Time Time Total # of Moves

Name 20x20 100x300 Time (Millions)

Jorg Brown (10)

& Eric Gundrum 183 175730 261694 249

Peter Lewis 195 197905 294931 228

Christopher Phillips 211 200368 302774 269

Ludovic Nicolle 252 220952 337386 300

Ernst Munter (110) 159 258165 365894 295

Randy Boring 386 309630 481609 307

Cathy Saxton 264 338127 496610 299

Rishi Khan (age 16) 284 350763 506952 320

Xan Gregg (88) 296 360735 531493 299

Tom Saxton (10) 333 404155 592967 339

Miguel Cruz Picão(7) 245 652588 912267 339

John Sweeney (4) 300 875362 1223956 321

A. K. * * * *

G. L. * * * *

Top Contestants Of All Time

Here are the Top Contestants for the Programmer’s Challenges to date, including everyone who has accumulated more than 20 points. The numbers below include points awarded for this month’s entrants.

Rank Name Points Rank Name Points

1. [Name deleted] 176 11. Mallett, Jeff 44

2. Munter, Ernst 112 12. Kasparian, Raffi 42

3. Gregg, Xan 88 13. Vineyard, Jeremy 42

4. Larsson, Gustav 87 14. Lengyel, Eric 40

5. Karsh, Bill 80 15. Darrah, Dave 31

6. Stenger, Allen 65 16. Brown, Jorg 30

7. Riha, Stepan 51 17. Landry, Larry 29

8. Cutts, Kevin 50 18. Elwertowski, Tom 24

9. Goebel, James 49 19. Lee, Johnny 22

10. Nepsund, Ronald 47 20. Noll, Robert 22

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 5th place 2 points

2nd place 10 points finding bug 2 points

3rd place 7 points suggesting Challenge 2 points

4th place 4 points

Here the winning solution by Jorg and Eric:

SlidingTiles.c

Written by Jorg Brown & Eric Gundrum

Thanks also to Brad Kollmyer

typedef Boolean (*MoveProc)(
  long tileToMoveRow, long tileToMoveCol);

static MoveProc gMakeMove;
static long *gTiles, gNumCols, *gPieceRow, *gPieceCol;
static long *gBlankSquare; // initialized by SolveTiles, updated by BlankXXX()

#define gBlankRow pieceRow[0]
#define gBlankCol pieceCol[0]

BlankUp
static void BlankUp() { // move the BLANK SQUARE up
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;

   oldBlankSquare = gBlankSquare;
   gBlankSquare = newBlankSquare = oldBlankSquare - gNumCols;
   tile = *newBlankSquare;
   pieceRow[tile] = gBlankRow--;
   gMakeMove(gBlankRow, gBlankCol);
   *oldBlankSquare = tile;
   *newBlankSquare = 0;
}

BlankDown
static void BlankDown() { // move the BLANK SQUARE down
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;

   oldBlankSquare = gBlankSquare;
   gBlankSquare = newBlankSquare = oldBlankSquare + gNumCols;
   tile = *newBlankSquare;
   pieceRow[tile] = gBlankRow++;
   gMakeMove(gBlankRow, gBlankCol);
   *oldBlankSquare = tile;
   *newBlankSquare = 0;
}

MoveBlankToRow
static void MoveBlankToRow(long destRow) {
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;
   long   blankRow = gBlankRow;
   long   rowInc, BlankSquareInc;

   if (blankRow < destRow) { // move DOWN
      rowInc = 1;
      BlankSquareInc = gNumCols;
   } else if (blankRow > destRow) { // move UP
      rowInc = -1;
      BlankSquareInc = -gNumCols;
   } else return;

   oldBlankSquare = gBlankSquare;
   do {
      newBlankSquare = oldBlankSquare + BlankSquareInc;
      tile = *newBlankSquare;
      pieceRow[tile] = blankRow;
      blankRow += rowInc;
      gMakeMove(blankRow, gBlankCol);
      *oldBlankSquare = tile;
      *newBlankSquare = 0;
      oldBlankSquare = newBlankSquare;
   } while (blankRow != destRow);
   gBlankSquare = newBlankSquare;
   gBlankRow = blankRow;
}

BlankLeft
static void BlankLeft() { // move the BLANK SQUARE left
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;

   oldBlankSquare = gBlankSquare;
   gBlankSquare = newBlankSquare = oldBlankSquare - 1;
   tile = *newBlankSquare;
   pieceCol[tile] = gBlankCol--;
   gMakeMove(gBlankRow, gBlankCol);
   *oldBlankSquare = tile;
   *newBlankSquare = 0;
}


BlankRight
static void BlankRight() { // move the BLANK SQUARE right
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;

   oldBlankSquare = gBlankSquare;
   gBlankSquare = newBlankSquare = oldBlankSquare + 1;
   tile = *newBlankSquare;
   pieceCol[tile] = gBlankCol++;
   gMakeMove(gBlankRow, gBlankCol);
   *oldBlankSquare = tile;
   *newBlankSquare = 0;
}

MoveBlankToCol
static void MoveBlankToCol(long destCol) {
   long   tile, *oldBlankSquare, *newBlankSquare;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;
   long   blankCol = gBlankCol;
   long   inc;

   if (blankCol < destCol) { // move RIGHT
      inc = 1;
   } else if (blankCol > destCol) { // move LEFT
      inc = -1;
   } else return;

   oldBlankSquare = gBlankSquare;
   do {
      newBlankSquare = oldBlankSquare + inc;
      tile = *newBlankSquare;
      pieceCol[tile] = blankCol;
      blankCol += inc;
      gMakeMove(gBlankRow, blankCol);
      *oldBlankSquare = tile;
      *newBlankSquare = 0;
      oldBlankSquare = newBlankSquare;
   } while (blankCol != destCol);
   gBlankSquare = newBlankSquare;
   gBlankCol = blankCol;
}

MoveAPiece

static void MoveAPiece(long piece, long destRow, long destCol, 
 long nextPiece) {
   long   sourceRow, sourceCol;
   long   *pieceRow = gPieceRow;
   long   *pieceCol = gPieceCol;
   long   nextRow = gNumCols;

   sourceRow = pieceRow[piece];
   sourceCol = pieceCol[piece];

   if (sourceRow >= destRow) { 
      // in this case, we have to move the tile up (or directly right) to get to its destination
      if (sourceRow == destRow) {
         if (sourceCol == destCol) return;

         // simplify: move the blank so that it is not to the right of the target
         if (gBlankCol > destCol) {
            MoveBlankToCol(destCol);
         }

         // move the blank to the left of the source, possibly moving the source
         // to the right at the same time.
         if (gBlankRow != destRow) {
            if (sourceCol == destCol-1 && gBlankRow > destRow) {
               MoveBlankToCol(sourceCol - 1);
               MoveBlankToRow(sourceRow - 1);
               BlankRight(); BlankRight();
               BlankDown();  BlankLeft();
               return; // all done
            } else {
               MoveBlankToCol(sourceCol + 1);
               MoveBlankToRow(sourceRow);
               BlankLeft();
               sourceCol++;
            }
         } else {
            if (gBlankCol < sourceCol) {
               MoveBlankToCol(sourceCol - 1);
            } else {
               MoveBlankToCol(sourceCol);
               sourceCol++;
            }
         }
         
WereOnTheSameRowNow:
         // at this point, the blank is to the left of the source,
         // and the puzzle might very well be done already.
         while (sourceCol != destCol) {
            if (nextPiece == piece - 1) { // into a row?
               if (gBlankCol != 0 && 
                     pieceCol[nextPiece]-pieceRow[nextPiece] <= 
                     gBlankCol-gBlankRow) {
                  MoveAPiece(nextPiece, gBlankRow, gBlankCol, 
                               nextPiece - 1);
               }
               if (gBlankRow == destRow) 
                 while ((gBlankSquare[-1] == gBlankSquare[1] - 1) 
                        && gBlankCol != 0) {
                  BlankLeft();
               }
            }
            if (gBlankRow == destRow) BlankUp();
            do {
               BlankRight();
            } while (gBlankCol <= sourceCol);
            BlankDown();  BlankLeft();  sourceCol++;
         }
         return;
      }
      // simplify: move the blank so that it is to the left of the target
      if (gBlankCol >= destCol) {
         MoveBlankToCol(destCol - 1);
      }
      // simplify: move the blank so that it is not above the target.
      if (gBlankRow < destRow) MoveBlankToRow(destRow);

again1:   // simplify: if the blank is below the source, move it so it’s not.
      sourceRow = pieceRow[piece];
      sourceCol = pieceCol[piece];
      if (gBlankRow >= sourceRow) {
         // if the blank is in the same column, move it away first.
         if (gBlankCol == sourceCol) {
            if (gBlankCol == destCol - 1) {
               BlankLeft();
            } else {
               BlankRight();
            }
         }
         // now that they're in different columns, move the blank so it’s not below
         MoveBlankToRow(sourceRow);
      }
      // simplify: if the blank is on the same row, and to the left, move up.
      if (gBlankRow == sourceRow && gBlankCol < sourceCol) {
         BlankUp();
      }
      // simplify: if the blank is to the left, move it to the same column.
      if (gBlankCol < sourceCol) {
         MoveBlankToCol(sourceCol);
      }
      // simplify: if the blank is to the upper right, move it to the left and down.
      if (gBlankRow < sourceRow) {
         if (gBlankCol > sourceCol) {
            MoveBlankToCol(sourceCol);
         }
         if (gBlankRow < sourceRow - 1) {
            MoveBlankToRow(sourceRow - 1);
         }
      }
      // if the blank is off to the right, move it next to the source.
      while (gBlankCol > sourceCol + 1) {
         MoveBlankToCol(sourceCol + 1);
      }
      // at this point, the blank should be either just above or just right of the piece.
      if (gBlankCol == sourceCol) {
         if (gBlankRow != sourceRow - 1) Debugger();
      } else {
         if (gBlankRow != sourceRow) Debugger();
         if (gBlankCol != sourceCol + 1) Debugger();
         BlankLeft();  BlankUp();    BlankRight();
      }
      BlankDown();
      sourceRow = pieceRow[piece];
      sourceCol = pieceCol[piece];
      if (sourceRow != destRow) goto again1;
      if (gBlankCol != destCol - 1) {
         BlankRight(); BlankUp();    BlankLeft();
         while (pieceCol[piece] != destCol) {
            BlankUp();    BlankRight(); BlankRight();
            BlankDown();  BlankLeft();
         }
         return; // DONE!!!!
      }
      BlankLeft();  BlankUp();    BlankUp();    BlankRight(); 
      BlankRight(); BlankDown();  BlankLeft();
      return; // DONE!!!!
   }

   // at this point, we know that source is above our destination.
   if (sourceCol >= destCol) { 
      // in this case, we have to move the tile left (or directly down) to get to its destination
      if (sourceCol == destCol) {

         // simplify: move the blank so that it is not below the target
         if (gBlankRow > destRow) {
            MoveBlankToRow(destRow);
         }

         // move the blank above the source, possibly moving the source
         // down at the same time.
         if (gBlankCol != destCol) {
            if (sourceRow == destRow-1 && gBlankCol > destCol) {
               MoveBlankToRow(sourceRow - 1);
               MoveBlankToCol(sourceCol - 1);
               BlankDown();  BlankDown();
               BlankRight(); BlankUp();
               return; // all done
            } else {
               MoveBlankToRow(sourceRow + 1);
               MoveBlankToCol(sourceCol);
               BlankUp();
               sourceRow++;
            }
         } else {
            if (gBlankRow < sourceRow) {
               MoveBlankToRow(sourceRow - 1);
            } else {
               MoveBlankToRow(sourceRow);
               sourceRow++;
            }
         }
         
WereInTheSameColumnNow:
         // at this point, the blank is on top of the source,
         // and the puzzle might very well be done already.
         while (sourceRow != destRow) {
            if (nextPiece == piece - nextRow) { // into a column?
               if (gBlankRow != 0 && 
                   pieceCol[nextPiece]-pieceRow[nextPiece] >= 
                     gBlankCol-gBlankRow) {
                  MoveAPiece(nextPiece, gBlankRow, gBlankCol, 
                               nextPiece - nextRow);
               }
               if (gBlankCol == destCol) 
                 while ((gBlankSquare[-nextRow] == 
                         gBlankSquare[nextRow] - 1) && 
                        gBlankRow != 0) {
                  BlankUp();
               }
            }
            if (gBlankCol == destCol) BlankLeft();
            do {
               BlankDown();
            } while (gBlankRow <= sourceRow);
            BlankRight(); BlankUp();    sourceRow++;
         }
         return;
      }
      // simplify: move the blank so that it is to the up of the target
      if (gBlankRow >= destRow) {
         MoveBlankToRow(destRow - 1);
      }
      // simplify: move the blank so that it is not to the left of the target.
      if (gBlankCol < destCol) MoveBlankToCol(destCol);

again2:   // simplify: if the blank is to the right of the source, move it so it’s not.
      sourceRow = pieceRow[piece];
      sourceCol = pieceCol[piece];
      if (gBlankCol >= sourceCol) {
         // if the blank is in the same row, move it away first.
         if (gBlankRow == sourceRow) {
            if (gBlankRow == destRow - 1) {
               BlankUp();
            } else {
               BlankDown();
            }
         }
         // now that they’re in different rows, move the blank so it’s not to the right of
         MoveBlankToCol(sourceCol);
      }
      // simplify: if the blank is on the same column, and to the up, move left.
      if (gBlankCol == sourceCol && gBlankRow < sourceRow) {
         BlankLeft();
      }
      // simplify: if the blank is to the up, move it to the same row.
      if (gBlankRow < sourceRow) {
         MoveBlankToRow(sourceRow);
      }
      // simplify: if the blank is to the lower left, move it to the right and up.
      if (gBlankCol < sourceCol) {
         if (gBlankRow > sourceRow) {
            MoveBlankToRow(sourceRow);
         }
         if (gBlankCol < sourceCol - 1) {
            MoveBlankToCol(sourceCol - 1);
         }
      }
      // if the blank is off to the down, move it next to the source.
      while (gBlankRow > sourceRow + 1) {
         MoveBlankToRow(sourceRow + 1);
      }
      // at this point, the blank should be either just below or just to the left of the piece.
      if (gBlankRow == sourceRow) {
         if (gBlankCol != sourceCol - 1) Debugger();
      } else {
         if (gBlankCol != sourceCol) Debugger();
         if (gBlankRow != sourceRow + 1) Debugger();
         BlankUp();    BlankLeft();  BlankDown();
      }
      BlankRight();
      sourceRow = pieceRow[piece];
      sourceCol = pieceCol[piece];
      if (sourceCol != destCol) goto again2;
      if (gBlankRow != destRow - 1) {
         BlankDown();  BlankLeft();  BlankUp();
         while (pieceRow[piece] != destRow) {
            BlankLeft();  BlankDown();
            BlankDown();  BlankRight(); BlankUp();
         }
         return; // DONE!!!!
      }
      BlankUp();    BlankLeft();  BlankLeft();  BlankDown();
      BlankDown();  BlankRight(); BlankUp();
      return; // DONE!!!!
   }

   // at this point, we know that we are above and to the left of our destination.

   if (destCol - sourceCol == destRow - sourceRow) {
      // we’re on the diagonal.
      if (gBlankCol >= sourceCol) {
         if (gBlankRow <= sourceRow) goto MoveSrcRightFirst;
      }
      if (gBlankRow >= sourceRow) {
         if (gBlankCol <= sourceCol) goto MoveSrcDownFirst;
      }
      if (gPieceRow[nextPiece] - gPieceCol[nextPiece] > 
          destRow - destCol) {
         // relative to the destination, the next piece is to the lower left.
         // this means we want the blank to end up to the left of the target,
         // rather than above it.
         goto MoveSrcDownFirst;
      }
      goto MoveSrcRightFirst;
   }
   if (destCol - sourceCol < destRow - sourceRow) {
MoveSrcDownFirst:
      // the source is within the 90š-135š octant.
      // we want to move the blank square just below the source.
      // we will end up just to the left of the target.
      if (gBlankCol == sourceCol && gBlankRow < sourceRow) {
         // the blank is on top of the source.  moving it
         // down would move our square in the wrong direction.
         BlankRight();
      }
      // in case the source is just to the upper left of the target,
      // we have to make sure we don’t accidentally munge the protected area.
      if (gBlankCol > destCol) MoveBlankToCol(destCol);
      MoveBlankToRow(sourceRow + 1);
      MoveBlankToCol(sourceCol);
      BlankUp();    sourceRow++;
      BlankRight(); BlankDown();
      // the blank is now to the right of the source.
   } else {
MoveSrcRightFirst:
      // the source is within the 135š-180š octant.
      // we want to move the blank square just to the right of the source.
      // we will end up just above the target.
      if (gBlankRow == sourceRow && gBlankCol < sourceCol) {
         // the blank is to the left of the source.  moving it
         // to the right would move our square in the wrong direction.
         BlankDown();
      }
      // in case the source is just to the upper left of the target,
      // we have to make sure we don't accidentally munge the protected area.
      if (gBlankRow > destRow) MoveBlankToRow(destRow);
      MoveBlankToCol(sourceCol + 1);
      MoveBlankToRow(sourceRow);
      // the blank is now to the right of the source.
   }
   BlankLeft();
   sourceCol++;
   // the blank is now to the left of the source.
   // are we done yet?
   for (;;) {
      if (sourceCol == destCol) {
         if (sourceRow == destRow) return;
         // the blank is still to the left of the source.
         BlankDown();  BlankRight(); BlankUp();   sourceRow++;
         goto WereInTheSameColumnNow;
      }
      if (sourceRow == destRow) {
         goto WereOnTheSameRowNow;
      }
      BlankDown();  BlankRight(); BlankUp();    sourceRow++;
      BlankRight(); BlankDown();  BlankLeft();  sourceCol++;
   }
}

QuickBlock
static void *QuickBlock(long size) {
   Handle   h = NewHandle(size);
   if (h == 0) return 0;
   HLock(h);
   return *h;
}

DisposeBlock
static void DisposeBlock(void *block) {
   Handle   h = RecoverHandle(block);
   DisposeHandle(h);
}


SolveTiles
void SolveTiles(
  long *tiles,      /* pointer to array of tiles where */
  long numRows,     /*   tile (row,col) is at */
  long numCols,     /*   *(tiles + row*numCols + col) */
  MoveProc MakeMove /* Callback procedure to move a tile */
) {
   long   col, row, target, tile, correctTile;
   long   colsToGo, rowsToGo;
   long   *tileRover, *pieceRow, *pieceCol;

   pieceRow = QuickBlock(numRows * numCols * sizeof(long));
   if (pieceRow == 0) return;
   pieceCol = QuickBlock(numRows * numCols * sizeof(long));
   if (pieceCol == 0) {
      DisposeBlock(pieceRow);
      return;
   }
   gPieceRow = pieceRow;
   gPieceCol = pieceCol;
   gMakeMove = MakeMove;
   gTiles = tiles;
   gNumCols = numCols;

   tileRover = tiles;
   correctTile = 0;
   for (row = 0; row < numRows; row++) {
      for (col = 0; col < numCols; col++) {
         tile = *tileRover++;
         pieceRow[tile] = row;
         pieceCol[tile] = col;
         correctTile++;
      }
   }

   gBlankSquare = &tiles[gBlankRow * numCols + gBlankCol];

   rowsToGo = numRows;
   colsToGo = numCols;

   for (;;) {
      if (rowsToGo >= colsToGo) {
         if (rowsToGo <= 2) break;
         row = rowsToGo - 1;
         for (col = colsToGo - 1; col > 1; col--) {
            tile = row * numCols + col;
            MoveAPiece(tile, row, col, tile - 1);
         }
         tile = row * numCols;
         if (pieceRow[tile]     != row || 
             pieceCol[tile    ] != 0   ||
             pieceRow[tile + 1] != row || 
             pieceCol[tile + 1] != 1) {
            MoveAPiece(tile, row, 1, tile + 1);
            if (gBlankRow == row) {
               if (tiles[(row - 1) * numCols] == tile + 1) {
                  // problem scenario 1
rowScenario1:
                  BlankRight(); BlankUp();    BlankLeft();
                  BlankUp();    BlankRight(); BlankDown();
                  BlankDown();  BlankLeft();  BlankUp();
                  BlankRight(); BlankUp();    BlankLeft();
                  BlankDown();  BlankDown();  BlankRight();
                  BlankUp();
                  goto nextRow;
               }
            } else if (tiles[row * numCols] == tile + 1) {
               // problem scenario 2
               MoveBlankToCol(0);
               MoveBlankToRow(row);
               goto rowScenario1;
            }
            MoveAPiece(tile + 1, row, 1, 0);
         }
nextRow:   if (gBlankRow == row) BlankUp();
         if (pieceRow[tile]     != row || 
             pieceCol[tile    ] != 0) {
            // the “12” isn’t in place...
            Debugger();
         }
      rowsToGo--;
      } else {
         if (colsToGo <= 2) break;
         col = colsToGo - 1;
         for (row = rowsToGo - 1; row > 1; row--) {
            tile = row * numCols + col;
            MoveAPiece(tile, row, col, tile - numCols);
         }
         if (pieceRow[          col] != 0 || 
             pieceCol[          col] != col ||
             pieceRow[numCols + col] != 1 || 
             pieceCol[numCols + col] != col) {
            MoveAPiece(col, 1, col, col + numCols);
            if (gBlankCol == col) {
               if (tiles[col - 1] == numCols + col) {
                  // problem scenario 1
colScenario1:
                  BlankDown();  BlankLeft();  BlankUp();
                  BlankLeft();  BlankDown();  BlankRight();
                  BlankRight(); BlankUp();    BlankLeft();
                  BlankDown();  BlankLeft();  BlankUp();
                  BlankRight(); BlankRight(); BlankDown();
                  BlankLeft();
                  goto nextCol;
               }
            } else if (tiles[col] == numCols + col) {
               // problem scenario 2
               MoveBlankToRow(0);
               MoveBlankToCol(col);
               goto colScenario1;
            }
            MoveAPiece(numCols + col, 1, col, 0);
         }
nextCol:   if (gBlankCol == col) BlankLeft();
         if (pieceRow[          col] != 0 || 
             pieceCol[          col] != col) {
            // the “3” isn’t in place...
            Debugger();
         }
         colsToGo--;
      }
   }

   if (gBlankRow == 0) {
      if (gBlankCol == 0) {
         if (pieceRow[1] == 0) goto pos0123;
         if (pieceCol[1] == 0) goto pos0312;
                               goto pos0231;
      } else {
         if (pieceRow[1] == 0) goto pos1023;
         if (pieceCol[1] == 0) goto pos3012;
                               goto pos2031;
      }
   } else {
      if (gBlankCol == 0) {
         if (pieceRow[1] != 0) goto pos3201;
         if (pieceCol[1] == 0) goto pos1302;
                               goto pos2103;
      } else {
         if (pieceRow[1] != 0) goto pos3210;
         if (pieceCol[1] == 0) goto pos1320;
                               goto pos2130;
      }
   }

pos3012: BlankLeft();
pos0312: BlankDown();
pos1302: BlankRight();
pos1320: BlankUp();
pos1023: BlankLeft();
         goto all_done;

pos3210: BlankLeft();
pos3201: BlankUp();
pos0231: BlankRight();
pos2031: BlankDown();
pos2130: BlankLeft();
pos2103: BlankUp();
pos0123: goto all_done;

all_done:
   DisposeBlock(pieceRow); gPieceRow = 0;
   DisposeBlock(pieceCol); gPieceCol = 0;
}

 
AAPL
$98.91
Apple Inc.
+1.24
MSFT
$44.22
Microsoft Corpora
-0.29
GOOG
$591.51
Google Inc.
+2.49

MacTech Search:
Community Search:

Software Updates via MacUpdate

Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more
TotalFinder 1.6.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Tab-based... Read more
Vienna 3.0.0 RC 2 :be5265e: - RSS and At...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
VLC Media Player 2.1.5 - Popular multime...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more
Default Folder X 4.6.7 - Enhances Open a...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more
TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more
Autopano Giga 3.6 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
Airfoil 4.8.7 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more

Latest Forum Discussions

See All

The Phantom PI Mission Apparition Review
The Phantom PI Mission Apparition Review By Jordan Minor on July 28th, 2014 Our Rating: :: GHOSTS BUSTEDUniversal App - Designed for iPhone and iPad The Phantom PI is an exceedingly clever and well-crafted adventure game.   | Read more »
More Stubies Are Coming Your Way in a Ne...
More Stubies Are Coming Your Way in a New Update Posted by Jessica Fisher on July 28th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
The Great Prank War Review
The Great Prank War Review By Nadia Oxford on July 28th, 2014 Our Rating: :: PRANKING IS SERIOUS BUSINESSUniversal App - Designed for iPhone and iPad Though short, The Great Prank War offers an interesting and fun mix of action and... | Read more »
Marvel Contest of Champions Announced at...
Marvel Contest of Champions Announced at Comic-Con Posted by Jennifer Allen on July 28th, 2014 [ permalink ] Announced over the weekend at San Diego Comic-Con was the fairly exciting looking Marvel Contest of Champions. | Read more »
Teenage Mutant Ninja Turtles Review
Teenage Mutant Ninja Turtles Review By Jennifer Allen on July 28th, 2014 Our Rating: :: DULL SWIPINGUniversal App - Designed for iPhone and iPad The pizza power is weak when it comes to this Teenage Mutant Ninja Turtles game.   | Read more »
Exploration Focused Puzzle Game Beatbudd...
Exploration Focused Puzzle Game Beatbuddy Set to Make Transition from PC to iOS this September Posted by Jennifer Allen on July 28th, 2014 [ permalink ] | Read more »
PlanetHD
PlanetHD By Nadia Oxford on July 28th, 2014 Our Rating: :: SPACE MADNESSUniversal App - Designed for iPhone and iPad PlanetHD will keep players busy for a while, though its unpredictable physics are a handful to deal with.   | Read more »
This Week at 148Apps: July 21-25, 2014
Another Week of Expert App Reviews   At 148Apps, we help you sort through the great ocean of apps to find the ones we think you’ll like and the ones you’ll need. Our top picks become Editor’s Choice, our stamp of approval for apps with that little... | Read more »
Reddme for iPhone - The Reddit Client (...
Reddme for iPhone - The Reddit Client 1.0 Device: iOS iPhone Category: News Price: $.99, Version: 1.0 (iTunes) Description: Reddme for iPhone is an iOS 7-optimized Reddit client that offers a refreshing new way to experience Reddit... | Read more »
Jacob Jones and the Bigfoot Mystery : Ep...
Jacob Jones and the Bigfoot Mystery : Episode 2 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Jacob Jones is back in Episode 2 of one of Apples 'Best of 2013' games and an App Store... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro on sale for $1099,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $1099.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Roundup of Apple refurbished MacBook Pros, th...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available for up to $400 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Their prices... Read more
Record Mac Shipments In Q2/14 Confound Analys...
A Seeking Alpha Trefis commentary notes that Apple’s fiscal Q3 2014 results released July 22, beat market predictions on earnings, although revenues were slightly lower than anticipated. Apple’s Mac’... Read more
Intel To Launch Core M Silicon For Use In Not...
Digitimes’ Monica Chen and Joseph Tsai, report that Intel will launch 14nm-based Core M series processors specifically for use in fanless notebook/tablet 2-in-1 models in Q4 2014, with many models to... Read more
Apple’s 2014 Back to School promotion: $100 g...
 Apple’s 2014 Back to School promotion includes a free $100 App Store Gift Card with the purchase of any new Mac (Mac mini excluded), or a $50 Gift Card with the purchase of an iPad or iPhone,... Read more
iMacs on sale for $150 off MSRP, $250 off for...
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
Mac minis on sale for $100 off MSRP, starting...
Best Buy has Mac minis on sale for $100 off MSRP. Choose free shipping or free instant local store pickup. Prices are for online orders only, in-store prices may vary: 2.5GHz Mac mini: $499.99 2.3GHz... Read more
Global Tablet Market Grows 11% in Q2/14 Notwi...
Worldwide tablet sales grew 11.0 percent year over year in the second quarter of 2014, with shipments reaching 49.3 million units according to preliminary data from the International Data Corporation... Read more
New iPhone 6 Models to Have Staggered Release...
Digitimes’ Cage Chao and Steve Shen report that according to unnamed sources in Apple’s upstream iPhone supply chain, the new 5.5-inch iPhone will be released several months later than the new 4.7-... Read more
New iOS App Helps People Feel Good About thei...
Mobile shoppers looking for big savings at their favorite stores can turn to the Goodshop app, a new iOS app with the latest coupons and deals at more than 5,000 online stores. In addition to being a... Read more

Jobs Board

*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
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring 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* 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
WW Sales Program Manager, *Apple* Online St...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.