-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathsolver_basic.cc
149 lines (124 loc) · 5.2 KB
/
solver_basic.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "bitutil.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <memory>
#include <vector>
using namespace std;
namespace {
typedef uint32_t Bits;
constexpr Bits kAll = 0x1ff;
typedef tuple<int, int, int> RowColBox;
struct SolverBasic {
array<Bits, 9> rows_{}, cols_{}, boxes_{};
vector<RowColBox> cells_todo_;
size_t limit_ = 1;
bool min_heuristic_ = false;
size_t num_todo_ = 0, num_guesses_ = 0, num_solutions_ = 0;
int NumCandidates(const RowColBox &row_col_box) {
//int [row, col, box] = cells_todo_[todo_index];
int row = get<0>(row_col_box);
int col = get<1>(row_col_box);
int box = get<2>(row_col_box);
auto candidates = rows_[row] & cols_[col] & boxes_[box];
return NumBitsSet(candidates);
}
// move a cell with the fewest candidates to the head of the sublist [todo_index:end]
void MoveBestTodoToFront(size_t todo_index) {
auto first = cells_todo_.begin() + todo_index;
auto best = first;
int best_count = NumCandidates(*best);
for (auto next = first + 1; best_count > 1 && next < cells_todo_.end(); ++next) {
int next_count = NumCandidates(*next);
if (next_count < best_count) {
best_count = next_count;
best = next;
}
}
swap(*first, *best);
}
// Returns true if a solution is found, updates *solution to reflect assignments
// made on solution path. Also updates num_guesses_ to reflect the number of
// guesses made during search.
void SatisfyGivenPartialAssignment(size_t todo_index, char *solution) {
if (min_heuristic_) MoveBestTodoToFront(todo_index);
//int [row, col, box] = cells_todo_[todo_index];
int row = get<0>(cells_todo_[todo_index]);
int col = get<1>(cells_todo_[todo_index]);
int box = get<2>(cells_todo_[todo_index]);
auto candidates = rows_[row] & cols_[col] & boxes_[box];
while (candidates) {
uint32_t candidate = GetLowBit(candidates);
// only count assignment as a guess if there's more than one candidate.
if (candidates ^ candidate) num_guesses_++;
// clear the candidate from available candidate sets for row, col, box
rows_[row] ^= candidate;
cols_[col] ^= candidate;
boxes_[box] ^= candidate;
solution[row * 9 + col] = (char) ('1' + LowOrderBitIndex(candidate));
// recursively solve remaining cells and back out with the last solution.
if (todo_index < num_todo_) {
SatisfyGivenPartialAssignment(todo_index + 1, solution);
} else {
++num_solutions_;
}
if (num_solutions_ == limit_) return;
// restore the candidate to available candidate sets for row, col, box
rows_[row] ^= candidate;
cols_[col] ^= candidate;
boxes_[box] ^= candidate;
candidates = ClearLowBit(candidates);
}
}
const int boxen[81] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2,
3, 3, 3, 4, 4, 4, 5, 5, 5, 3, 3, 3, 4, 4, 4, 5, 5, 5, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 6, 7, 7, 7, 8, 8, 8};
bool Initialize(const char *input, size_t limit, uint32_t configuration, char *solution) {
rows_.fill(kAll);
cols_.fill(kAll);
boxes_.fill(kAll);
limit_ = limit;
min_heuristic_ = configuration > 0;
num_guesses_ = 0;
num_solutions_ = 0;
// copy initial clues to solution since our todo list won't include these cells
memcpy(solution, input, 81);
cells_todo_.clear();
for (int row = 0; row < 9; ++row) {
for (int col = 0; col < 9; ++col) {
int cell = row * 9 + col;
int box = boxen[cell];
if (input[row * 9 + col] == '.') {
// blank cell: add to the todo list
cells_todo_.emplace_back(make_tuple(row, col, box));
} else {
// a given clue: clear availability bits for row, col, and box
uint32_t value = 1u << (uint32_t) (input[cell] - '1');
if (rows_[row] & value && cols_[col] & value && boxes_[box] & value) {
rows_[row] ^= value;
cols_[col] ^= value;
boxes_[box] ^= value;
} else {
return false;
}
}
}
}
num_todo_ = cells_todo_.size() - 1;
return true;
}
};
} // namespace
extern "C"
size_t TdokuSolverBasic(const char *input, size_t limit, uint32_t configuration,
char *solution, size_t *num_guesses) {
static SolverBasic solver;
if (solver.Initialize(input, limit, configuration, solution)) {
solver.SatisfyGivenPartialAssignment(0, solution);
*num_guesses = solver.num_guesses_;
return solver.num_solutions_;
} else {
*num_guesses = 0;
return 0;
}
}