#include #include #include #include #include #include #define BOARD_SIZE 81 #define BOARD_DIMENSION 9 typedef std::pair, std::list> move; // Wrapper for the game board with the field data and some utility functions. class Board { public: int values[BOARD_SIZE]; int coordinates_to_id(int x, int y) { return y * BOARD_DIMENSION + x; } int get(int x, int y) { return values[coordinates_to_id(x, y)]; } void set(int x, int y, int value) { values[coordinates_to_id(x, y)] = value; } }; // A cell in the game board -- used for the heap class Cell { public: Board *board; int x, y; int value; std::list possible_values; Cell(Board *board, int x, int y) : board(board), x(x), y(y), value(board->get(x, y)) {} void set_value(int new_value) { value = new_value; update_possible_values(); } void update_possible_values() { // Start off with all possible numbers std::list possibilities{1, 2, 3, 4, 5, 6, 7, 8 ,9}; // Remove all from this row for (int dx = 0; dx < BOARD_DIMENSION; dx++) { possibilities.remove(board->get(dx, y)); } // Remove all from this column for (int dy = 0; dy < BOARD_DIMENSION; dy++) { possibilities.remove(board->get(x, dy)); } // Remove all from this quadrant for (int dy = 0; dy < 3; dy++) { for (int dx = 0; dx < 3; dx++) { possibilities.remove(board->get((x - x % 3) + dx, (y - y % 3) + dy)); } } possible_values = possibilities; } }; class Sudoku { private: Board *board = new Board(); std::vector heap; int value_count[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; public: Sudoku(std::string s) { int x = 0; int y = 0; // Parse the input for (unsigned int i = 0; i < s.length(); i++) { board->values[i] = (int) (s[i] - '0'); if (board->values[i] == 0) { Cell cell(board, x, y); heap.emplace_back(cell); } x++; if (x == BOARD_DIMENSION) { x -= BOARD_DIMENSION; y++; } } // Update values of cells initially for (Cell &cell : heap) { cell.update_possible_values(); } std::make_heap(heap.begin(), heap.end(), sort_cells); } static bool sort_cells(const Cell& a, const Cell& b) { return a.possible_values.size() < b.possible_values.size(); } // Update the possible values which a cell can take for every cell in the heap void update_all_cells() { for (Cell &cell : heap) { cell.update_possible_values(); } std::sort_heap(heap.begin(), heap.end(), sort_cells); } // Returns the best cell in the heap (the one with the most obvious choice) Cell get_next_best_empty_cell() { std::pop_heap(heap.begin(), heap.end(), sort_cells); Cell c = heap.back(); heap.pop_back(); return c; } // Wrapper for solve_recurse, to be called from outside void solve() { auto start = std::chrono::high_resolution_clock::now(); solve_recurse(); auto end = std::chrono::high_resolution_clock::now(); std::cout << std::endl; std::cout << "Time: " << std::chrono::duration_cast(end - start).count() << " microseconds" << std::endl; print_board(); } // Recurisvely solve the CSP bool solve_recurse() { // If there are no more possible moves in the heap... if (heap.empty()) { // Check whether there are empty fields left for (int y = 0; y < BOARD_DIMENSION; y++) { for (int x = 0; x < BOARD_DIMENSION; x++) { if (board->get(x, y) == 0) return false; } } // If not, we're done! return true; } Cell cell = get_next_best_empty_cell(); // Sort the possible values by how often that value has been used in the board. // This is a simple heuristic based on the thought that a value which hasn't been used often is likely the safer choice. cell.possible_values.sort([&](const int& a, const int& b) {return value_count[a - 1] > value_count[b - 1];}); // Iterate over all possible values this cell can take for (int value : cell.possible_values) { // Apply this value board->set(cell.x, cell.y, value); update_all_cells(); // TODO: We'd only need to re-rate the neighbours // Recurse; if we find a solution, we're done and can return true if (solve_recurse()) { return true; } // Seems like this was a dead end -- reset this move board->set(cell.x, cell.y, 0); update_all_cells(); // TODO: We could save and reuse the rating from before } // Put the cell back into the heap -- it couldn't be used for any valid solution, so we'll need it somewhere else later heap.push_back(cell); std::push_heap(heap.begin(), heap.end(), sort_cells); return false; } // Print the game board in a nice format void print_board() { std::cout << board->values[0] << " "; for (unsigned int i = 1; i < BOARD_SIZE; i++) { if (i % 3 == 0 && i % 9 != 0) { std::cout << "| "; } if (i % 9 == 0) { std::cout << std::endl; } if (i % 27 == 0) { std::cout << "------+-------+------" << std::endl; } std::cout << board->values[i] << " "; } std::cout << std::endl; } }; int main() { //char str[82]; //cin >> str; //Sudoku solver(str); Sudoku solver("006001849030000000000020006000400320400003000600008000010060003000005004029074005"); //Sudoku solver("850002400720000009004000000000107002305000900040000000000080070017000000000036040"); solver.solve(); //solver.print_board(); return 0; }