Compare commits

...

1 Commits

Author SHA1 Message Date
8d50ac79d0 Minor optimization + comments 2020-12-01 22:19:58 +01:00

View File

@ -10,6 +10,7 @@
typedef std::pair<std::vector<int>, std::list<int>> move; typedef std::pair<std::vector<int>, std::list<int>> move;
// Wrapper for the game board with the field data and some utility functions.
class Board { class Board {
public: public:
int values[BOARD_SIZE]; int values[BOARD_SIZE];
@ -27,6 +28,7 @@ public:
} }
}; };
// A cell in the game board -- used for the heap
class Cell { class Cell {
public: public:
Board *board; Board *board;
@ -43,6 +45,7 @@ public:
} }
void update_possible_values() { void update_possible_values() {
// Start off with all possible numbers
std::list<int> possibilities{1, 2, 3, 4, 5, 6, 7, 8 ,9}; std::list<int> possibilities{1, 2, 3, 4, 5, 6, 7, 8 ,9};
// Remove all from this row // Remove all from this row
@ -70,13 +73,14 @@ class Sudoku {
private: private:
Board *board = new Board(); Board *board = new Board();
std::vector<Cell> heap; std::vector<Cell> heap;
int value_count[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
public: public:
Sudoku(std::string s) { Sudoku(std::string s) {
int x = 0; int x = 0;
int y = 0; int y = 0;
// Parse the input
for (unsigned int i = 0; i < s.length(); i++) { for (unsigned int i = 0; i < s.length(); i++) {
board->values[i] = (int) (s[i] - '0'); board->values[i] = (int) (s[i] - '0');
@ -100,10 +104,12 @@ public:
std::make_heap(heap.begin(), heap.end(), sort_cells); 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) { static bool sort_cells(const Cell& a, const Cell& b) {
return a.possible_values.size() < b.possible_values.size(); 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() { void update_all_cells() {
for (Cell &cell : heap) { for (Cell &cell : heap) {
cell.update_possible_values(); cell.update_possible_values();
@ -112,6 +118,7 @@ public:
std::sort_heap(heap.begin(), heap.end(), sort_cells); 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() { Cell get_next_best_empty_cell() {
std::pop_heap(heap.begin(), heap.end(), sort_cells); std::pop_heap(heap.begin(), heap.end(), sort_cells);
Cell c = heap.back(); Cell c = heap.back();
@ -120,12 +127,20 @@ public:
return c; return c;
} }
// Wrapper for solve_recurse, to be called from outside
void solve() { void solve() {
auto start = std::chrono::high_resolution_clock::now();
solve_recurse(); solve_recurse();
auto end = std::chrono::high_resolution_clock::now();
std::cout << std::endl;
std::cout << "Time: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << " microseconds" << std::endl;
print_board(); print_board();
} }
// Recurisvely solve the CSP
bool solve_recurse() { bool solve_recurse() {
// If there are no more possible moves in the heap... // If there are no more possible moves in the heap...
if (heap.empty()) { if (heap.empty()) {
@ -139,28 +154,37 @@ public:
// If not, we're done! // If not, we're done!
return true; return true;
} }
if (heap.empty()) return true;
Cell cell = get_next_best_empty_cell(); Cell cell = get_next_best_empty_cell();
for (int value : cell.possible_values) { // Sort the possible values by how often that value has been used in the board.
board->set(cell.x, cell.y, value); // This is a simple heuristic based on the thought that a value which hasn't been used often is likely the safer choice.
update_all_cells(); 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()) { if (solve_recurse()) {
return true; return true;
} }
// Seems like this was a dead end -- reset this move
board->set(cell.x, cell.y, 0); 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); heap.push_back(cell);
std::push_heap(heap.begin(), heap.end(), sort_cells); std::push_heap(heap.begin(), heap.end(), sort_cells);
return false; return false;
} }
// Print the game board in a nice format
void print_board() { void print_board() {
std::cout << board->values[0] << " "; std::cout << board->values[0] << " ";
for (unsigned int i = 1; i < BOARD_SIZE; i++) { for (unsigned int i = 1; i < BOARD_SIZE; i++) {