From 8d50ac79d054192b0847520ff602f0006c9eb5ab Mon Sep 17 00:00:00 2001 From: karl Date: Tue, 1 Dec 2020 22:18:55 +0100 Subject: [PATCH] Minor optimization + comments --- main.cpp | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index 018b635..43953e4 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ 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]; @@ -27,6 +28,7 @@ public: } }; +// A cell in the game board -- used for the heap class Cell { public: Board *board; @@ -43,6 +45,7 @@ public: } 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 @@ -70,13 +73,14 @@ 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'); @@ -100,10 +104,12 @@ public: std::make_heap(heap.begin(), heap.end(), sort_cells); } + // TODO: Turn this into a member of Cell as opterator< 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(); @@ -112,6 +118,7 @@ public: 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(); @@ -120,12 +127,20 @@ public: 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()) { @@ -139,28 +154,37 @@ public: // If not, we're done! return true; } - if (heap.empty()) return true; Cell cell = get_next_best_empty_cell(); - for (int value : cell.possible_values) { - board->set(cell.x, cell.y, value); - update_all_cells(); + // 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(); + 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++) { @@ -188,6 +212,6 @@ int main() { //Sudoku solver("850002400720000009004000000000107002305000900040000000000080070017000000000036040"); solver.solve(); //solver.print_board(); - + return 0; } \ No newline at end of file