diff --git a/main.cpp b/main.cpp index d1cd5e3..018b635 100644 --- a/main.cpp +++ b/main.cpp @@ -1,79 +1,168 @@ #include #include +#include #include #include +#include #define BOARD_SIZE 81 #define BOARD_DIMENSION 9 -class Sudoku { -private: - int board[BOARD_SIZE]; - +typedef std::pair, std::list> move; + +class Board { public: - - Sudoku(std::string s) { - for (unsigned int i = 0; i < s.length(); i++) { - board[i] = (int) (s[i] - '0'); - } - } - - void solve() { - // Very naive first attempt: Fails because it sets stuff wrongly and doesn't backtrack - for (int y = 0; y < BOARD_DIMENSION; y++) { - for (int x = 0; x < BOARD_DIMENSION; x++) { - if (get(x, y) == 0) { - auto possibilities = get_possibilities(x, y); - - set(x, y, possibilities.front()); - } - } - } - - // Real search: - // 1. Sort possibilities by how many new constraints they create - // 2. Start with the one creating the smallest amount of new constraints - // 3. Continue until dead end - // 4. If dead end: Try the next one - } + int values[BOARD_SIZE]; int coordinates_to_id(int x, int y) { return y * BOARD_DIMENSION + x; } int get(int x, int y) { - return board[coordinates_to_id(x, y)]; + return values[coordinates_to_id(x, y)]; } void set(int x, int y, int value) { - board[coordinates_to_id(x, y)] = value; + values[coordinates_to_id(x, y)] = value; + } +}; + +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(); } - std::list get_possibilities(int x, int y) { + void update_possible_values() { 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(get(dx, y)); + possibilities.remove(board->get(dx, y)); } // Remove all from this column for (int dy = 0; dy < BOARD_DIMENSION; dy++) { - possibilities.remove(get(x, 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(get((x - x % 3) + dx, (y - y % 3) + dy)); + possibilities.remove(board->get((x - x % 3) + dx, (y - y % 3) + dy)); } } - return possibilities; + possible_values = possibilities; + } +}; + +class Sudoku { +private: + Board *board = new Board(); + std::vector heap; + +public: + + Sudoku(std::string s) { + int x = 0; + int y = 0; + + 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(); + } + + void update_all_cells() { + for (Cell &cell : heap) { + cell.update_possible_values(); + } + + std::sort_heap(heap.begin(), heap.end(), sort_cells); + } + + Cell get_next_best_empty_cell() { + std::pop_heap(heap.begin(), heap.end(), sort_cells); + Cell c = heap.back(); + heap.pop_back(); + + return c; + } + + void solve() { + solve_recurse(); + + print_board(); + } + + 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; + } + 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(); + + if (solve_recurse()) { + return true; + } + + board->set(cell.x, cell.y, 0); + update_all_cells(); + } + + heap.push_back(cell); + std::push_heap(heap.begin(), heap.end(), sort_cells); + + return false; } void print_board() { - std::cout << board[0] << " "; + std::cout << board->values[0] << " "; for (unsigned int i = 1; i < BOARD_SIZE; i++) { if (i % 3 == 0 && i % 9 != 0) { std::cout << "| "; @@ -84,7 +173,7 @@ public: if (i % 27 == 0) { std::cout << "------+-------+------" << std::endl; } - std::cout << board[i] << " "; + std::cout << board->values[i] << " "; } std::cout << std::endl; @@ -98,7 +187,7 @@ int main() { Sudoku solver("006001849030000000000020006000400320400003000600008000010060003000005004029074005"); //Sudoku solver("850002400720000009004000000000107002305000900040000000000080070017000000000036040"); solver.solve(); - solver.print_board(); + //solver.print_board(); return 0; } \ No newline at end of file