|
|
@ -1,496 +1,14 @@ |
|
|
|
#include "cnonogramsolver.h"
|
|
|
|
#include "cnonogram.h"
|
|
|
|
|
|
|
|
#include "nonogramsolver.h"
|
|
|
|
|
|
|
|
#include <QString>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
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; |
|
|
|
}; |
|
|
|
enum { UNDO_INT, UNDO_MARK } type; |
|
|
|
}; |
|
|
|
typedef QList<UndoOp> 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; |
|
|
|
} |
|
|
|
} |
|
|
|
undo_state.clear(); |
|
|
|
} |
|
|
|
|
|
|
|
struct State { |
|
|
|
int nrows, ncols; |
|
|
|
QVector< QVector<Block> > rows, cols; |
|
|
|
Mark **data; |
|
|
|
|
|
|
|
State(const CNonogramNumbers & numbers) |
|
|
|
: nrows(numbers.height()), ncols(numbers.width()) { |
|
|
|
data = new Mark*[ncols]; |
|
|
|
for (int col = 0; col < ncols; ++col) { |
|
|
|
data[col] = new Mark[nrows]; |
|
|
|
for (int row = 0; row < nrows; ++row) { |
|
|
|
data[col][row] = MARK_UNKNOWN; |
|
|
|
} |
|
|
|
} |
|
|
|
rows.resize(nrows); |
|
|
|
cols.resize(ncols); |
|
|
|
for (int row = 0; row < nrows; ++row) { |
|
|
|
foreach (quint16 len, numbers.rows()[row]) { |
|
|
|
Block block = { 0, ncols - len, len }; |
|
|
|
rows[row] << block; |
|
|
|
} |
|
|
|
} |
|
|
|
for (int col = 0; col < ncols; ++col) { |
|
|
|
foreach (quint16 len, numbers.columns()[col]) { |
|
|
|
Block block = { 0, nrows - len, len }; |
|
|
|
cols[col] << block; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
~State() { |
|
|
|
for (int col = 0; col < ncols; ++col) delete [] data[col]; |
|
|
|
delete[] 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]); |
|
|
|
int lineLen = line.count(); |
|
|
|
|
|
|
|
if (0 == lineLen) { |
|
|
|
if (!clearHorizontal(undo_state, changed, i, 0, ncols-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[cell1][i]) { |
|
|
|
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[cell][i] != 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[cell1][i]) { |
|
|
|
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[cell+len-1][i] != 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[minFirst-1][i]) ++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]) { |
|
|
|
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[cell][i] != 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[maxFirst + len][i]) --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]) { |
|
|
|
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[cell+len-1][i] != 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 (!clearHorizontal(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 (!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) { |
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
CNonogramSolution* solution() { |
|
|
|
bool **data = new bool*[ncols]; |
|
|
|
for (int i = 0; i < ncols; ++i) { |
|
|
|
data[i] = new bool[nrows]; |
|
|
|
for (int j = 0; j < nrows; ++j) { |
|
|
|
data[i][j] = (this->data[i][j] == MARK_BLACK); |
|
|
|
} |
|
|
|
} |
|
|
|
return new CNonogramSolution(QSize(ncols, nrows), data); |
|
|
|
} |
|
|
|
|
|
|
|
void debugState() { |
|
|
|
for (int j = 0; j < nrows; ++j) { |
|
|
|
QDebug dbg = qDebug(); |
|
|
|
for (int i = 0; i < ncols; ++i) { |
|
|
|
switch (data[i][j]) { |
|
|
|
case MARK_UNKNOWN: |
|
|
|
dbg << "?"; break; |
|
|
|
case MARK_BLACK: |
|
|
|
dbg << "M"; break; |
|
|
|
case MARK_WHITE: |
|
|
|
dbg << " "; break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
qDebug() << "Row blocks:"; |
|
|
|
for (int j = 0; j < nrows; ++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) { |
|
|
|
QDebug dbg = qDebug() << j << ":"; |
|
|
|
foreach (Block block, cols[j]) { |
|
|
|
dbg << "[" << block.minFirst << block.maxFirst << block.length << "]"; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void solve(QList<CNonogramSolution*> &solutions, UndoState *undo_state = 0) { |
|
|
|
bool changed = TRUE; |
|
|
|
while (changed) { |
|
|
|
changed = FALSE; |
|
|
|
if (!updateRows(undo_state, changed)) return; |
|
|
|
if (!updateCols(undo_state, 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[i][j] == MARK_UNKNOWN) { |
|
|
|
UndoState subundo; |
|
|
|
|
|
|
|
trackMark(&subundo, changed, data[i][j], MARK_BLACK); |
|
|
|
solve(solutions, &subundo); |
|
|
|
undo(subundo); |
|
|
|
|
|
|
|
trackMark(&subundo, changed, data[i][j], MARK_WHITE); |
|
|
|
solve(solutions, &subundo); |
|
|
|
undo(subundo); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
qDebug() << "Found solution:"; |
|
|
|
debugState(); |
|
|
|
|
|
|
|
solutions << solution(); |
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
QList<CNonogramSolution*> solve(const CNonogramNumbers & numbers) { |
|
|
|
QList<CNonogramSolution*> solutions; |
|
|
|
State solveState(numbers); |
|
|
|
solveState.solve(solutions); |
|
|
|
|
|
|
|
foreach(CNonogramSolution* solution, solutions) { |
|
|
|
Q_ASSERT(numbers.check(solution->data())); |
|
|
|
} |
|
|
|
|
|
|
|
return solutions; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CNonogramSolver::CNonogramSolver(QObject * parent) : QObject(parent), |
|
|
|
m_Nonogram(NULL) |
|
|
|
{ |
|
|
|
CNonogramSolver::CNonogramSolver(QObject * parent) |
|
|
|
: QObject(parent), m_Nonogram(0) { |
|
|
|
} |
|
|
|
|
|
|
|
CNonogramSolver::~CNonogramSolver() { |
|
|
@ -503,19 +21,15 @@ namespace libqnono { |
|
|
|
bool CNonogramSolver::solve() { |
|
|
|
if (!m_Nonogram) return false; |
|
|
|
|
|
|
|
{ |
|
|
|
QList<CNonogramSolution*> solutions = libqnono::solve(CNonogramNumbers(*m_Nonogram)); |
|
|
|
if (!solutions.empty()) { |
|
|
|
bool **data = solutions.first()->data(); |
|
|
|
for (int i = 0; i < m_Nonogram->width(); ++i) { |
|
|
|
for (int j = 0; j < m_Nonogram->height(); ++j) { |
|
|
|
emit markRequested(i, j, data[i][j] ? CMT_MARKED : CMT_CROSSED); |
|
|
|
} |
|
|
|
QList<NonogramImage> solutions = libqnono::solve(m_Nonogram->numbers()); |
|
|
|
if (!solutions.empty()) { |
|
|
|
NonogramImage &sol(solutions.first()); |
|
|
|
for (int i = 0; i < m_Nonogram->width(); ++i) { |
|
|
|
for (int j = 0; j < m_Nonogram->height(); ++j) { |
|
|
|
emit markRequested(i, j, sol.pixel(i, j) ? CMT_MARKED : CMT_CROSSED); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (CNonogramSolution *s, solutions) delete s; |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
return FALSE; |
|
|
|