Sunday, January 13, 2013

Code Examples: SudokuPuzzle.h and SudokuPuzzle.cpp

My latest project, a command-line interface Sudoku solver, used the class SudokuPuzzle. Here is the source code for the header file and implementation file for SudokuPuzzle. The class will be re-used in a project that creates a GUI (graphical user interface) for the solver and adds new features, such as the ability to save and load puzzles.

SudokuPuzzle.h

#ifndef SudokuPuzzle_Header
#define SudokuPuzzle_Header

#include <vector>

using namespace std;

class SudokuPuzzle
{
public:

    //Default constructor.
    SudokuPuzzle();

    //Returns a list of candidate values for a spot at (x, y).
    void getPossibleValuesAt(int possibleSolutions[], int x, int y);

    //Returns the number of possible candidates at (x, y).
    int getNumberOfPossibleValuesAt(int x, int y);

    //Attempts to add a number to (x, y). As long as the number
    //is a valid candidate, the method returns true.
    bool addNumberAt(int addNumber, int x, int y);

    //Removes a number at (x, y)
    void removeNumberAt(int x, int y);

    //Resets the puzzle grid and solution list.
    void newPuzzle();

    //Prints the puzzle.
    void printPuzzle();

    //Returns a vector container of all the possible solutions
    //found for the current puzzle.
    void solvePuzzle();

    //Determines whether or not the current puzzle is solved.
    bool isSolved();

    //Copies the puzzle into newPuzzle with a deep copy of the
    //grid.
    void copyPuzzle(SudokuPuzzle &newPuzzle);

    //Shows a solution in the list of solutions at the
    //specified index.
    void showSolution(int index);

    //Returns a solution in the list of solutions at the
    //specified index.
    SudokuPuzzle getSolution(int index);

    //Returns the number of solutions in the list of solutions.
    int getNumberOfSolutionsFound();

private:

    //Two-dimensional grid to store the puzzle.
    int myGrid[9][9];

    //Dynamically expanding vector to store all possible solutions
    //to the puzzle. Most puzzles only have 1 solution, but poorly
    //designed puzzles may have many.
    vector<SudokuPuzzle> possibleSolutions;
};

#endif

SudokuPuzzle.cpp

#include <iostream>
#include <cmath>
#include <vector>
#include "SudokuPuzzle.h"

using namespace std;

SudokuPuzzle::SudokuPuzzle()
{
    //Default constructor. This constructor creates an empty Sudoku grid.

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            myGrid[x][y] = 0;
        }
    }
}

void SudokuPuzzle::getPossibleValuesAt(int possibleSolutions[], int x, int y)
{
    //This function will use the array possibleSolutions as a hash table to
    //determine the value of all candidates for the Sudoku grid at (x, y).
    //The hash table is then used by the calling method to determine which
    //candidates are allowed for that spot.

    for (int index = 0; index < 9; index++)
    {
        possibleSolutions[index] = 1;
    }

    for (int xPos = 0; xPos < 9; xPos++)
    {
        if (xPos != x && myGrid[xPos][y] > 0) possibleSolutions[myGrid[xPos][y] - 1] = 0;
    }

    for (int yPos = 0; yPos < 9; yPos++)
    {
        if (yPos != y && myGrid[x][yPos] > 0) possibleSolutions[myGrid[x][yPos] - 1] = 0;
    }

    int xGroup = floor((double)(x / 3));
    int yGroup = floor((double)(y / 3));
   
    for (int xPos = 0 + (3 * xGroup); xPos < 3 + (3 * xGroup); xPos++)
    {
        for (int yPos = 0 + (3 * yGroup); yPos < 3 + (3 * yGroup); yPos++)
        {
            if (yPos != y && xPos != x && myGrid[xPos][yPos] > 0) possibleSolutions[myGrid[xPos][yPos] - 1] = 0;
        }
    }

    return;
}

int SudokuPuzzle::getNumberOfPossibleValuesAt(int x, int y)
{
    //This function will return the number of candidates available
    //for the Sudoku grid at (x, y).

    if (myGrid[x][y] != 0) return 0;

    int possibleSolutions[9];

    getPossibleValuesAt(possibleSolutions, x, y);

    int candidateCount = 0;

    for (int x = 0; x < 9; x++)
    {
        if (possibleSolutions[x] == 1) candidateCount++;
    }

    return candidateCount;
}

bool SudokuPuzzle::addNumberAt(int addNumber, int x, int y)
{
    //This function will put in addNumber in the grid at (x, y) as long
    //as addNumber is a valid candidate for that spot. A boolean is returned
    //so the caller knows whether or not the number was successfully added.

    if (addNumber == 0) return true;

    if (myGrid[x][y] != 0)
    {
        cout << "The spot at (" << x << "," << y << ") is currently occupied.\n\n";
        system("PAUSE");
        return false;
    }

    int possibleSolutions[9];

    getPossibleValuesAt(possibleSolutions, x, y);

    if (possibleSolutions[addNumber - 1] == 0)
    {
        cout << addNumber << " is not a valid candidate for (" << x << "," << y << ").\n\n";
        system("PAUSE");
        return false;
    }

    myGrid[x][y] = addNumber;
    return true;
}

void SudokuPuzzle::removeNumberAt(int x, int y)
{
    //Remove one number.

    myGrid[x][y] = 0;
}

void SudokuPuzzle::newPuzzle()
{
    //Clears the grid.

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            myGrid[x][y] = 0;
        }
    }
}

void SudokuPuzzle::printPuzzle()
{
    //Display the puzzle for implementation
    //in a command-line environment.

    cout << "\t0 1 2  3 4 5  6 7 8\n\n";

    for (int y = 0; y < 9; y++)
    {
        cout << y << "\t";

        for (int x = 0; x < 9; x++)
        {
            cout << myGrid[x][y] << " ";
            if (x == 2 || x == 5) cout << " ";
        }

        if (y == 2 || y == 5 || y == 8) cout << "\n\n";
        else cout << "\n";
    }
}

bool SudokuPuzzle::isSolved()
{
    //Determine if all spots are filled with valid candidates.

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            if (myGrid[x][y] == 0) return false;
        }
    }

    return true;
}

void SudokuPuzzle::solvePuzzle()
{
    //The puzzle solving algorithm. The algorithm scans each spot
    //and starts with the spot that has the lowest number of
    //possible candidates. Each candidate is tried one at a time
    //and the rest of the spots are filled in the same manner.
    //If a spot is found that is not yet filled, but there are no
    //valid candidates, then the puzzle is unsolvable and a new
    //candidate is tried in a previous spot.

    int candidateX, candidateY, candidateNumber = 100, tempCandidate;
    int candidateList[9];

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            if (myGrid[x][y] == 0)
            {
                tempCandidate = getNumberOfPossibleValuesAt(x, y);

                if (tempCandidate < candidateNumber)
                {
                    candidateNumber = tempCandidate;
                    candidateX = x;
                    candidateY = y;
                    getPossibleValuesAt(candidateList, x, y);
                }
            }
        }
    }

    //All candidates have been determine for (candidateX, candidateY) where
    //that spot has the least amount of candidates. Loop through those candidates
    //and attempt to solve the next resulting puzzle.

    for (int index = 0; index < 9; index++)
    {
        if (candidateList[index] == 1)
        {
            addNumberAt(index + 1, candidateX, candidateY);

            if (isSolved())
            {
                SudokuPuzzle tempPuzzle;
                copyPuzzle(tempPuzzle);
                possibleSolutions.push_back(tempPuzzle);
            }
            else solvePuzzle();
           
            removeNumberAt(candidateX, candidateY);
        }
    }
}

void SudokuPuzzle::copyPuzzle(SudokuPuzzle &newPuzzle)
{
    //Deep copy the grid into newPuzzle.

    for (int x = 0; x < 9; x++)
    {
        for (int y = 0; y < 9; y++)
        {
            newPuzzle.addNumberAt(myGrid[x][y], x, y);
        }
    }
}

void SudokuPuzzle::showSolution(int index)
{
    //Show the solution in possibleSolutions at the specified index.
   
    if (index < 0 || index >= possibleSolutions.size()) return;

    possibleSolutions[index].printPuzzle();
}

SudokuPuzzle SudokuPuzzle::getSolution(int index)
{
    //Return the solution in possibleSolutions at the specified index.

    if (index < 0 || index >= possibleSolutions.size())
    {
        SudokuPuzzle newPuzzle;
        return newPuzzle;
    }
       
    return possibleSolutions[index];
}

int SudokuPuzzle::getNumberOfSolutionsFound()
{
    //Return the number of solutions in possibleSolutions.

    return possibleSolutions.size();
}