add new abstractions, include magic numbers in serialize
This commit is contained in:
parent
5cb6dd6880
commit
e9c726b27e
@ -18,6 +18,11 @@ set(SOURCES_CPP
|
||||
ccrosspackage.cpp
|
||||
ccrosspackagemodel.cpp
|
||||
ccrosspackagelistmodel.cpp
|
||||
nonogramimage.cpp
|
||||
nonogrammarker.cpp
|
||||
nonogramnumbers.cpp
|
||||
nonogramproblem.cpp
|
||||
nonogramsolver.cpp
|
||||
)
|
||||
|
||||
qt4_wrap_cpp(SOURCES_MOC_CPP ${SOURCES_MOC_H})
|
||||
|
@ -18,7 +18,7 @@
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "ccrosspackage.h"
|
||||
#include "cnonogram.h"
|
||||
#include "nonogramproblem.h"
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
|
||||
@ -28,10 +28,6 @@ namespace libqnono {
|
||||
|
||||
CCrossPackage::~CCrossPackage() {
|
||||
close();
|
||||
|
||||
foreach (CNonogram * i, m_PictureList) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCrossPackage::open() {
|
||||
@ -108,8 +104,6 @@ namespace libqnono {
|
||||
QDataStream in(m_File);
|
||||
in.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
CNonogram * newPicture = NULL;
|
||||
|
||||
qint32 pictureCount = 0;
|
||||
|
||||
if (in.atEnd()) {
|
||||
@ -121,14 +115,13 @@ namespace libqnono {
|
||||
|
||||
int i;
|
||||
for (i = 0; i < pictureCount && !in.atEnd(); i++) {
|
||||
newPicture = new CNonogram();
|
||||
if (!newPicture->readFromStream(in)) {
|
||||
m_PictureList.append(NonogramProblem());
|
||||
if (!m_PictureList.last().readFromStream(in)) {
|
||||
m_PictureList.pop_back();
|
||||
qCritical("invalid package file - invalid picture");
|
||||
delete newPicture;
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
m_PictureList << newPicture;
|
||||
}
|
||||
|
||||
if (i < pictureCount)
|
||||
@ -139,11 +132,9 @@ namespace libqnono {
|
||||
return true;
|
||||
}
|
||||
|
||||
CNonogram * CCrossPackage::takePicture(int ndx) {
|
||||
const NonogramProblem & CCrossPackage::getPicture(int ndx) {
|
||||
loadPictures();
|
||||
CNonogram * result = m_PictureList.takeAt(ndx);
|
||||
unloadPictures();
|
||||
return result;
|
||||
return m_PictureList[ndx];
|
||||
}
|
||||
|
||||
bool CCrossPackage::loadPictures() {
|
||||
@ -158,9 +149,6 @@ namespace libqnono {
|
||||
|
||||
void CCrossPackage::unloadPictures() {
|
||||
if (!m_PictureList.empty()) {
|
||||
foreach (CNonogram * i, m_PictureList) {
|
||||
delete i;
|
||||
}
|
||||
m_PictureList.clear();
|
||||
m_headersOnly = true;
|
||||
}
|
||||
@ -179,8 +167,8 @@ namespace libqnono {
|
||||
out << m_Name;
|
||||
out << (qint32)(m_PictureList.size());
|
||||
|
||||
foreach (CNonogram * i, m_PictureList)
|
||||
i->writeToStream(out);
|
||||
foreach (const NonogramProblem & i, m_PictureList)
|
||||
i.writeToStream(out);
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
|
@ -26,9 +26,9 @@
|
||||
class QFile;
|
||||
|
||||
namespace libqnono {
|
||||
class CNonogram;
|
||||
class NonogramProblem;
|
||||
|
||||
typedef QList<CNonogram *> QMonoPictureList;
|
||||
typedef QList<NonogramProblem> QMonoPictureList;
|
||||
|
||||
class CCrossPackage {
|
||||
public:
|
||||
@ -42,7 +42,7 @@ namespace libqnono {
|
||||
QString name() const { return m_Name; }
|
||||
|
||||
QMonoPictureList & pictures() { Q_ASSERT(!m_headersOnly); return m_PictureList; }
|
||||
CNonogram * takePicture(int ndx);
|
||||
const NonogramProblem & getPicture(int ndx);
|
||||
|
||||
bool loadPictures();
|
||||
void unloadPictures();
|
||||
|
@ -81,7 +81,7 @@ namespace libqnono {
|
||||
|
||||
QModelIndex CCrossPackageModel::index(int row, int column, const QModelIndex & /*parent*/) const {
|
||||
return (m_Package) && (column >= 0) && (row >= 0) && (row < m_Package->pictures().size()) ?
|
||||
createIndex(row, column, static_cast<void *>(m_Package->pictures()[row])) :
|
||||
createIndex(row, column, static_cast<void *>(&m_Package->pictures()[row])) :
|
||||
QModelIndex();
|
||||
}
|
||||
|
||||
@ -160,12 +160,12 @@ namespace libqnono {
|
||||
if (name.isEmpty())
|
||||
return false;
|
||||
|
||||
foreach (CNonogram * i, m_Package->pictures()) {
|
||||
if (i->name() == name)
|
||||
foreach (const NonogramProblem & i, m_Package->pictures()) {
|
||||
if (i.name() == name)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Package->pictures()[index.row()]->setName(name);
|
||||
m_Package->pictures()[index.row()].setName(name);
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
@ -178,9 +178,8 @@ namespace libqnono {
|
||||
|
||||
beginInsertRows(QModelIndex(), m_Package->pictures().size(), 1);
|
||||
|
||||
CNonogram * newNonogram = new CNonogram(size);
|
||||
newNonogram->fill(fillValue);
|
||||
newNonogram->setName(name);
|
||||
NonogramProblem newNonogram(name, 60*60, NonogramImage(size));
|
||||
newNonogram.fill(fillValue);
|
||||
|
||||
m_Package->pictures() << newNonogram;
|
||||
|
||||
@ -192,12 +191,9 @@ namespace libqnono {
|
||||
if (!m_Package)
|
||||
return false;
|
||||
|
||||
CNonogram * newNonogram = new CNonogram();
|
||||
newNonogram->loadFromImage(image);
|
||||
newNonogram->setName(name);
|
||||
newNonogram->setTimeout(60*60); // set an hour as default timeout
|
||||
NonogramProblem newNonogram(name, 60*60, NonogramImage(image));
|
||||
|
||||
if (newNonogram->isValid()) {
|
||||
if (!newNonogram.size().isEmpty()) {
|
||||
beginInsertRows(QModelIndex(), m_Package->pictures().size(), 1);
|
||||
|
||||
m_Package->pictures() << newNonogram;
|
||||
@ -206,7 +202,6 @@ namespace libqnono {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
delete newNonogram;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -217,12 +212,8 @@ namespace libqnono {
|
||||
|
||||
beginInsertRows(parent, row, count);
|
||||
|
||||
CNonogram * newNonogram;
|
||||
|
||||
for (int i = row; i < row+count; ++i) {
|
||||
newNonogram = new CNonogram();
|
||||
newNonogram->fill(false);
|
||||
m_Package->pictures() << newNonogram;
|
||||
m_Package->pictures().push_back(NonogramProblem());
|
||||
}
|
||||
|
||||
endInsertRows();
|
||||
@ -236,7 +227,7 @@ namespace libqnono {
|
||||
beginRemoveRows(parent, row, row+count-1);
|
||||
|
||||
for (int i = row; i < row+count; ++i)
|
||||
delete m_Package->pictures().takeAt(i);
|
||||
m_Package->pictures().removeAt(i);
|
||||
|
||||
endRemoveRows();
|
||||
return true;
|
||||
|
@ -19,342 +19,48 @@
|
||||
***************************************************************************/
|
||||
#include "cnonogram.h"
|
||||
#include <QImage>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace libqnono {
|
||||
static void boolsFromImage(bool ** & data, const QImage &image) {
|
||||
int cols = image.width(), rows = image.height();
|
||||
data = new bool*[cols];
|
||||
for (int col = 0; col < cols; ++col) data[col] = new bool[rows];
|
||||
for (int row = 0; row < rows; ++row) {
|
||||
for (int col = 0; col < cols; ++col) {
|
||||
data[col][row] = 0 == (image.pixel(col, row) & 0x00FFFFFF);
|
||||
}
|
||||
}
|
||||
static const quint64 CNonogram_MAGIC = Q_UINT64_C(0x35a8bca32006c5a9);
|
||||
|
||||
CNonogram::CNonogram() {
|
||||
}
|
||||
|
||||
static void freeBools(const QSize & size, bool ** data) {
|
||||
int cols = size.width();
|
||||
for (int col = 0; col < cols; ++col) delete [] data[col];
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
static const quint64 CNonogramNumbers_MAGIC = Q_UINT64_C(0xd33efcacc0050164);
|
||||
|
||||
CNonogramNumbers::CNonogramNumbers(const vv_num & rows, const vv_num & columns)
|
||||
: m_rows(rows), m_columns(columns) {
|
||||
}
|
||||
|
||||
CNonogramNumbers::CNonogramNumbers(const CNonogram & source)
|
||||
: m_rows(source.height()), m_columns(source.width()) {
|
||||
// remove the "0" blocks (they are used for otherwise empty lines)
|
||||
for (int i = 0; i < source.height(); ++i) {
|
||||
m_rows[i] = source.rowNumbers(i);
|
||||
if (1 == m_rows[i].count() && 0 == m_rows[i][0]) m_rows[i].clear();
|
||||
}
|
||||
for (int i = 0; i < source.width(); ++i) {
|
||||
m_columns[i] = source.columnNumbers(i);
|
||||
if (1 == m_columns[i].count() && 0 == m_columns[i][0]) m_columns[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
CNonogramNumbers::CNonogramNumbers(QSize size, bool **data) {
|
||||
calcFromImage(size, data);
|
||||
}
|
||||
|
||||
CNonogramNumbers::CNonogramNumbers(const QImage & image) {
|
||||
bool ** data = 0;
|
||||
boolsFromImage(data, image);
|
||||
calcFromImage(image.size(), data);
|
||||
freeBools(image.size(), data);
|
||||
}
|
||||
|
||||
void CNonogramNumbers::calcFromImage(QSize size, bool **data) {
|
||||
int rows = size.height(), cols = size.width();
|
||||
m_rows.clear(); m_rows.resize(rows);
|
||||
m_columns.clear(); m_columns.resize(cols);
|
||||
for (int row = 0; row < rows; ++row) {
|
||||
quint16 *val = 0;
|
||||
for (int col = 0; col < cols; ++col) {
|
||||
bool b = data[col][row];
|
||||
if (!val && b) {
|
||||
m_rows[row].append(1);
|
||||
val = &m_rows[row].last();
|
||||
} else if (b) {
|
||||
++(*val);
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int col = 0; col < cols; ++col) {
|
||||
quint16 *val = 0;
|
||||
for (int row = 0; row < rows; ++row) {
|
||||
bool b = data[col][row];
|
||||
if (!val && b) {
|
||||
m_columns[col].append(1);
|
||||
val = &m_columns[col].last();
|
||||
} else if (b) {
|
||||
++(*val);
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CNonogramNumbers::readFromStream(QDataStream & stream) {
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
if (CNonogramNumbers_MAGIC != magic || QDataStream::Ok != stream.status()) {
|
||||
if (QDataStream::ReadPastEnd != stream.status()) stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
stream >> m_rows >> m_columns;
|
||||
return QDataStream::Ok == stream.status();
|
||||
}
|
||||
|
||||
void CNonogramNumbers::writeToStream(QDataStream & stream) {
|
||||
stream << CNonogramNumbers_MAGIC << m_rows << m_columns;
|
||||
}
|
||||
|
||||
bool CNonogramNumbers::operator==(const CNonogramNumbers &other) const {
|
||||
return m_rows == other.m_rows && m_columns == other.m_columns;
|
||||
}
|
||||
|
||||
bool CNonogramNumbers::check(bool **data) const {
|
||||
CNonogramNumbers tmp(size(), data);
|
||||
return *this == tmp;
|
||||
}
|
||||
|
||||
bool CNonogramNumbers::check(const QImage & image) const {
|
||||
if (size() != image.size()) return FALSE;
|
||||
CNonogramNumbers tmp(image);
|
||||
return *this == tmp;
|
||||
}
|
||||
|
||||
QDataStream & operator<<(QDataStream & stream, CNonogramNumbers & numbers) {
|
||||
numbers.writeToStream(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
QDataStream & operator>>(QDataStream & stream, CNonogramNumbers & numbers) {
|
||||
numbers.readFromStream(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
CNonogram::CNonogram()
|
||||
: m_Size(0, 0),
|
||||
m_Data(NULL),
|
||||
m_Timeout(0),
|
||||
m_MaximumNumberCount(0) {
|
||||
}
|
||||
|
||||
CNonogram::CNonogram(QSize size) : m_Size(size), m_MaximumNumberCount(0) {
|
||||
init();
|
||||
}
|
||||
|
||||
CNonogram::CNonogram(QImage & image) : m_Data(NULL), m_MaximumNumberCount(0) {
|
||||
loadFromImage(image);
|
||||
}
|
||||
|
||||
CNonogram::CNonogram(QDataStream & stream) : m_Data(NULL), m_MaximumNumberCount(0) {
|
||||
readFromStream(stream);
|
||||
}
|
||||
|
||||
CNonogram::~CNonogram() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void CNonogram::loadFromImage(QImage & image) {
|
||||
if (image.isNull())
|
||||
return;
|
||||
|
||||
resize(image.size());
|
||||
|
||||
for (int iy = 0; iy < m_Size.height(); iy++)
|
||||
for (int ix = 0; ix < m_Size.width(); ix++)
|
||||
m_Data[ix][iy] = !(image.pixel(ix, iy) & 0x00FFFFFF);
|
||||
CNonogram::CNonogram(const NonogramProblem & problem)
|
||||
: m_problem(problem), m_marker(problem.size()) {
|
||||
}
|
||||
|
||||
bool CNonogram::readFromStream(QDataStream & stream) {
|
||||
unsigned char data;
|
||||
QString stringBuffer;
|
||||
qint32 intBuffer;
|
||||
QSize sizeBuffer;
|
||||
|
||||
stream >> stringBuffer; qDebug("reading image: %s", qPrintable(stringBuffer));
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid nonogram");
|
||||
cleanup();
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
if (CNonogram_MAGIC != magic || QDataStream::Ok != stream.status()) {
|
||||
if (QDataStream::ReadPastEnd != stream.status()) stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
stream >> intBuffer; qDebug("width %i", intBuffer);
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid nonogram");
|
||||
cleanup();
|
||||
NonogramProblem problem;
|
||||
NonogramMarker marker;
|
||||
|
||||
if (!problem.readFromStream(stream)) return false;
|
||||
if (!marker.readFromStream(stream)) return false;
|
||||
|
||||
if (problem.size() != marker.size()) {
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
sizeBuffer.setWidth(intBuffer);
|
||||
|
||||
stream >> intBuffer; qDebug("height %i", intBuffer);
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid nonogram");
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
sizeBuffer.setHeight(intBuffer);
|
||||
|
||||
m_Name = stringBuffer;
|
||||
|
||||
stream >> intBuffer; qDebug("timeout %i", intBuffer);
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid nonogram");
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Timeout = intBuffer;
|
||||
|
||||
resize(sizeBuffer);
|
||||
|
||||
for (int x = 0; x < sizeBuffer.width(); x++) {
|
||||
int y = 0;
|
||||
|
||||
while (y < sizeBuffer.height()) {
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid nonogram");
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.readRawData((char *)&data, 1);
|
||||
|
||||
int b;
|
||||
for (b = 0; b < 8; b++) {
|
||||
if (y < sizeBuffer.height())
|
||||
m_Data[x][y] = (bool)(data & 0x80);
|
||||
data = data << 1;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_problem = problem;
|
||||
m_marker = marker;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNonogram::writeToStream(QDataStream & stream) {
|
||||
stream << m_Name;
|
||||
stream << qint32(m_Size.width());
|
||||
stream << qint32(m_Size.height());
|
||||
stream << qint32(m_Timeout);
|
||||
|
||||
unsigned char data;
|
||||
|
||||
for (int x = 0; x < m_Size.width(); x++) {
|
||||
data = 0x00;
|
||||
int y = 0;
|
||||
while (y < m_Size.height()) {
|
||||
int b;
|
||||
for (b = 0; b < 8; b++) {
|
||||
data = data << 1;
|
||||
if (y < m_Size.height() && m_Data[x][y])
|
||||
data |= 0x01;
|
||||
y++;
|
||||
}
|
||||
|
||||
stream.writeRawData((char *)&data, 1);
|
||||
}
|
||||
}
|
||||
void CNonogram::writeToStream(QDataStream & stream) const {
|
||||
stream << CNonogram_MAGIC << m_problem << m_marker;
|
||||
}
|
||||
|
||||
void CNonogram::resize(QSize size) {
|
||||
if (m_Data)
|
||||
cleanup();
|
||||
|
||||
m_Size = size;
|
||||
init();
|
||||
}
|
||||
|
||||
void CNonogram::setPixel(int x, int y, bool value) {
|
||||
m_Data[x][y] = value;
|
||||
}
|
||||
|
||||
void CNonogram::updateNumbers() {
|
||||
m_BlackPixels = 0;
|
||||
int pixelCount;
|
||||
|
||||
m_MaximumNumberCount = 0;
|
||||
|
||||
for (int i = 0; i < m_Size.height(); i++) {
|
||||
m_RowNumbers[i].clear();
|
||||
pixelCount = 0;
|
||||
for (int j = 0; j < m_Size.width(); j++) {
|
||||
if (m_Data[j][i]) {
|
||||
pixelCount++;
|
||||
m_BlackPixels++;
|
||||
}
|
||||
else if (pixelCount) {
|
||||
m_RowNumbers[i] << pixelCount;
|
||||
pixelCount = 0;
|
||||
}
|
||||
}
|
||||
if (pixelCount || m_RowNumbers[i].empty())
|
||||
m_RowNumbers[i] << pixelCount;
|
||||
|
||||
if (m_RowNumbers[i].count() > m_MaximumNumberCount)
|
||||
m_MaximumNumberCount = m_RowNumbers[i].count();
|
||||
}
|
||||
|
||||
for (int j = 0; j < m_Size.width(); j++) {
|
||||
m_ColumnNumbers[j].clear();
|
||||
pixelCount = 0;
|
||||
for (int i = 0; i < m_Size.height(); i++) {
|
||||
if (m_Data[j][i]) {
|
||||
pixelCount++;
|
||||
}
|
||||
else if (pixelCount) {
|
||||
m_ColumnNumbers[j] << pixelCount;
|
||||
pixelCount = 0;
|
||||
}
|
||||
}
|
||||
if (pixelCount || m_ColumnNumbers[j].empty())
|
||||
m_ColumnNumbers[j] << pixelCount;
|
||||
|
||||
if (m_ColumnNumbers[j].count() > m_MaximumNumberCount)
|
||||
m_MaximumNumberCount = m_ColumnNumbers[j].count();
|
||||
}
|
||||
}
|
||||
|
||||
void CNonogram::fill(bool value) {
|
||||
for (int i = 0; i < m_Size.width(); i++)
|
||||
for (int j = 0; j < m_Size.height(); j++)
|
||||
m_Data[i][j] = value;
|
||||
}
|
||||
|
||||
void CNonogram::cleanup() {
|
||||
delete[] m_RowNumbers;
|
||||
delete[] m_ColumnNumbers;
|
||||
|
||||
for (int i = 0; i < m_Size.width(); i++)
|
||||
delete[] m_Data[i];
|
||||
delete[] m_Data;
|
||||
|
||||
m_Data = NULL;
|
||||
}
|
||||
|
||||
void CNonogram::init() {
|
||||
m_Data = new bool *[m_Size.width()];
|
||||
for (int i = 0; i < m_Size.width(); i++)
|
||||
m_Data[i] = new bool[m_Size.height()];
|
||||
|
||||
m_RowNumbers = new NumbersVector[m_Size.height()];
|
||||
m_ColumnNumbers = new NumbersVector[m_Size.width()];
|
||||
}
|
||||
|
||||
QDataStream & operator<<(QDataStream & stream, CNonogram & nonogram) {
|
||||
QDataStream & operator<<(QDataStream& stream, const libqnono::CNonogram& nonogram) {
|
||||
nonogram.writeToStream(stream);
|
||||
return stream;
|
||||
}
|
||||
|
@ -24,106 +24,43 @@
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
|
||||
#include "nonogramproblem.h"
|
||||
#include "nonogrammarker.h"
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace libqnono {
|
||||
class CNonogram;
|
||||
class CNonogramNumbers;
|
||||
|
||||
class CNonogramNumbers {
|
||||
public:
|
||||
typedef QVector<quint16> v_num;
|
||||
typedef QVector<v_num> vv_num;
|
||||
|
||||
CNonogramNumbers(const vv_num & rows, const vv_num & columns);
|
||||
CNonogramNumbers(const CNonogram & source);
|
||||
CNonogramNumbers(QSize size, bool **data);
|
||||
CNonogramNumbers(const QImage & image);
|
||||
|
||||
void calcFromImage(QSize size, bool **data);
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
void writeToStream(QDataStream & stream);
|
||||
|
||||
bool operator==(const CNonogramNumbers &other) const;
|
||||
|
||||
inline const vv_num & rows() const { return m_rows; }
|
||||
inline const vv_num & columns() const { return m_columns; }
|
||||
|
||||
inline QSize size() const { return QSize(m_columns.count(), m_rows.count()); }
|
||||
inline int width() const { return m_columns.count(); }
|
||||
inline int height() const { return m_rows.count(); }
|
||||
|
||||
bool check(bool **data) const;
|
||||
bool check(const QImage & image) const;
|
||||
|
||||
private:
|
||||
vv_num m_rows, m_columns;
|
||||
};
|
||||
|
||||
|
||||
class CNonogram {
|
||||
public:
|
||||
typedef QVector<quint16> NumbersVector;
|
||||
|
||||
CNonogram();
|
||||
CNonogram(QSize size);
|
||||
CNonogram(QImage & image);
|
||||
CNonogram(QDataStream & stream);
|
||||
CNonogram(const CNonogram &other);
|
||||
~CNonogram();
|
||||
CNonogram& operator=(const CNonogram &other);
|
||||
CNonogram(const NonogramProblem & problem);
|
||||
|
||||
void loadFromImage(QImage & image);
|
||||
void resize(QSize size);
|
||||
const NonogramProblem & problem() const { return m_problem; }
|
||||
QString name() const { return m_problem.name(); }
|
||||
quint16 timeout() const { return m_problem.timeout(); }
|
||||
const NonogramImage & solution() const { return m_problem.solution(); }
|
||||
const NonogramNumbers & numbers() const { return m_problem.numbers(); }
|
||||
|
||||
const NonogramMarker & marker() const { return m_marker; }
|
||||
|
||||
QSize size() const { return m_problem.size(); }
|
||||
int width() const { return size().width(); }
|
||||
int height() const { return size().height(); }
|
||||
|
||||
/* returns false if set MARKED on white solution pixel (it still sets the mark) */
|
||||
bool setMark(int x, int y, NonogramMarker::Mark mark);
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
void writeToStream(QDataStream & stream);
|
||||
|
||||
inline QString name() const { return m_Name; }
|
||||
inline void setName(QString value) { m_Name = value; }
|
||||
|
||||
inline quint16 timeout() const { return m_Timeout; }
|
||||
void setTimeout(quint16 value) { m_Timeout = value; }
|
||||
|
||||
inline QSize size() const { return m_Size; }
|
||||
inline int width() const { return m_Size.width(); }
|
||||
inline int height() const { return m_Size.height(); }
|
||||
|
||||
inline bool pixel(int x, int y) const { return m_Data[x][y]; }
|
||||
void setPixel(int x, int y, bool value);
|
||||
|
||||
NumbersVector & rowNumbers(int index) const { return m_RowNumbers[index]; }
|
||||
NumbersVector & columnNumbers(int index) const { return m_ColumnNumbers[index]; }
|
||||
|
||||
inline int maximumNumberCount() const { return m_MaximumNumberCount; }
|
||||
|
||||
inline quint32 blackPixels() const { return m_BlackPixels; }
|
||||
|
||||
void updateNumbers();
|
||||
|
||||
void fill(bool value);
|
||||
|
||||
bool isValid() const { return m_Data; }
|
||||
void writeToStream(QDataStream& stream) const;
|
||||
|
||||
protected:
|
||||
QSize m_Size;
|
||||
|
||||
bool ** m_Data;
|
||||
|
||||
QString m_Name;
|
||||
quint16 m_Timeout;
|
||||
|
||||
NumbersVector * m_RowNumbers;
|
||||
NumbersVector * m_ColumnNumbers;
|
||||
quint32 m_BlackPixels;
|
||||
int m_MaximumNumberCount;
|
||||
|
||||
void cleanup();
|
||||
void init();
|
||||
NonogramProblem m_problem;
|
||||
NonogramMarker m_marker;
|
||||
};
|
||||
|
||||
QDataStream & operator<<(QDataStream & stream, CNonogram & nonogram);
|
||||
QDataStream & operator<<(QDataStream & stream, const CNonogram & nonogram);
|
||||
QDataStream & operator>>(QDataStream & stream, CNonogram & nonogram);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -6,32 +6,6 @@
|
||||
#include <QList>
|
||||
|
||||
namespace libqnono {
|
||||
class CNonogramNumbers;
|
||||
|
||||
class CNonogramSolution {
|
||||
public:
|
||||
CNonogramSolution(QSize size, bool ** data)
|
||||
: m_size(size), m_data(data) {
|
||||
}
|
||||
|
||||
~CNonogramSolution() {
|
||||
int cols = m_size.width();
|
||||
for (int col = 0; col < cols; ++col) delete [] m_data[col];
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
QSize size() const { return m_size; }
|
||||
bool** data() { return m_data; }
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(CNonogramSolution)
|
||||
|
||||
QSize m_size;
|
||||
bool ** m_data;
|
||||
};
|
||||
|
||||
QList<CNonogramSolution*> solve(const CNonogramNumbers & numbers);
|
||||
|
||||
class CNonogram;
|
||||
|
||||
class CNonogramSolver : public QObject {
|
||||
|
208
libqnono/nonogramimage.cpp
Normal file
208
libqnono/nonogramimage.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 Stefan Bühler <stbuehler@web.de> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "nonogramimage.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
namespace libqnono {
|
||||
static const quint64 NonogramImage_DataStream_MAGIC = Q_UINT64_C(0xe47028650d925b33);
|
||||
|
||||
class NonogramImageViewRowColumn : public NonogramImageView {
|
||||
public:
|
||||
NonogramImageViewRowColumn(NonogramImage & img) : m_img(img) { }
|
||||
|
||||
virtual bool pixel(int row, int col) const {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
virtual bool & pixel(int row, int col) {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
private:
|
||||
NonogramImage &m_img;
|
||||
};
|
||||
|
||||
class NonogramImageViewColumnRow : public NonogramImageView {
|
||||
public:
|
||||
NonogramImageViewColumnRow(NonogramImage & img) : m_img(img) { }
|
||||
|
||||
virtual bool pixel(int col, int row) const {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
virtual bool & pixel(int col, int row) {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
private:
|
||||
NonogramImage &m_img;
|
||||
};
|
||||
|
||||
NonogramImage::NonogramImage() : m_size(0,0), m_data(0), m_blackPixels(0) {
|
||||
}
|
||||
|
||||
NonogramImage::NonogramImage(QSize size)
|
||||
: m_size(size), m_blackPixels(0) {
|
||||
int n = m_size.width() * m_size.height();
|
||||
m_data = new bool[n];
|
||||