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
© Copyright 2024