[solver] refactor as generic code for column and rows
This commit is contained in:
parent
05bccab309
commit
71e31dcc22
@ -84,15 +84,76 @@ namespace libqnono {
|
||||
|
||||
int nrows, ncols;
|
||||
QVector< QVector<Block> > rows, cols;
|
||||
Mark **data;
|
||||
Mark **m_data;
|
||||
|
||||
Mark& data(int row, int col) {
|
||||
return m_data[col][row];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct View : public T {
|
||||
View(SolverState *state) : T(state) { }
|
||||
|
||||
/* horizontal uses ViewRowColumn, vertical ViewColumnRow */
|
||||
|
||||
bool mark(UndoState *undo_state, bool & changed, int m, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (this->data(m, i) == MARK_WHITE) return FALSE;
|
||||
trackMark(undo_state, changed, this->data(m, i), MARK_BLACK);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool clear(UndoState *undo_state, bool & changed, int m, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (this->data(m, i) == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, this->data(m, i), MARK_WHITE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool markBlock(UndoState *undo_state, bool & changed, int m, const Block &b) {
|
||||
if (b.minFirst == b.maxFirst) {
|
||||
if (b.minFirst > 0) {
|
||||
if (this->data(m, b.minFirst-1) == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, this->data(m, b.minFirst-1), MARK_WHITE);
|
||||
}
|
||||
if (b.minFirst + b.length < this->dimSecond()) {
|
||||
if (this->data(m, b.minFirst + b.length) == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, this->data(m, b.minFirst + b.length), MARK_WHITE);
|
||||
}
|
||||
}
|
||||
return mark(undo_state, changed, m, b.maxFirst, b.minFirst+b.length-1);
|
||||
}
|
||||
|
||||
SolverState *state;
|
||||
};
|
||||
|
||||
struct ViewRowColumn {
|
||||
ViewRowColumn(SolverState *state) : state(state) { }
|
||||
|
||||
Mark& data(int row, int col) { return state->data(row, col); }
|
||||
int dimFirst() const { return state->nrows; }
|
||||
int dimSecond() const { return state->ncols; }
|
||||
|
||||
SolverState *state;
|
||||
};
|
||||
|
||||
struct ViewColumnRow {
|
||||
ViewColumnRow(SolverState *state) : state(state) { }
|
||||
|
||||
Mark& data(int col, int row) { return state->data(row, col); }
|
||||
int dimFirst() const { return state->ncols; }
|
||||
int dimSecond() const { return state->nrows; }
|
||||
|
||||
SolverState *state;
|
||||
};
|
||||
|
||||
SolverState(const NonogramNumbers & numbers)
|
||||
: nrows(numbers.height()), ncols(numbers.width()) {
|
||||
data = new Mark*[ncols];
|
||||
m_data = new Mark*[ncols];
|
||||
for (int col = 0; col < ncols; ++col) {
|
||||
data[col] = new Mark[nrows];
|
||||
m_data[col] = new Mark[nrows];
|
||||
for (int row = 0; row < nrows; ++row) {
|
||||
data[col][row] = MARK_UNKNOWN;
|
||||
m_data[col][row] = MARK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
rows.resize(nrows);
|
||||
@ -111,73 +172,18 @@ namespace libqnono {
|
||||
}
|
||||
}
|
||||
~SolverState() {
|
||||
for (int col = 0; col < ncols; ++col) delete [] data[col];
|
||||
delete[] data;
|
||||
for (int col = 0; col < ncols; ++col) delete [] m_data[col];
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
bool markHorizontal(UndoState *undo_state, bool &changed, int row, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (data[i][row] == MARK_WHITE) return FALSE;
|
||||
trackMark(undo_state, changed, data[i][row], MARK_BLACK);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool clearHorizontal(UndoState *undo_state, bool &changed, int row, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (data[i][row] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[i][row], MARK_WHITE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool markHorizontalBlock(UndoState *undo_state, bool &changed, int row, int minFirst, int maxFirst, int length) {
|
||||
if (minFirst == maxFirst) {
|
||||
if (minFirst > 0) {
|
||||
if (data[minFirst-1][row] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[minFirst-1][row], MARK_WHITE);
|
||||
}
|
||||
if (minFirst + length < ncols) {
|
||||
if (data[minFirst + length][row] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[minFirst + length][row], MARK_WHITE);
|
||||
}
|
||||
}
|
||||
return markHorizontal(undo_state, changed, row, maxFirst, minFirst+length-1);
|
||||
}
|
||||
|
||||
bool markVertical(UndoState *undo_state, bool &changed, int col, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (data[col][i] == MARK_WHITE) return FALSE;
|
||||
trackMark(undo_state, changed, data[col][i], MARK_BLACK);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool clearVertical(UndoState *undo_state, bool &changed, int col, int from, int to) {
|
||||
for (int i = from; i <= to; ++i) {
|
||||
if (data[col][i] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[col][i], MARK_WHITE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
bool markVerticalBlock(UndoState *undo_state, bool &changed, int col, int minFirst, int maxFirst, int length) {
|
||||
if (minFirst == maxFirst) {
|
||||
if (minFirst > 0) {
|
||||
if (data[col][minFirst-1] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[col][minFirst-1], MARK_WHITE);
|
||||
}
|
||||
if (minFirst + length < nrows) {
|
||||
if (data[col][minFirst + length] == MARK_BLACK) return FALSE;
|
||||
trackMark(undo_state, changed, data[col][minFirst + length], MARK_WHITE);
|
||||
}
|
||||
}
|
||||
return markVertical(undo_state, changed, col, maxFirst, minFirst+length-1);
|
||||
}
|
||||
|
||||
bool updateRows(UndoState *undo_state, bool &changed) {
|
||||
for (int i = 0; i < nrows; ++i) {
|
||||
QVector<Block> &line(rows[i]);
|
||||
template<typename T>
|
||||
bool update(UndoState *undo_state, QVector< QVector<Block> > & lines, View<T> *view, bool & changed) {
|
||||
for (int i = 0; i < lines.count(); ++i) {
|
||||
QVector<Block> &line(lines[i]);
|
||||
int lineLen = line.count();
|
||||
|
||||
if (0 == lineLen || (1 == lineLen && 0 == line[0].length)) {
|
||||
if (!clearHorizontal(undo_state, changed, i, 0, ncols-1)) return FALSE;
|
||||
if (!view->clear(undo_state, changed, i, 0, view->dimSecond()-1)) return FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -186,7 +192,7 @@ namespace libqnono {
|
||||
int cell = line[0].minFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell1 = cell, end = cell + line[0].length; cell <= line[0].maxFirst && cell1 < end; ++cell1) {
|
||||
if (MARK_WHITE == data[cell1][i]) {
|
||||
if (MARK_WHITE == view->data(i, cell1)) {
|
||||
cell = cell1 + 1;
|
||||
end = cell + line[0].length;
|
||||
}
|
||||
@ -197,7 +203,7 @@ namespace libqnono {
|
||||
trackInt(undo_state, changed, line[0].minFirst, cell);
|
||||
}
|
||||
// the first black can't be before the first block
|
||||
while (cell < line[0].maxFirst && data[cell][i] != MARK_BLACK) ++cell;
|
||||
while (cell < line[0].maxFirst && view->data(i, cell) != MARK_BLACK) ++cell;
|
||||
if (cell < line[0].maxFirst) {
|
||||
trackInt(undo_state, changed, line[0].maxFirst, cell);
|
||||
}
|
||||
@ -209,7 +215,7 @@ namespace libqnono {
|
||||
int cell = line.last().maxFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell1 = cell + len - 1; cell >= line.last().minFirst && cell1 >= cell; --cell1) {
|
||||
if (MARK_WHITE == data[cell1][i]) {
|
||||
if (MARK_WHITE == view->data(i, cell1)) {
|
||||
cell = cell1 - len;
|
||||
}
|
||||
}
|
||||
@ -219,7 +225,7 @@ namespace libqnono {
|
||||
trackInt(undo_state, changed, line.last().maxFirst, cell);
|
||||
}
|
||||
// the last black can't be after the last block
|
||||
while (cell > line.last().minFirst && data[cell+len-1][i] != MARK_BLACK) --cell;
|
||||
while (cell > line.last().minFirst && view->data(i, cell+len-1) != MARK_BLACK) --cell;
|
||||
if (cell > line.last().minFirst) {
|
||||
trackInt(undo_state, changed, line.last().minFirst, cell);
|
||||
}
|
||||
@ -230,10 +236,10 @@ namespace libqnono {
|
||||
{
|
||||
int minFirst = qMax(line[j].minFirst, 1 + line[j-1].minFirst + line[j-1].length);
|
||||
// the cell before first can't be black
|
||||
while (minFirst <= line[j].maxFirst && MARK_BLACK == data[minFirst-1][i]) ++minFirst;
|
||||
while (minFirst <= line[j].maxFirst && MARK_BLACK == view->data(i, minFirst-1)) ++minFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell = minFirst, end = minFirst + line[j].length; minFirst <= line[j].maxFirst && cell < end; ++cell) {
|
||||
if (MARK_WHITE == data[cell][i]) {
|
||||
if (MARK_WHITE == view->data(i, cell)) {
|
||||
minFirst = cell + 1;
|
||||
end = minFirst + line[j].length;
|
||||
}
|
||||
@ -242,7 +248,7 @@ namespace libqnono {
|
||||
if (minFirst >= line[j-1].maxFirst + line[j-1].length) {
|
||||
int cell = minFirst;
|
||||
// next black cell can't be before this block
|
||||
while (cell < line[j].maxFirst && data[cell][i] != MARK_BLACK) ++cell;
|
||||
while (cell < line[j].maxFirst && view->data(i, cell) != MARK_BLACK) ++cell;
|
||||
if (cell < line[j].maxFirst) {
|
||||
trackInt(undo_state, changed, line[j].maxFirst, cell);
|
||||
}
|
||||
@ -258,10 +264,10 @@ namespace libqnono {
|
||||
int len = line[k-1].length;
|
||||
int maxFirst = qMin(line[k-1].maxFirst, line[k].maxFirst - len - 1);
|
||||
// the cell after last can't be black
|
||||
while (maxFirst >= line[k-1].minFirst && MARK_BLACK == data[maxFirst + len][i]) --maxFirst;
|
||||
while (maxFirst >= line[k-1].minFirst && MARK_BLACK == view->data(i, maxFirst + len)) --maxFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell = maxFirst + len - 1; maxFirst >= line[k-1].minFirst && cell >= maxFirst; --cell) {
|
||||
if (MARK_WHITE == data[cell][i]) {
|
||||
if (MARK_WHITE == view->data(i, cell)) {
|
||||
maxFirst = cell - len;
|
||||
}
|
||||
}
|
||||
@ -269,7 +275,7 @@ namespace libqnono {
|
||||
if (maxFirst + len <= line[k].minFirst) {
|
||||
int cell = maxFirst;
|
||||
// next black cell before maxFirst+len can't be after this block
|
||||
while (cell > line[k-1].minFirst && data[cell+len-1][i] != MARK_BLACK) --cell;
|
||||
while (cell > line[k-1].minFirst && view->data(i, cell+len-1) != MARK_BLACK) --cell;
|
||||
if (cell > line[k-1].minFirst) {
|
||||
trackInt(undo_state, changed, line[k-1].minFirst, cell);
|
||||
}
|
||||
@ -282,134 +288,12 @@ namespace libqnono {
|
||||
}
|
||||
}
|
||||
|
||||
if (!clearHorizontal(undo_state, changed, i, 0, line[0].minFirst-1)) return FALSE;
|
||||
if (!view->clear(undo_state, changed, i, 0, line[0].minFirst-1)) return FALSE;
|
||||
for (int j = 0; j < lineLen; ++j) {
|
||||
if (j > 0 && !clearHorizontal(undo_state, changed, i, line[j-1].maxFirst + line[j-1].length, line[j].minFirst-1)) return FALSE;
|
||||
if (!markHorizontalBlock(undo_state, changed, i, line[j].minFirst, line[j].maxFirst, line[j].length)) return FALSE;
|
||||
if (j > 0 && !view->clear(undo_state, changed, i, line[j-1].maxFirst + line[j-1].length, line[j].minFirst-1)) return FALSE;
|
||||
if (!view->markBlock(undo_state, changed, i, line[j])) return FALSE;
|
||||
}
|
||||
if (!clearHorizontal(undo_state, changed, i, line.last().maxFirst + line.last().length, ncols-1)) return FALSE;
|
||||
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool updateCols(UndoState *undo_state, bool &changed) {
|
||||
for (int i = 0; i < ncols; ++i) {
|
||||
QVector<Block> &line(cols[i]);
|
||||
int lineLen = line.count();
|
||||
|
||||
if (0 == lineLen || (1 == lineLen && 0 == line[0].length)) {
|
||||
if (!clearVertical(undo_state, changed, i, 0, nrows-1)) return FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// first block
|
||||
{
|
||||
int cell = line[0].minFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell1 = cell, end = cell + line[0].length; cell <= line[0].maxFirst && cell1 < end; ++cell1) {
|
||||
if (MARK_WHITE == data[i][cell1]) {
|
||||
cell = cell1 + 1;
|
||||
end = cell + line[0].length;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell > line[0].minFirst) {
|
||||
if (cell > line[0].maxFirst) return FALSE; // no solution impossible
|
||||
trackInt(undo_state, changed, line[0].minFirst, cell);
|
||||
}
|
||||
// the first black can't be before the first block
|
||||
while (cell < line[0].maxFirst && data[i][cell] != MARK_BLACK) ++cell;
|
||||
if (cell < line[0].maxFirst) {
|
||||
trackInt(undo_state, changed, line[0].maxFirst, cell);
|
||||
}
|
||||
}
|
||||
|
||||
// last block
|
||||
{
|
||||
int len = line.last().length;
|
||||
int cell = line.last().maxFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell1 = cell + len - 1; cell >= line.last().minFirst && cell1 >= cell; --cell1) {
|
||||
if (MARK_WHITE == data[i][cell1]) {
|
||||
cell = cell1 - len;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell < line.last().maxFirst) {
|
||||
if (cell < line.last().minFirst) return FALSE; // no solution impossible
|
||||
trackInt(undo_state, changed, line.last().maxFirst, cell);
|
||||
}
|
||||
// the last black can't be after the last block
|
||||
while (cell > line.last().minFirst && data[i][cell+len-1] != MARK_BLACK) --cell;
|
||||
if (cell > line.last().minFirst) {
|
||||
trackInt(undo_state, changed, line.last().minFirst, cell);
|
||||
}
|
||||
}
|
||||
|
||||
/* check relative block offsets (min distance 1) */
|
||||
for (int j = 1, k = lineLen - 1; j < lineLen; ++j, --k) {
|
||||
{
|
||||
int minFirst = qMax(line[j].minFirst, 1 + line[j-1].minFirst + line[j-1].length);
|
||||
// the cell before first can't be black
|
||||
while (minFirst <= line[j].maxFirst && MARK_BLACK == data[i][minFirst-1]) ++minFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell = minFirst, end = minFirst + line[j].length; minFirst <= line[j].maxFirst && cell < end; ++cell) {
|
||||
if (MARK_WHITE == data[i][cell]) {
|
||||
minFirst = cell + 1;
|
||||
end = minFirst + line[j].length;
|
||||
}
|
||||
}
|
||||
|
||||
if (minFirst >= line[j-1].maxFirst + line[j-1].length) {
|
||||
int cell = minFirst;
|
||||
// next black cell can't be before this block
|
||||
while (cell < line[j].maxFirst && data[i][cell] != MARK_BLACK) ++cell;
|
||||
if (cell < line[j].maxFirst) {
|
||||
trackInt(undo_state, changed, line[j].maxFirst, cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (minFirst > line[j].minFirst) {
|
||||
if (minFirst > line[j].maxFirst) return FALSE; // no solution impossible
|
||||
trackInt(undo_state, changed, line[j].minFirst, minFirst);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int len = line[k-1].length;
|
||||
int maxFirst = qMin(line[k-1].maxFirst, line[k].maxFirst - len - 1);
|
||||
// the cell after last can't be black
|
||||
while (maxFirst >= line[k-1].minFirst && MARK_BLACK == data[i][maxFirst + len]) --maxFirst;
|
||||
// there must be "length" adjacent non white cells
|
||||
for (int cell = maxFirst + len - 1; maxFirst >= line[k-1].minFirst && cell >= maxFirst; --cell) {
|
||||
if (MARK_WHITE == data[i][cell]) {
|
||||
maxFirst = cell - len;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFirst + len <= line[k].minFirst) {
|
||||
int cell = maxFirst;
|
||||
// next black cell before maxFirst+len can't be after this block
|
||||
while (cell > line[k-1].minFirst && data[i][cell+len-1] != MARK_BLACK) --cell;
|
||||
if (cell > line[k-1].minFirst) {
|
||||
trackInt(undo_state, changed, line[k-1].minFirst, cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFirst < line[k-1].maxFirst) {
|
||||
if (maxFirst < line[k-1].minFirst) return FALSE; // no solution impossible
|
||||
trackInt(undo_state, changed, line[k-1].maxFirst, maxFirst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!clearVertical(undo_state, changed, i, 0, line[0].minFirst-1)) return FALSE;
|
||||
for (int j = 0; j < lineLen; ++j) {
|
||||
if (j > 0 && !clearVertical(undo_state, changed, i, line[j-1].maxFirst + line[j-1].length, line[j].minFirst-1)) return FALSE;
|
||||
if (!markVerticalBlock(undo_state, changed, i, line[j].minFirst, line[j].maxFirst, line[j].length)) return FALSE;
|
||||
}
|
||||
if (!clearVertical(undo_state, changed, i, line.last().maxFirst + line.last().length, nrows-1)) return FALSE;
|
||||
if (!view->clear(undo_state, changed, i, line.last().maxFirst + line.last().length, view->dimSecond()-1)) return FALSE;
|
||||
|
||||
}
|
||||
return TRUE;
|
||||
@ -419,7 +303,7 @@ namespace libqnono {
|
||||
image.resize(QSize(ncols, nrows));
|
||||
for (int i = 0; i < ncols; ++i) {
|
||||
for (int j = 0; j < nrows; ++j) {
|
||||
image.setPixel(i, j, data[i][j] == MARK_BLACK);
|
||||
image.setPixel(i, j, data(j, i) == MARK_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -428,7 +312,7 @@ namespace libqnono {
|
||||
for (int j = 0; j < nrows; ++j) {
|
||||
QDebug dbg = qDebug();
|
||||
for (int i = 0; i < ncols; ++i) {
|
||||
switch (data[i][j]) {
|
||||
switch (data(j, i)) {
|
||||
case MARK_UNKNOWN:
|
||||
dbg << "?"; break;
|
||||
case MARK_BLACK:
|
||||
@ -456,10 +340,12 @@ namespace libqnono {
|
||||
|
||||
void solve(QList<NonogramImage> &solutions, UndoState *undo_state = 0) {
|
||||
bool changed = TRUE;
|
||||
View<ViewRowColumn> rowColumn(this);
|
||||
View<ViewColumnRow> columnRow(this);
|
||||
while (changed) {
|
||||
changed = FALSE;
|
||||
if (!updateRows(undo_state, changed)) return;
|
||||
if (!updateCols(undo_state, changed)) return;
|
||||
if (!update(undo_state, rows, &rowColumn, changed)) return;
|
||||
if (!update(undo_state, cols, &columnRow, changed)) return;
|
||||
}
|
||||
if (!undo_state) {
|
||||
qDebug() << "State after first run:";
|
||||
@ -467,14 +353,14 @@ namespace libqnono {
|
||||
}
|
||||
for (int i = 0; i < ncols; ++i) {
|
||||
for (int j = 0; j < nrows; ++j) {
|
||||
if (data[i][j] == MARK_UNKNOWN) {
|
||||
if (data(j, i) == MARK_UNKNOWN) {
|
||||
UndoState subundo;
|
||||
|
||||
trackMark(&subundo, changed, data[i][j], MARK_BLACK);
|
||||
trackMark(&subundo, changed, data(j, i), MARK_BLACK);
|
||||
solve(solutions, &subundo);
|
||||
undo(subundo);
|
||||
|
||||
trackMark(&subundo, changed, data[i][j], MARK_WHITE);
|
||||
trackMark(&subundo, changed, data(j, i), MARK_WHITE);
|
||||
solve(solutions, &subundo);
|
||||
undo(subundo);
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user