--- /dev/null
+/* Copyright (c) 2006 Scott Gasch
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+//#include <unistd.h>
+
+#include "puzzle.h"
+
+ULONG g_uNodes = 0;
+ULONG g_uGuesses = 0;
+ULONG g_uSolutions = 0;
+POSITION g_Solution;
+
+BOOL
+PositionIsLegal(POSITION *, BOOL);
+
+
+char *
+ShowPossibilities(BITV bv)
+/*++
+
+Routine description:
+
+ Return a text representation of the possible moves at a square.
+
+Parameters:
+
+ BITV bv
+
+Return value:
+
+ char
+
+--*/
+{
+ static char bits[] = "*123456789";
+ static char sz[10];
+ ULONG u;
+ ULONG v;
+
+ memset(&sz, 0, sizeof(sz));
+ v = 0;
+ for (u = 1; u < 10; u++)
+ {
+ if (bv & (1 << (u - 1)))
+ {
+ sz[v++] = bits[u];
+ }
+ }
+ return(sz);
+}
+
+
+
+
+void
+DumpBoard(POSITION *pos)
+/*++
+
+Routine description:
+
+ Draw a text representation of the playing board.
+
+Parameters:
+
+ POSITION *pos : position
+
+Return value:
+
+ void
+
+--*/
+{
+ COOR c;
+
+ FOREACH_SQUARE(c)
+ {
+ if ((c % 9) == 0) printf("\n");
+ if ((c == 27) || (c == 54)) printf("-----------------------------------------------------------------------------\n");
+ if (((c % 3) == 0) && ((c % 9) != 0)) printf(" | ");
+ if (pos->rgSquare[c].uValue)
+ {
+ printf("[%u] ", (unsigned)pos->rgSquare[c].uValue);
+ }
+ else
+ {
+ printf("%-7s ",
+ ShowPossibilities(pos->rgSquare[c].bvPossibilities));
+ }
+ }
+ printf("\n");
+#ifdef DBG
+ printf("%u empty squares...\n", (unsigned)pos->uEmpty);
+#endif
+}
+
+
+
+
+void
+DumpBoardHtml(POSITION *pos)
+/*++
+
+Routine description:
+
+ Draw a text representation of the playing board.
+
+Parameters:
+
+ POSITION *pos : position
+
+Return value:
+
+ void
+
+--*/
+{
+ COOR c;
+
+ FOREACH_SQUARE(c)
+ {
+ if (pos->rgSquare[c].uValue)
+ {
+ printf("%u:%u\n", (unsigned)c,
+ (unsigned)pos->rgSquare[c].uValue);
+ }
+ }
+ printf("\n");
+ printf("%u empty squares...\n", (unsigned)pos->uEmpty);
+}
+
+void
+DumpBoardSimple(POSITION *pos) {
+ COOR c;
+
+ FOREACH_SQUARE(c)
+ {
+ if (pos->rgSquare[c].uValue) {
+ printf("%u", (unsigned)pos->rgSquare[c].uValue);
+ } else {
+ printf("-");
+ }
+ }
+ printf("\n");
+}
+
+
+BOOL
+LiftValue(POSITION *pos, COOR sq)
+{
+ ULONG uValue = pos->rgSquare[sq].uValue;
+ BITV bvMask = (1UL << (uValue - 1));
+ COOR c;
+ COOR cCol, cRow;
+ ULONG x;
+
+ //
+ // Make sure it's full
+ //
+ if (IS_EMPTY(uValue))
+ {
+ return(FALSE);
+ }
+
+ //
+ // uValue is a possibility on the file/rank/group again.
+ //
+ cCol = COL(sq);
+ ASSERT(cCol == pos->rgSquare[sq].cCol);
+ FOREACH_COL(c, cCol)
+ {
+ pos->rgSquare[c].bvPossibilities |= bvMask;
+ }
+
+ cRow = ROW(sq);
+ ASSERT(cRow == pos->rgSquare[sq].cRow);
+ FOREACH_ROW(c, cRow)
+ {
+ pos->rgSquare[c].bvPossibilities |= bvMask;
+ }
+
+ FOREACH_GROUP(c, pos->rgSquare[sq].cGroup, x)
+ {
+ pos->rgSquare[c].bvPossibilities |= bvMask;
+ }
+
+ pos->uEmpty += 1;
+ pos->rgSquare[sq].uValue = 0;
+ pos->rgSquare[sq].bvPossibilities |= bvMask;
+ pos->bvRemainingByGroup[pos->rgSquare[sq].cGroup] |= bvMask;
+ pos->bvRemainingByCol[pos->rgSquare[sq].cCol] |= bvMask;
+ pos->bvRemainingByRow[pos->rgSquare[sq].cRow] |= bvMask;
+
+ return(TRUE);
+}
+
+
+
+BOOL
+PlaceValue(POSITION *pos, COOR sq, ULONG uValue, BOOL fReduce)
+/*++
+
+Routine description:
+
+ Put a number in a square.
+
+Parameters:
+
+ POSITION *pos : board
+ COOR sq : square
+ ULONG uValue : number
+
+Return value:
+
+ BOOL : TRUE if move is legal, FALSE if it leads to an invalid position
+
+--*/
+{
+ BITV bvMask = ~(1UL << (uValue - 1));
+ COOR c;
+ COOR cCol, cRow;
+ ULONG x;
+
+ //
+ // Make sure it's empty
+ //
+ if (!IS_EMPTY(pos->rgSquare[sq].uValue))
+ {
+ if (pos->rgSquare[sq].uValue == uValue)
+ {
+ return(TRUE);
+ }
+ else
+ {
+ return(FALSE);
+ }
+ }
+
+ //
+ // There can only be one uValue per file/rank/group
+ //
+ cCol = COL(sq);
+ ASSERT(cCol == pos->rgSquare[sq].cCol);
+ FOREACH_COL(c, cCol)
+ {
+ pos->rgSquare[c].bvPossibilities &= bvMask;
+ }
+
+ cRow = ROW(sq);
+ ASSERT(cRow == pos->rgSquare[sq].cRow);
+ FOREACH_ROW(c, cRow)
+ {
+ pos->rgSquare[c].bvPossibilities &= bvMask;
+ }
+
+ FOREACH_GROUP(c, pos->rgSquare[sq].cGroup, x)
+ {
+ pos->rgSquare[c].bvPossibilities &= bvMask;
+ }
+
+ ASSERT(pos->uEmpty > 0);
+ pos->uEmpty -= 1;
+ ASSERT(pos->uEmpty < 81);
+ pos->rgSquare[sq].uValue = uValue;
+ pos->rgSquare[sq].bvPossibilities = (1 << (uValue - 1));
+ pos->bvRemainingByGroup[pos->rgSquare[sq].cGroup] &= bvMask;
+ pos->bvRemainingByCol[pos->rgSquare[sq].cCol] &= bvMask;
+ pos->bvRemainingByRow[pos->rgSquare[sq].cRow] &= bvMask;
+
+ return(PositionIsLegal(pos, fReduce));
+}
+
+
+
+
+ULONG
+BitNumber(BITV x)
+/*++
+
+Routine description:
+
+ Given a bit, return its number.
+
+Parameters:
+
+ BITV x
+
+Return value:
+
+ ULONG
+
+--*/
+{
+ ULONG u = 0;
+ do
+ {
+ x >>= 1;
+ u++;
+ }
+ while(x > 0);
+ return(u);
+}
+
+
+ULONG
+BitCount(BITV x)
+/*++
+
+Routine description:
+
+ Count the number of bits in a bitvector.
+
+Parameters:
+
+ BITV x : bitv
+
+Return value:
+
+ ULONG : population count
+
+--*/
+{
+ ULONG uCount = 0;
+
+ ASSERT(!(x & 0xFFFFFE00));
+ while(x)
+ {
+ uCount += (x & 1);
+ x >>= 1;
+ }
+ return(uCount);
+}
+
+
+ULONG
+LegalValue(POSITION *pos, COOR c)
+{
+ ULONG x, y;
+ if (!IS_EMPTY(pos->rgSquare[c].uValue))
+ {
+ return(pos->rgSquare[c].uValue);
+ }
+
+ x = pos->rgSquare[c].bvPossibilities;
+ if ((x & 0x1FF) == 0) return(0);
+ do
+ {
+ y = 1 << (rand() % 9);
+ if (y & x)
+ {
+ return(BitNumber(y));
+ }
+ }
+ while(1);
+}
+
+
+
+
+BOOL
+PositionIsLegal(POSITION *pos, BOOL fReduce)
+/*++
+
+Routine description:
+
+ Determine if a position is legal
+
+Parameters:
+
+ POSITION *pos : board
+
+Return value:
+
+ BOOL : TRUE on success, FALSE on failure
+
+--*/
+{
+ COOR c, sq, cOnly;
+ ULONG x, y, uCount;
+ BITV bvMaskFull, bvMaskPoss;
+
+ g_uNodes += 1;
+#ifdef DBG
+ printf("Is this legal?\n");
+ DumpBoardSimple(pos);
+ printf("----------------------------------------------------------\n");
+#endif
+
+ FOREACH_SQUARE(c)
+ {
+ if (IS_EMPTY(pos->rgSquare[c].uValue))
+ {
+ x = pos->rgSquare[c].bvPossibilities;
+ if (x == 0)
+ {
+#ifdef DBG
+ printf("Nothing possible at sq %u\n", c);
+#endif
+ return(FALSE);
+ }
+ }
+ }
+
+ //
+ // Note: I don't combine the two loops here because this way we
+ // detect invalid positions (empty squares w/ no legal moves in
+ // them) earlier instead of recursing on obviously bad positions.
+ //
+ FOREACH_SQUARE(c)
+ {
+ if (IS_EMPTY(pos->rgSquare[c].uValue))
+ {
+ x = pos->rgSquare[c].bvPossibilities;
+ if (ONE_BIT(x))
+ {
+#ifdef DBG
+ printf("%u at %u is forced...\n", BitNumber(x), c);
+#endif
+ if ((TRUE == fReduce) &&
+ (FALSE == PlaceValue(pos, c, BitNumber(x), fReduce)))
+ {
+#ifdef DBG
+ printf("Couldn't place forced %u at sq %u\n",
+ BitNumber(x), c);
+#endif
+ return(FALSE);
+ }
+ }
+ }
+ }
+
+ //
+ // If there's only one square in a row|col|group that can be
+ // a given number, make the move.
+ //
+ for (x = 1; x <= 256; x <<= 1)
+ {
+ for (c = 0; c < 9; c++)
+ {
+ uCount = 0;
+ FOREACH_COL(sq, c)
+ {
+ if (IS_EMPTY(pos->rgSquare[sq].uValue) &&
+ (pos->rgSquare[sq].bvPossibilities & x))
+ {
+ uCount++;
+ cOnly = sq;
+ if (uCount > 1) break;
+ }
+ }
+ if (uCount == 1)
+ {
+#ifdef DBG
+ printf("%u at square %u is forced...\n", BitNumber(x), cOnly);
+#endif
+ if ((TRUE == fReduce) &&
+ (FALSE == PlaceValue(pos, cOnly, BitNumber(x), fReduce)))
+ {
+ return(FALSE);
+ }
+ }
+
+ uCount = 0;
+ FOREACH_ROW(sq, c)
+ {
+ if (IS_EMPTY(pos->rgSquare[sq].uValue) &&
+ (pos->rgSquare[sq].bvPossibilities & x))
+ {
+ uCount++;
+ cOnly = sq;
+ if (uCount > 1) break;
+ }
+ }
+ if (uCount == 1)
+ {
+#ifdef DBG
+ printf("%u at square %u is forced...\n", BitNumber(x), cOnly);
+#endif
+ if ((TRUE == fReduce) &&
+ (FALSE == PlaceValue(pos, cOnly, BitNumber(x), fReduce)))
+ {
+ return(FALSE);
+ }
+ }
+
+ uCount = 0;
+ FOREACH_GROUP(sq, c, y)
+ {
+ if (IS_EMPTY(pos->rgSquare[sq].uValue) &&
+ (pos->rgSquare[sq].bvPossibilities & x))
+ {
+ uCount++;
+ cOnly = sq;
+ if (uCount > 1) break;
+ }
+ }
+ if (uCount == 1)
+ {
+#ifdef DBG
+ printf("%u at square %u is forced...\n", BitNumber(x), cOnly);
+#endif
+ if ((TRUE == fReduce) &&
+ (FALSE == PlaceValue(pos, cOnly, BitNumber(x), fReduce)))
+ {
+ return(FALSE);
+ }
+ }
+ }
+ }
+
+ for (c = 0; c < 9; c++)
+ {
+ bvMaskPoss = bvMaskFull = 0;
+ FOREACH_ROW(sq, c)
+ {
+ bvMaskPoss |= pos->rgSquare[sq].bvPossibilities;
+ if (!IS_EMPTY(pos->rgSquare[sq].uValue))
+ {
+ ASSERT(pos->rgSquare[sq].bvPossibilities ==
+ (1 << pos->rgSquare[sq].uValue));
+ if (bvMaskFull & pos->rgSquare[sq].bvPossibilities)
+ {
+#ifdef DBG
+ printf("Two %u's in row %u.\n", pos->rgSquare[sq].uValue,
+ c);
+#endif
+ return(FALSE);
+ }
+ bvMaskFull |= pos->rgSquare[sq].bvPossibilities;
+ }
+ }
+ if (bvMaskPoss != 0x1FF)
+ {
+#ifdef DBG
+ printf("Not everything is possible in row %u (%x).\n",
+ c, bvMaskPoss);
+#endif
+ return(FALSE);
+ }
+
+ bvMaskPoss = bvMaskFull = 0;
+ FOREACH_COL(sq, c)
+ {
+ bvMaskPoss |= pos->rgSquare[sq].bvPossibilities;
+ if (!IS_EMPTY(pos->rgSquare[sq].uValue))
+ {
+ ASSERT(pos->rgSquare[sq].bvPossibilities ==
+ (1 << pos->rgSquare[sq].uValue));
+ if (bvMaskFull & pos->rgSquare[sq].bvPossibilities)
+ {
+#ifdef DBG
+ printf("Two %u's in col %u.\n", pos->rgSquare[sq].uValue,
+ c);
+#endif
+ return(FALSE);
+ }
+ bvMaskFull |= pos->rgSquare[sq].bvPossibilities;
+ }
+ }
+ if (bvMaskPoss != 0x1FF)
+ {
+#ifdef DBG
+ printf("Not everything is possible in col %u (%x).\n",
+ c, bvMaskPoss);
+#endif
+ return(FALSE);
+ }
+
+ bvMaskPoss = bvMaskFull = 0;
+ FOREACH_GROUP(sq, c, y)
+ {
+ bvMaskPoss |= pos->rgSquare[sq].bvPossibilities;
+ if (!IS_EMPTY(pos->rgSquare[sq].uValue))
+ {
+ ASSERT(pos->rgSquare[sq].bvPossibilities ==
+ (1 << pos->rgSquare[sq].uValue));
+ if (bvMaskFull & pos->rgSquare[sq].bvPossibilities)
+ {
+#ifdef DBG
+ printf("Two %u's in group %u.\n", pos->rgSquare[sq].uValue,
+ c);
+#endif
+ return(FALSE);
+ }
+ bvMaskFull |= pos->rgSquare[sq].bvPossibilities;
+ }
+ }
+ if (bvMaskPoss != 0x1FF)
+ {
+#ifdef DBG
+ printf("Not everything is possible in group %u (%x).\n",
+ c, bvMaskPoss);
+#endif
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+
+void
+InitializePosition(POSITION *pos)
+/*++
+
+Routine description:
+
+ Initialize a position to empty.
+
+Parameters:
+
+ POSITION *pos : position
+
+Return value:
+
+ void
+
+--*/
+{
+ COOR c;
+ memset(pos, 0, sizeof(POSITION));
+ pos->uEmpty = 81;
+ FOREACH_SQUARE(c)
+ {
+ pos->rgSquare[c].bvPossibilities = 0x1FF;
+ pos->rgSquare[c].cGroup = g_cGroup[c];
+ pos->rgSquare[c].cRow = ROW(c);
+ pos->rgSquare[c].cCol = COL(c);
+ }
+
+ for (c = 0; c < 9; c++)
+ {
+ pos->bvRemainingByRow[c] = 0x1FF;
+ pos->bvRemainingByCol[c] = 0x1FF;
+ pos->bvRemainingByGroup[c] = 0x1FF;
+ }
+}
+
+
+typedef struct _FEWEST
+{
+ COOR c;
+ ULONG uBitCount;
+ BITV bvPossibilities;
+} FEWEST;
+
+
+void
+Sort(FEWEST *p, ULONG uLen)
+/*++
+
+Routine description:
+
+ Selection sort to put the FEWEST array in order... this is so that
+ that when we make a guess we are guessing at the square with the
+ fewest possible choices.
+
+Parameters:
+
+ FEWEST *p : start of array
+ ULONG uLen : number of elements in it
+
+Return value:
+
+ void
+
+--*/
+{
+ ULONG u, v;
+ ULONG uMinPos;
+ FEWEST temp;
+
+ for (u = 0; u < uLen; u++)
+ {
+ uMinPos = u;
+ for (v = u + 1; v < uLen; v++)
+ {
+ if (p[v].uBitCount < p[uMinPos].uBitCount)
+ {
+ uMinPos = v;
+ }
+ }
+
+ temp = p[u];
+ p[u] = p[uMinPos];
+ p[uMinPos] = temp;
+ }
+}
+
+
+BOOL
+Solve(POSITION *pos, ULONG uDepth)
+/*++
+
+Routine description:
+
+ Recursively solve a suduko position.
+
+Parameters:
+
+ POSITION *pos : position
+ ULONG uDepth : number of ply from root
+
+Return value:
+
+ BOOL : TRUE if solved, FALSE if no solution exists
+
+--*/
+{
+ COOR c;
+ ULONG u, v;
+ POSITION p;
+ FEWEST sFewest[81];
+ BOOL fRet = FALSE;
+
+ g_uNodes++;
+
+#ifdef DBG
+ printf("Depth %u (%u node%s):\n", uDepth,
+ g_uNodes,
+ (g_uNodes != 1) ? "" : "s");
+ DumpBoardSimple(pos);
+ printf("---------------------------------------------------\n");
+#endif
+
+ //
+ // Find the square with the fewest possibilities
+ //
+ FOREACH_SQUARE(c)
+ {
+ sFewest[c].c = c;
+ sFewest[c].bvPossibilities = pos->rgSquare[c].bvPossibilities;
+ sFewest[c].uBitCount = BitCount(pos->rgSquare[c].bvPossibilities);
+ if ((pos->rgSquare[c].uValue == 0) && (sFewest[c].uBitCount == 0))
+ {
+ ASSERT(!sFewest[c].bvPossibilities);
+#ifdef DBG
+ printf("%u: Found inconsistency at square %u.\n", uDepth, c);
+ DumpBoard(pos);
+#endif
+ goto end;
+ }
+ }
+ Sort(sFewest, 81);
+
+ //
+ // Make the guess
+ //
+ FOREACH_SQUARE(v) {
+ if (IS_EMPTY(pos->rgSquare[sFewest[v].c].uValue)) {
+ ASSERT(sFewest[v].uBitCount > 0);
+ for (u = 1; u <= 9; u++) {
+
+ //
+ // Only make guesses that are legal. Don't "guess"
+ // the same thing as a prior solution.
+ //
+ if ((sFewest[v].bvPossibilities & (1 << (u - 1))) &&
+ ((g_uSolutions == 0) ||
+ (g_Solution.rgSquare[sFewest[v].c].uValue != u))) {
+ memcpy(&p, pos, sizeof(p));
+ g_uGuesses += 1;
+#ifdef DBG
+ printf("%u: Guessing %u at square %u\n",
+ uDepth, u, sFewest[v].c);
+#endif
+ if ((FALSE == PlaceValue(pos, sFewest[v].c, u, TRUE)) ||
+ (FALSE == Solve(pos, uDepth + 1)))
+ {
+ //
+ // Unmake the guess move.
+ //
+#ifdef DBG
+ printf("%u: Bad guess (%u at %u), taking it back.\n",
+ uDepth, u, sFewest[v].c);
+#endif
+ memcpy(pos, &p, sizeof(p));
+
+ //
+ // We've learned that the guess is wrong, mask it
+ // off the possibilities bitv.
+ //
+ pos->rgSquare[sFewest[v].c].bvPossibilities &=
+ ~(1 << (u - 1));
+
+ //
+ // Are we in a screwed up state now after masking
+ // off that bit? If so something above us in the
+ // callstack is screwed up.
+ //
+ if (pos->rgSquare[sFewest[v].c].bvPossibilities == 0)
+ {
+#ifdef DBG
+ printf("%u: Nothing possible at square %u.\n",
+ uDepth, sFewest[v].c);
+#endif
+ goto end;
+ }
+ }
+ else
+ {
+ //
+ // This move leads to a win. Count it as a
+ // solution and carry on.
+ //
+#ifdef DBG
+ printf("%u: Guess of %u at square %u leads to a "
+ "solution\n",
+ uDepth, u, sFewest[v].c);
+#endif
+ fRet = TRUE;
+ if (g_uSolutions == 1)
+ {
+ memcpy(pos, &p, sizeof(p));
+ } else {
+ goto end;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (0 == pos->uEmpty)
+ {
+ fRet = TRUE;
+ g_uSolutions += 1;
+#ifdef DBG
+ printf("%u: Puzzle is solved, solution number %u\n",
+ uDepth, g_uSolutions);
+ DumpBoardSimple(pos);
+#endif
+ if (g_uSolutions == 1)
+ {
+ memcpy(&g_Solution, pos, sizeof(POSITION));
+ }
+ goto end;
+ }
+
+ end:
+ return fRet;
+}
+
+
+void
+GeneratePuzzle(BOOL fHard, POSITION *pos)
+{
+ COOR c, x;
+ ULONG v;
+ POSITION p;
+ ULONG u;
+ ULONG uTries;
+ COOR cSeed[] = {
+ 1, 7, 12, 14, 17, 27, 31, 35, 37,40, 43,
+ 45, 49, 53, 63, 66, 68, 71, 73, 79, 0 };
+
+ srand(time(0));
+ u = 0;
+ do
+ {
+ //
+ // Clear the board
+ //
+ InitializePosition(pos);
+
+ if (!fHard)
+ {
+ //
+ // Place a number, try to solve it, repeat...
+ //
+ while(pos->uEmpty > 45)
+ {
+ c = RANDOM_COOR;
+ if (IS_EMPTY(pos->rgSquare[c].uValue))
+ {
+ v = LegalValue(pos, c);
+ if (0 == v) break;
+
+ //
+ // See if the rest can be solved by deduction...
+ //
+ memcpy(&p, pos, sizeof(p));
+ if ((TRUE == PlaceValue(pos, c, v, TRUE)) &&
+ (pos->uEmpty == 0))
+ {
+ memcpy(pos, &p, sizeof(p));
+ pos->rgSquare[c].uValue = v;
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ //
+ // Throw some random numbers out there on the board...
+ //
+ u = 0;
+ while(cSeed[u] != 0) {
+ if (IS_EMPTY(pos->rgSquare[cSeed[u]].uValue))
+ {
+ v = LegalValue(pos, cSeed[u]);
+ if (0 == v) break;
+
+ (void)PlaceValue(pos, cSeed[u], v, TRUE);
+ }
+ u++;
+ }
+
+ //
+ // Solve the puzzle within these constraints...
+ //
+ g_uSolutions = g_uGuesses = g_uNodes = 0;
+ if (TRUE == Solve(pos, 0))
+ {
+ //
+ // Now start taking away values until you get a puzzle
+ // that is still uniquely solvable but requires
+ // guessing
+ //
+ uTries = 0;
+ while(1) {
+ c = RANDOM_COOR;
+ if (!IS_EMPTY(pos->rgSquare[c].uValue)) {
+ InitializePosition(&p);
+ FOREACH_SQUARE(x) {
+ if ((x != c) &&
+ (!IS_EMPTY(pos->rgSquare[x].uValue))) {
+ PlaceValue(&p, x,
+ pos->rgSquare[x].uValue, FALSE);
+ }
+ }
+ ASSERT(p.uEmpty - 1 == pos->uEmpty);
+ g_uSolutions = g_uGuesses = g_uNodes = 0;
+ if ((TRUE == Solve(&p, 0)) && (g_uSolutions == 1))
+ {
+ uTries = 0;
+ memcpy(pos, &p, sizeof(p));
+ if ((g_uGuesses > 5) &&
+ (g_uNodes > 200) &&
+ (pos->uEmpty > 55)) {
+ printf("%u guesses, %u nodes.\n",
+ g_uGuesses, g_uNodes);
+ return;
+ }
+ }
+ else
+ {
+ uTries++;
+ if (uTries > 50) {
+ usleep(20);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ while(1);
+}
+
+
+int
+main(int argc, char *argv[])
+/*++
+
+Routine description:
+
+ Program entry point
+
+Parameters:
+
+ void
+
+Return value:
+
+ int
+
+--*/
+{
+ POSITION pos;
+ char buf[256];
+ char *p;
+ COOR c;
+
+ InitializePosition(&pos);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (argc == 1)
+ {
+ printf("Enter board: ");
+ scanf("%255s", buf);
+ }
+ else
+ {
+ if (!strcmp(argv[1], "--create-easy"))
+ {
+ GeneratePuzzle(FALSE, &pos);
+ DumpBoardHtml(&pos);
+ DumpBoardSimple(&pos);
+ exit(0);
+ }
+ else if (!strcmp(argv[1], "--create-hard"))
+ {
+ GeneratePuzzle(TRUE, &pos);
+ DumpBoardHtml(&pos);
+ DumpBoardSimple(&pos);
+ exit(0);
+ }
+ else
+ {
+ strncpy(buf, argv[1], 255);
+ }
+ }
+
+ //
+ // Solve...
+ //
+ p = buf;
+ c = 0;
+ while(*p)
+ {
+ if (!isspace(*p))
+ {
+ if (isdigit(*p) && (*p != '0'))
+ {
+ if (FALSE == PlaceValue(&pos, c, *p - '0', TRUE))
+ {
+ printf("%u!%u\n",
+ (unsigned)c, (unsigned)(*p - '0'));
+ exit(0);
+ }
+ }
+ c++;
+ }
+ p++;
+ }
+
+ g_uSolutions = g_uGuesses = g_uNodes = 0;
+ if (TRUE == Solve(&pos, 0)) {
+ memcpy(&pos, &g_Solution, sizeof(pos));
+ DumpBoardHtml(&pos);
+ printf("%u solutions, %u nodes, %u guesses.\n",
+ g_uSolutions, g_uNodes, g_uGuesses);
+ }
+ else
+ {
+ DumpBoardHtml(&pos);
+ printf("No solution found...\n");
+ }
+ exit(0);
+}
--- /dev/null
+/* Copyright (c) 2006 Scott Gasch
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PUZZLE_H_
+#define _PUZZLE_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef unsigned long ULONG;
+typedef unsigned long BITV;
+typedef unsigned long COOR;
+#define RANDOM_COOR (rand() % 81)
+typedef unsigned long BOOL;
+
+#if 0
+#define ASSERT(x) if (x) \
+ { ; } \
+ else \
+ { _assert(__FILE__, __LINE__); }
+#define VERIFY(x) ASSERT(x)
+#else
+#define ASSERT(x) ;
+#define VERIFY(x) x;
+#endif // DEBUG
+
+#ifdef PERF_COUNTERS
+#define INC(x) ((x) += 1)
+#else
+#define INC(x)
+#endif
+
+typedef struct _SQUARE
+{
+ BITV bvPossibilities;
+ ULONG uValue;
+ ULONG cCol;
+ ULONG cRow;
+ ULONG cGroup;
+} SQUARE;
+
+//
+// 0 1 2 3 4 5 6 7 8
+// 9 10 11 12 13 14 15 16 17
+// 18 19 20 21 22 23 24 25 26
+//
+// 27 28 29 30 31 32 33 34 35
+// 36 37 38 39 40 41 42 43 44
+// 45 46 47 48 49 50 51 52 53
+//
+// 54 55 56 57 58 59 60 61 62
+// 63 64 65 66 67 68 69 70 71
+// 72 73 74 75 76 77 78 79 80
+//
+COOR g_cGroup[81] = {
+ 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 4, 4, 4, 5, 5, 5,
+ 3, 3, 3, 4, 4, 4, 5, 5, 5,
+ 3, 3, 3, 4, 4, 4, 5, 5, 5,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8
+};
+
+COOR g_cSquareByGroup[9][9] =
+{
+ { 0, 1, 2, 9, 10, 11, 18, 19, 20 },
+ { 3, 4, 5, 12, 13, 14, 21, 22, 23 },
+ { 6, 7, 8, 15, 16, 17, 24, 25, 26 },
+ { 27, 28, 29, 36, 37, 38, 45, 46, 47 },
+ { 30, 31, 32, 39, 40, 41, 48, 49, 50 },
+ { 33, 34, 35, 42, 43, 44, 51, 52, 53 },
+ { 54, 55, 56, 63, 64, 65, 72, 73, 74 },
+ { 57, 58, 59, 66, 67, 68, 75, 76, 77 },
+ { 60, 61, 62, 69, 70, 71, 78, 79, 80 }
+};
+
+#define IS_EMPTY(x) ((x) == 0)
+#define COL(c) ((c) % 9)
+#define FIRST_OF_COL(c) (c)
+#define ROW(c) ((c) / 9)
+#define FIRST_OF_ROW(c) ((c) * 9)
+#define FOREACH_ROW(c, r) \
+ for((c) = FIRST_OF_ROW(r); (c) < FIRST_OF_ROW((r) + 1); (c)++)
+#define FOREACH_COL(c, f) \
+ for((c) = FIRST_OF_COL(f); (c) < 81; (c) += 9)
+#define FOREACH_GROUP(c, g, x) \
+ for((x) = 0, (c) = g_cSquareByGroup[(g)][(x)]; \
+ (x) < 9; \
+ (x)++, (c) = g_cSquareByGroup[(g)][(x)])
+#define FOREACH_SQUARE(x) \
+ for((x) = 0; (x) < 81; (x)++)
+COOR g_cGroupsByRow[9][3] =
+{
+ { 0, 1, 2 },
+ { 0, 1, 2 },
+ { 0, 1, 2 },
+ { 3, 4, 5 },
+ { 3, 4, 5 },
+ { 3, 4, 5 },
+ { 6, 7, 8 },
+ { 6, 7, 8 },
+ { 6, 7, 8 }
+};
+
+COOR g_cRowsByGroup[9][3] =
+{
+ { 0, 1, 2 },
+ { 0, 1, 2 },
+ { 0, 1, 2 },
+ { 3, 4, 5 },
+ { 3, 4, 5 },
+ { 3, 4, 5 },
+ { 6, 7, 8 },
+ { 6, 7, 8 },
+ { 6, 7, 8 }
+};
+
+COOR g_cGroupsByCol[9][3] =
+{
+ { 0, 3, 6 },
+ { 0, 3, 6 },
+ { 0, 3, 6 },
+ { 1, 4, 7 },
+ { 1, 4, 7 },
+ { 1, 4, 7 },
+ { 2, 5, 8 },
+ { 2, 5, 8 },
+ { 2, 5, 8 }
+};
+
+COOR g_cColsByGroup[9][3] =
+{
+ { 0, 1, 2 },
+ { 3, 4, 5 },
+ { 6, 7, 8 },
+ { 0, 1, 2 },
+ { 3, 4, 5 },
+ { 6, 7, 8 },
+ { 0, 1, 2 },
+ { 3, 4, 5 },
+ { 6, 7, 8 }
+};
+
+typedef struct _POSITION
+{
+ SQUARE rgSquare[81];
+ BITV bvRemainingByGroup[9];
+ BITV bvRemainingByCol[9];
+ BITV bvRemainingByRow[9];
+ ULONG uEmpty;
+} POSITION;
+
+#define ONE_BIT(x) (((x) & (x - 1)) == 0)
+
+#define TRUE (1)
+#define FALSE (0)
+
+#endif /* _PUZZLE_H_ */