Compare commits
No commits in common. "f145e9bc35a881ab6a9cdf9c7c763e48c689fb81" and "edc8a4922a18955f570a11bd6b7b5e9cc9f102ea" have entirely different histories.
f145e9bc35
...
edc8a4922a
11
Makefile
11
Makefile
@ -1,11 +0,0 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -Wall
|
||||
|
||||
q: main.o
|
||||
$(CXX) $(CXXFLAGS) -o q main.o
|
||||
|
||||
main.o: main.cpp
|
||||
$(CXX) $(CXXFLAGS) -c main.cpp
|
||||
|
||||
clean :
|
||||
-rm *.o q
|
216
main.cpp
216
main.cpp
@ -1,216 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#define BOARD_SIZE 81
|
||||
#define BOARD_DIMENSION 9
|
||||
|
||||
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 {
|
||||
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<int> 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<int> 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<Cell> 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<std::chrono::microseconds>(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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user