-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnonogram_solver.h
213 lines (172 loc) · 5.36 KB
/
nonogram_solver.h
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include <memory>
#include <vector>
#include "neuronet.hpp"
enum class CellState { EMPTY, SOLID, CROSSED };
enum class Direction { EMPTY, ROW, COLUMN };
struct LineName {
Direction dir = Direction::EMPTY;
int index = 0;
// comparison needed for use as key
bool operator==(const LineName &o) const {
return dir == o.dir && index == o.index;
}
static LineName Row(int i) {
LineName n{.dir = Direction::ROW, .index = i};
return n;
}
static LineName Column(int i) {
LineName n{.dir = Direction::COLUMN, .index = i};
return n;
}
};
// implement hash for LineName to use it as map key
namespace std {
template <>
struct hash<LineName> {
std::size_t operator()(const LineName &n) const {
return std::hash<Direction>()(n.dir) ^ std::hash<int>()(n.index);
}
};
} // namespace std
struct LineStats {
int wiggleRoom; // max wiggle for a segment
int numSegments; // number of segment constraints
int doneSegments; // number of segments marked done
int numChanges; // number of changes since last examination
};
class Solver;
class Slice {
Solver &solver_;
const std::vector<CellState> &g_;
int offset0_;
int step_;
int length_;
public:
Slice(Solver &solver, int offset0, int step, int length);
Slice(Solver &solver, LineName name);
CellState get(int i) const { return g_[offset0_ + step_ * i]; };
void set(int i, CellState s) const;
int length() const { return length_; };
// returns the first position >= start where a hole (no X) of size
// length is found. If no such hole is found, return -1.
int findHoleStartingAt(int start, int length) const;
// returns the length of strip (consecutive same state cells)
// starting at position i.
int stripLength(int i) const;
int indexOfNextSolid(int start, int bound) const;
// setSegment between i and j (exclusive) to state val.
int setSegment(int i, int j, CellState val) const;
Slice reverse() const;
};
class Line {
private:
const std::vector<int> len_;
std::vector<int> lb_;
std::vector<int> ub_;
std::vector<bool> done_;
const Slice slice_;
int numSegments() { return len_.size(); };
int len(int i) { return len_[i]; };
int lb(int i) { return lb_[i]; };
int ub(int i) { return slice_.length() - ub_[ub_.size() - 1 - i] - 1; };
bool done(int i) { return done_[i]; };
static bool fitLeftMost(Slice slice, const std::vector<int> &len,
std::vector<int> &lb);
// returns a segment index ranges (left inclusive, right exclusive)
// that lb(i) <= start and ub(i) >= end.
std::pair<int, int> collidingSegments(int start, int end);
public:
Line(Solver &solver, LineName name, std::vector<int> &&len);
void updateStats();
bool inferSegments();
bool inferStrips();
bool infer();
LineName name;
LineStats stats;
struct State {
std::vector<int> lb;
std::vector<int> ub;
std::vector<bool> done;
explicit State(const Line &l);
State() = default;
State(const State &) = default;
State(State &&) = default;
State &operator=(const State &) = default;
State &operator=(State &&) = default;
};
State getState() const;
void setState(State &&s);
};
constexpr int edgeScoreLen = 5; // special treatment of edge
constexpr int gridHalfEdge = 2; // neuronet grid size (5x5)
constexpr int gridSize = (2 * gridHalfEdge + 1) * (2 * gridHalfEdge + 1);
class Solver {
public:
struct Config {
// for getDirty sorting
double wiggleRoom;
double numSegments;
double doneSegments;
double numChanges;
double LineScore(const LineStats &s) const {
return wiggleRoom * s.wiggleRoom + numSegments * s.numSegments +
doneSegments * s.doneSegments + numChanges * s.numChanges;
};
// for make a guess at X,Y
double rowCoef;
double colCoef;
double edgeScore[edgeScoreLen];
std::unique_ptr<Net> n;
std::pair<double, CellState> GuessScore(const Solver &s, int x,
int y) const;
int maxLines; // number of lines to check before failing
};
const Config &config_;
const int width_;
const int height_;
std::vector<CellState> g_;
LineName lineName_; // the line we are working on
bool failed_ = false;
struct Guess {
int x;
int y;
CellState val;
static Guess Empty() {
Guess g{.x = -1, .y = -1, .val = CellState::EMPTY};
return g;
};
bool isEmpty() { return x == -1 && y == -1 && val == CellState::EMPTY; };
} guessed_ = Guess::Empty();
struct State {
std::vector<CellState> g;
std::vector<Line::State> lines;
Guess guessed;
};
struct Stats {
int lineCount = 0;
int wrongGuesses = 0;
int maxDepth = 0;
} stats_;
private:
std::vector<std::unique_ptr<Line>> lines_;
std::vector<LineName> dirty_;
std::vector<State> states_;
public:
Solver(const Config &config, std::vector<std::vector<int>> &&rows,
std::vector<std::vector<int>> &&cols);
CellState get(int x, int y) const { return g_[x + y * width_]; };
void set(int x, int y, CellState s);
Line &getLine(LineName name) const {
int i = name.dir == Direction::ROW ? name.index : name.index + height_;
return *(lines_[i].get());
};
LineName getDirty();
void markDirty(LineName n);
std::vector<double> GridAt(int x, int y) const;
void pushState();
void popState();
bool infer();
Guess guess();
bool solve();
void printGrid();
};