From 71e31dcc228e1a0184397640d85eac71b98ab9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Thu, 19 Apr 2012 21:03:02 +0200 Subject: [PATCH] [solver] refactor as generic code for column and rows --- libqnono/nonogramsolver.cpp | 304 +++++++++++------------------------- 1 file changed, 95 insertions(+), 209 deletions(-) diff --git a/libqnono/nonogramsolver.cpp b/libqnono/nonogramsolver.cpp index 998a4ae..3e8471f 100644 --- a/libqnono/nonogramsolver.cpp +++ b/libqnono/nonogramsolver.cpp @@ -84,15 +84,76 @@ namespace libqnono { int nrows, ncols; QVector< QVector > rows, cols; - Mark **data; + Mark **m_data; + + Mark& data(int row, int col) { + return m_data[col][row]; + } + + template + 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 &line(rows[i]); + template + bool update(UndoState *undo_state, QVector< QVector > & lines, View *view, bool & changed) { + for (int i = 0; i < lines.count(); ++i) { + QVector &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 &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 &solutions, UndoState *undo_state = 0) { bool changed = TRUE; + View rowColumn(this); + View 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;