Initial commit of sudoku puzzle solver.
authorScott Gasch <[email protected]>
Thu, 2 Jun 2016 02:39:59 +0000 (19:39 -0700)
committerScott Gasch <[email protected]>
Thu, 2 Jun 2016 02:39:59 +0000 (19:39 -0700)
GNUmakefile [new file with mode: 0644]
README [new file with mode: 0644]
puzzle.c [new file with mode: 0644]
puzzle.h [new file with mode: 0644]
puzzle.sln [new file with mode: 0644]
puzzle.suo [new file with mode: 0644]

diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644 (file)
index 0000000..90ab63f
--- /dev/null
@@ -0,0 +1,30 @@
+CC             =       gcc
+RM             =       /bin/rm
+CFLAGS                 =       -pipe -Wall
+HEADERS        =       puzzle.h
+BINARY         =       puzzle
+
+ifdef DBG
+PROFILE                += -g -O -DDEBUG -DDBG
+else
+PROFILE                += -O3
+endif
+
+#
+# ---> .o, not .c! <---
+# 
+OBJS    =       puzzle.o
+
+.SUFFIXES:     .c .o
+
+all:           $(BINARY)
+
+$(BINARY):     $(OBJS)
+               $(CC) $(PROFILE) -o $(BINARY) -Wall $(OBJS)
+
+%.o:           %.c $(HEADERS)
+               $(CC) $(CFLAGS) $(PROFILE) -c $< -o $@
+
+clean:         
+               $(RM) -f *.core *~ *.gmon \#*\# $(OBJS) $(BINARY)
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..6c8ce5c
--- /dev/null
+++ b/README
@@ -0,0 +1,21 @@
+
+    This is a simple text-mode sudoku puzzle solver.  Sudoku is a
+    number game that has been published in the Sunday newspapers in
+    some cities.  For more information see http://www.sudoku.com or
+    http://en.wikipedia.org/wiki/Sudoku.  You can use this program to
+    enter a puzzle and have it search for the solution.
+
+/* 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.
+ */
diff --git a/puzzle.c b/puzzle.c
new file mode 100644 (file)
index 0000000..e8e50c6
--- /dev/null
+++ b/puzzle.c
@@ -0,0 +1,1069 @@
+/* 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);
+}
diff --git a/puzzle.h b/puzzle.h
new file mode 100644 (file)
index 0000000..161d005
--- /dev/null
+++ b/puzzle.h
@@ -0,0 +1,173 @@
+/* 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_ */
diff --git a/puzzle.sln b/puzzle.sln
new file mode 100644 (file)
index 0000000..ff0f832
--- /dev/null
@@ -0,0 +1,11 @@
+Microsoft Visual Studio Solution File, Format Version 8.00\r
+Global\r
+       GlobalSection(SolutionConfiguration) = preSolution\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfiguration) = postSolution\r
+       EndGlobalSection\r
+       GlobalSection(ExtensibilityGlobals) = postSolution\r
+       EndGlobalSection\r
+       GlobalSection(ExtensibilityAddIns) = postSolution\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/puzzle.suo b/puzzle.suo
new file mode 100644 (file)
index 0000000..a46875d
Binary files /dev/null and b/puzzle.suo differ