From 281109d1e43926401b76f4f52e38f524db438b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Fri, 20 Apr 2012 15:23:20 +0200 Subject: [PATCH] use Marker in solver, put undo into a class --- libqnono/nonogramsolver.cpp | 257 +++++++++++++++++------------------- 1 file changed, 124 insertions(+), 133 deletions(-) diff --git a/libqnono/nonogramsolver.cpp b/libqnono/nonogramsolver.cpp index 3e8471f..826f453 100644 --- a/libqnono/nonogramsolver.cpp +++ b/libqnono/nonogramsolver.cpp @@ -18,6 +18,7 @@ ***************************************************************************/ #include "nonogramsolver.h" #include "nonogramimage.h" +#include "nonogrammarker.h" #include "nonogramnumbers.h" #include @@ -27,86 +28,95 @@ namespace libqnono { struct Block { int minFirst, maxFirst, length; }; - enum Mark { MARK_UNKNOWN = 0, MARK_BLACK, MARK_WHITE }; - struct UndoOp { - union { - struct { - int *ptr, old; - } data_int; - struct { - Mark *ptr, old; - } data_mark; + typedef NonogramMarker::Mark Mark; + + static const Mark MARK_UNKNOWN = NonogramMarker::NONE; + static const Mark MARK_BLACK = NonogramMarker::MARKED; + static const Mark MARK_WHITE = NonogramMarker::CROSSED; + + class UndoState { + private: + struct UndoOp { + union { + struct { + int *ptr, old; + } data_int; + struct { + Mark *ptr, old; + } data_mark; + }; + enum { UNDO_INT, UNDO_MARK } type; }; - enum { UNDO_INT, UNDO_MARK } type; - }; - typedef QList UndoState; - - static void trackInt(UndoState *undo_state, bool &changed, int &ptr, int val) { - if (val == ptr) return; - changed = TRUE; - if (undo_state) { - UndoOp op; - op.type = UndoOp::UNDO_INT; - op.data_int.ptr = &ptr; - op.data_int.old = ptr; - undo_state->push_front(op); - } - ptr = val; - } - - static void trackMark(UndoState *undo_state, bool &changed, Mark &ptr, Mark val) { - if (val == ptr) return; - changed = TRUE; - if (undo_state) { - UndoOp op; - op.type = UndoOp::UNDO_MARK; - op.data_mark.ptr = &ptr; - op.data_mark.old = ptr; - undo_state->push_front(op); - } - ptr = val; - } - - static void undo(UndoState & undo_state) { - foreach (const UndoOp &op, undo_state) { - switch (op.type) { - case UndoOp::UNDO_INT: - *op.data_int.ptr = op.data_int.old; - break; - case UndoOp::UNDO_MARK: - *op.data_mark.ptr = op.data_mark.old; - break; + + QList m_ops; + + public: + void trackInt(bool &changed, int &ptr, int val) { + if (val == ptr) return; + changed = TRUE; + if (this) { + UndoOp op; + op.type = UndoOp::UNDO_INT; + op.data_int.ptr = &ptr; + op.data_int.old = ptr; + m_ops.push_front(op); } + ptr = val; } - undo_state.clear(); - } + + void trackMark(bool &changed, Mark &ptr, Mark val) { + if (val == ptr) return; + changed = TRUE; + if (this) { + UndoOp op; + op.type = UndoOp::UNDO_MARK; + op.data_mark.ptr = &ptr; + op.data_mark.old = ptr; + m_ops.push_front(op); + } + ptr = val; + } + + void undo() { + foreach (const UndoOp &op, m_ops) { + switch (op.type) { + case UndoOp::UNDO_INT: + *op.data_int.ptr = op.data_int.old; + break; + case UndoOp::UNDO_MARK: + *op.data_mark.ptr = op.data_mark.old; + break; + } + } + m_ops.clear(); + } + }; - int nrows, ncols; + NonogramMarker m_marks; QVector< QVector > rows, cols; - Mark **m_data; - Mark& data(int row, int col) { - return m_data[col][row]; + inline Mark& pixel(int x, int y) { + return m_marks.pixel(x, y); } template struct View : public T { - View(SolverState *state) : T(state) { } + 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); + undo_state->trackMark(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); + undo_state->trackMark(changed, this->data(m, i), MARK_WHITE); } return TRUE; } @@ -114,48 +124,40 @@ namespace libqnono { 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); + undo_state->trackMark(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); + undo_state->trackMark(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) { } + 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; } + inline Mark& data(int row, int col) { return state.pixel(col, row); } + inline int dimFirst() const { return state.m_marks.height(); } + inline int dimSecond() const { return state.m_marks.width(); } - SolverState *state; + SolverState & state; }; struct ViewColumnRow { - ViewColumnRow(SolverState *state) : state(state) { } + 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; } + inline Mark& data(int col, int row) { return state.pixel(col, row); } + inline int dimFirst() const { return state.m_marks.width(); } + inline int dimSecond() const { return state.m_marks.height(); } - SolverState *state; + SolverState & state; }; SolverState(const NonogramNumbers & numbers) - : nrows(numbers.height()), ncols(numbers.width()) { - m_data = new Mark*[ncols]; - for (int col = 0; col < ncols; ++col) { - m_data[col] = new Mark[nrows]; - for (int row = 0; row < nrows; ++row) { - m_data[col][row] = MARK_UNKNOWN; - } - } + : m_marks(numbers.size()) { + int nrows = m_marks.height(), ncols = m_marks.width(); rows.resize(nrows); cols.resize(ncols); for (int row = 0; row < nrows; ++row) { @@ -171,19 +173,15 @@ namespace libqnono { } } } - ~SolverState() { - for (int col = 0; col < ncols; ++col) delete [] m_data[col]; - delete[] m_data; - } template - bool update(UndoState *undo_state, QVector< QVector > & lines, View *view, bool & changed) { + 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 (!view->clear(undo_state, changed, i, 0, view->dimSecond()-1)) return FALSE; + if (!view.clear(undo_state, changed, i, 0, view.dimSecond()-1)) return FALSE; continue; } @@ -192,7 +190,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 == view->data(i, cell1)) { + if (MARK_WHITE == view.data(i, cell1)) { cell = cell1 + 1; end = cell + line[0].length; } @@ -200,12 +198,12 @@ namespace libqnono { if (cell > line[0].minFirst) { if (cell > line[0].maxFirst) return FALSE; // no solution impossible - trackInt(undo_state, changed, line[0].minFirst, cell); + undo_state->trackInt(changed, line[0].minFirst, cell); } // the first black can't be before the first block - while (cell < line[0].maxFirst && view->data(i, cell) != 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); + undo_state->trackInt(changed, line[0].maxFirst, cell); } } @@ -215,19 +213,19 @@ 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 == view->data(i, cell1)) { + if (MARK_WHITE == view.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); + undo_state->trackInt(changed, line.last().maxFirst, cell); } // the last black can't be after the last block - while (cell > line.last().minFirst && view->data(i, cell+len-1) != 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); + undo_state->trackInt(changed, line.last().minFirst, cell); } } @@ -236,10 +234,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 == view->data(i, minFirst-1)) ++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 == view->data(i, cell)) { + if (MARK_WHITE == view.data(i, cell)) { minFirst = cell + 1; end = minFirst + line[j].length; } @@ -248,15 +246,15 @@ 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 && view->data(i, cell) != 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); + undo_state->trackInt(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); + undo_state->trackInt(changed, line[j].minFirst, minFirst); } } @@ -264,10 +262,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 == view->data(i, maxFirst + len)) --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 == view->data(i, cell)) { + if (MARK_WHITE == view.data(i, cell)) { maxFirst = cell - len; } } @@ -275,44 +273,36 @@ 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 && view->data(i, cell+len-1) != 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); + undo_state->trackInt(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); + undo_state->trackInt(changed, line[k-1].maxFirst, maxFirst); } } } - if (!view->clear(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 && !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 (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 (!view->clear(undo_state, changed, i, line.last().maxFirst + line.last().length, view->dimSecond()-1)) return FALSE; + if (!view.clear(undo_state, changed, i, line.last().maxFirst + line.last().length, view.dimSecond()-1)) return FALSE; } return TRUE; } - void storeSolution(NonogramImage & image) { - image.resize(QSize(ncols, nrows)); - for (int i = 0; i < ncols; ++i) { - for (int j = 0; j < nrows; ++j) { - image.setPixel(i, j, data(j, i) == MARK_BLACK); - } - } - } - void debugState() { - for (int j = 0; j < nrows; ++j) { +#ifndef QT_NO_DEBUG_OUTPUT + for (int j = 0; j < m_marks.height(); ++j) { QDebug dbg = qDebug(); - for (int i = 0; i < ncols; ++i) { - switch (data(j, i)) { + for (int i = 0; i < m_marks.width(); ++i) { + switch (pixel(i, j)) { case MARK_UNKNOWN: dbg << "?"; break; case MARK_BLACK: @@ -323,46 +313,47 @@ namespace libqnono { } } qDebug() << "Row blocks:"; - for (int j = 0; j < nrows; ++j) { + for (int j = 0; j < rows.count(); ++j) { QDebug dbg = qDebug() << j << ":"; foreach (Block block, rows[j]) { dbg << "[" << block.minFirst << block.maxFirst << block.length << "]"; } } qDebug() << "Col blocks:"; - for (int j = 0; j < ncols; ++j) { + for (int j = 0; j < cols.count(); ++j) { QDebug dbg = qDebug() << j << ":"; foreach (Block block, cols[j]) { dbg << "[" << block.minFirst << block.maxFirst << block.length << "]"; } } +#endif } void solve(QList &solutions, UndoState *undo_state = 0) { bool changed = TRUE; - View rowColumn(this); - View columnRow(this); + View rowColumn(*this); + View columnRow(*this); while (changed) { changed = FALSE; - if (!update(undo_state, rows, &rowColumn, changed)) return; - if (!update(undo_state, cols, &columnRow, 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:"; debugState(); } - for (int i = 0; i < ncols; ++i) { - for (int j = 0; j < nrows; ++j) { - if (data(j, i) == MARK_UNKNOWN) { + for (int i = 0; i < m_marks.width(); ++i) { + for (int j = 0; j < m_marks.height(); ++j) { + if (pixel(i, j) == MARK_UNKNOWN) { UndoState subundo; - trackMark(&subundo, changed, data(j, i), MARK_BLACK); + subundo.trackMark(changed, pixel(i, j), MARK_BLACK); solve(solutions, &subundo); - undo(subundo); + subundo.undo(); - trackMark(&subundo, changed, data(j, i), MARK_WHITE); + subundo.trackMark(changed, pixel(i, j), MARK_WHITE); solve(solutions, &subundo); - undo(subundo); + subundo.undo(); return; } } @@ -372,7 +363,7 @@ namespace libqnono { debugState(); solutions.append(NonogramImage());; - storeSolution(solutions.last()); + solutions.last().load(m_marks); } };