Sudoku Solver

Transcription

Sudoku Solver
C++ Sudoku Solver via Backtracking
CS 355
Due 11:59 PM, Tuesday, April 27, 2015
9 4
1 3
7 6
1
8
3 2
2
3
6
4
1
8
9
5
2
2
6
5
6 3
7 9 4 5 8 2 1 3 6
4
8
4
7
8
6
1
8
3
5
2
4
7
8
5
9
2
7
1
3
6
9
4
7
8
2
6
1
3
3
7
1
6
4
5
2
9
1
6
5
9
3
7
8
4
7
9
3
5
8
4
6
2
4
8
2
7
6
9
5
1
5
2
4
1
9
3
7
8
Figure 1: Example Sudoku puzzle (left) and solution (right).
1
Introduction
For this programming project you will write a C++ program that implements a Simple Backtracking algorithm to solve 9 × 9 Sudoku puzzles. To learn more about Sudoku, check out the following wikipedia
entry:
http://en.wikipedia.org/wiki/Sudoku
Given an initial puzzle with some subset of cells initially fixed, the goal is to fill each cell with an integer
between 1 and 9 so that the following three criteria are met:
1. Each row contains each of the integers 1 through 9 exactly once;
2. Each column contains each of the integers 1 through 9 exactly once;
3. Each 3 × 3 blocks of cells contains each of the integers 1 through 9 exactly once.
Figure 1 shows an initial puzzle on the left, and a correctly solved puzzle on the right.
The goal of this project is give you practice on created a properly encapsulated data type (representing
a 9 × 9 Sudoku grid) in C++, and provide you with some experience with using other C++ facilities like
the standard library. Section 2 describes the SudokuGrid class you are to implement. A simple method for
deducing some of the puzzle values is given in Section 3. The backtracking algorithm is listed in Section 4
along with the target input and output. What you are to submit is specified in Section 5.
1
2
SudokuGrid class
Define a SudokuGrid class that represents a (partially solved) 9 × 9 Sudoku Puzzle. Each of the cells within
the puzzle can contain a number between 0 and 9 inclusive, where 0 is interpreted as an empty cell. Each cell
number can be marked as being fixed or solved meaning that its an original number from the input puzzle
or its value has been determined respectively. If the cell holds a non-zero number and is neither marked as
fixed nor solved then the number is considered to be a tentative value (i.e., a guess). Further more, each
cell can hold zero to nine “penciled in” values which typically represent tentative possible solutions for that
cell (see the middle and right images in Figure 2).
2.1
Supported methods
SudokuGrid objects should support the following methods:
• SudokuGrid(std::string s); Constructor that creates initial puzzle from an input string as described
in Section 4.1.
• int number(int row, int col) const; Read number at grid location (row, col). Returns 0 for
empty cell.
• void setNumber(int row, int col, int num); Set number at grid location (row, col). Use num =
0 to clear value.
• bool isFixed(int row, int col) const; number at grid location (row, col) is original “fixed”
value.
• bool isSolved(int row, int col) const; Cell at (row, col) has been marked as solved.
• void setSolved(int row, int col); Mark the cell at (row, col) has having been solved.
• bool isPencilSet(int row, int col, int n) const; Is value 1 ≤ n ≤ 9 penciled into the cell at
(row, col)?
• bool anyPencilsSet(int row, int col) const; Are any values at cell (row, col) penciled in?
• void setPencil(int row, int col, int n); Set pencil n in cell (row, col).
• void setAllPencils(int row, int col); Set all nine pencil values in cell (row, col).
• clearPencil(int row, int col, int n); Clear pencil n in cell (row, col).
• void clearAllPencils(int row, int col); Clear all pencil values in cell (row, col).
2.2
Implementation hints
Use types from the standard library (e.g., array) for you containers. These type already correctly implement
deep copy, move, and handle memory management issues for you. Since the grid is fixed at 9 × 9 using an
array of array’s might be appropriate:
std::array<std::array<Cell,9>,9> grid;
bitset is a convenient container for holding a set of a small number of values:
std::bitset<9> pencils; // value 0..8 represent pencils 1..9
May sure your properly encapsulate all internal types and instance variables.
2
Figure 2: Original puzzle (left), all possible valued penciled in (middle), numbers deduced (right).
void autoPencil(SudokuGrid& grid) {
for (int r = 0; r < 9; r++)
for (int c = 0; c < 9; c++)
if (grid.number(r,c) == 0) {
grid.setAllPencils(r,c);
for (int n = 1; n <= 9; n++)
if (conflictingNumber(grid,r,c,n))
grid.clearPencil(r,c,n);
}
}
Figure 3: Algorithm for determining all possible pencil values.
3
Solving by deduction
In Section 4 we describe the algorithm for solving the entire puzzle, but we can often speed up the solution
by deducing the value of certain cells ahead of time. The autoPencil() function listed in Figure 3 can
be used to deduce all the possible values in each cell and “pencil in” the result (see the middle image in
Figure 2). From this, we can use a common method searching for “naked singles” using the algorithm in
Figure 4. If we find a row, column, or block that contains only one unique pencil value we can promote that
pencil value to the appropriate number and mark it as solved. The algorithm in Figure 4 repeats the process
until to more naked singles are found.
4
Backtracking solver
The algorithm in Figure 5 employs a classic recursive backtracking algorithm that may exhaustively examine
the entire search space looking for a solution. In each call we scan the grid for an empty cell (performed
by the findUnassignedLocation function). If we can not find one, then we must have filled in the entire
puzzle and we are done. Otherwise, we try to fill this cell with all possible digits that do not introduce a
conflict with the row, column, or block that the cell is in.
3
void deduce(SudokuGrid& grid) {
bool changed;
do { // repeat until no changes made
autoPencil(grid);
changed = false;
for (int row = 0; row < 9; row++)
for (int col = 0; col < 9; col++)
for (int n = 1; n <= 9; n++)
if (grid.isPencilSet(row, col, n) &&
(numPencilsInRow(grid, row, n) == 1 ||
numPencilsInColumn(grid, col, n) == 1 ||
numPencilsInBlock(grid, row, col, n) == 1)) {
grid.clearAllPencils(row, col);
grid.setNumber(row,col,n);
grid.setSolved(row,col);
autopencil(grid);
changed = true;
break;
}
} while(changed);
}
Figure 4: Algorithm for deducing cell values by searching for “naked singles.”
bool solveSudoku(SudokuGrid& grid) {
int row, col;
if (!findUnassignedLocation(grid, row, col))
return true; // puzzle filled, solution found!
for (int num = 1; num <= 9; num++)
if (!conflictingNumber(grid, row, col, num)) {
grid.setNumber(row, col, num); // try next number
if (solveSudoku(grid))
return true;
// solved!
grid.setNumber(row, col, 0);
// not solved, clear number
}
return false; // not solved, back track
}
Figure 5: Backtracking Sudoku solver.
4.1
4.1.1
Input / Output
Input
The program reads a string from the standard input stream std::cin that specifies the initial fixed and
non-fixed cells for a classic 9 × 9 puzzle. The contents of the puzzle are given in row-major order. A dot ’.’
indicates an empty cell and a digit specifies a fixed value for a cell. For example, the puzzle on the left in
Figure 6 is specified as follows:
4
4 . . | . . . | 8 . 5
. 3 . | . . . | . . .
. . . | 7 . . | . . .
------+-------+------. 2 . | . . . | . 6 .
. . . | . 8 . | 4 . .
. . . | . 1 . | . . .
------+-------+------. . . | 6 . 3 | . 7 .
5 . . | 2 . . | . . .
1 . 4 | . . . | . . .
4 . . | . . . | 8 . 5
. 3 . | . . . | . . .
. . . | 7 . . | . . .
------+-------+------. 2 . | . . . | . 6 .
. . . | . 8 . | 4 . .
. 4 . | . 1 . | . . .
------+-------+------. . . | 6 . 3 | . 7 .
5 . 3 | 2 . 1 | . . .
1 . 4 | . . . | . . .
4 1 7 | 3 6 9 | 8 2 5
6 3 2 | 1 5 8 | 9 4 7
9 5 8 | 7 2 4 | 3 1 6
------+-------+------8 2 5 | 4 3 7 | 1 6 9
7 9 1 | 5 8 6 | 4 3 2
3 4 6 | 9 1 2 | 7 5 8
------+-------+------2 8 9 | 6 4 3 | 5 7 1
5 7 3 | 2 9 1 | 6 8 4
1 6 4 | 8 7 5 | 2 9 3
Figure 6: Printed output of input puzzle (left), deduced cells (middle), and solved puzzle (right).
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
The SudokuGrid class provides the appropriate constructor for initializing a grid from this string:
std::string puzzle;
std::cin >> puzzle;
// validate input
SudokuGrid grid(puzzle);
4.1.2
Output
You program should output the puzzle, the deduced puzzle, and the final solved puzzle as illustrated in
Figure 6 (Note that your puzzles will be printed stacked on top of one another instead of side-by-side as in
the figure).
5
Submit
You will archive your solution (into a .zip or .tar.gz file) which should include the following
• README : A text file (perhaps markdown) that contains the following:
– Your name and email address
– A brief description of the project and archive.
– Description of how to build/run.
– A list of files in the archive.
– Describe any issues where the program may fail or anything the reader should be aware of.
• Makefile : for build main app using g++ or clang++ using the C++11 standard.
• All source code
• Any useful test input or other errata,
Submit your solution via the course electronic website by midnight on the due date. Have fun.
5