Browse Source

big ugly commit

- renamed library
- added support for saving loading current game
- added (still crappy) nonogram solver
- some code cleanups
stefan-wip
Oliver Groß 10 years ago
parent
commit
8ebd6975d7
  1. 2
      libqnono/ccrosspackage.cpp
  2. 2
      libqnono/ccrosspackage.h
  3. 8
      libqnono/ccrosspackagelistmodel.cpp
  4. 7
      libqnono/ccrosspackagelistmodel.h
  5. 4
      libqnono/ccrosspackagemodel.cpp
  6. 6
      libqnono/ccrosspackagemodel.h
  7. 50
      libqnono/cnonogram.cpp
  8. 29
      libqnono/cnonogram.h
  9. 532
      libqnono/cnonogramsolver.cpp
  10. 62
      libqnono/cnonogramsolver.h
  11. 341
      qcross/ccrossfieldwidget.cpp
  12. 24
      qcross/ccrossfieldwidget.h
  13. 135
      qcross/cgamewindow.cpp
  14. 15
      qcross/cgamewindow.h
  15. 6
      qcross/cmaskedcrosspackagemodel.cpp
  16. 4
      qcross/cmaskedcrosspackagemodel.h
  17. 27
      qcross/cnewgamedialog.cpp
  18. 8
      qcross/cnewgamedialog.h
  19. 3
      qcross/constants.h
  20. 19
      qcrossedit/cmainwindow.cpp
  21. 6
      qcrossedit/cmainwindow.h

2
libqnono/ccrosspackage.cpp

@ -22,7 +22,7 @@
#include <QDataStream>
#include <QFile>
namespace libqcross {
namespace libqnono {
//public:
CCrossPackage::CCrossPackage() {}
CCrossPackage::CCrossPackage(QString fileName) : m_FileName(fileName) {}

2
libqnono/ccrosspackage.h

@ -23,7 +23,7 @@
#include <QString>
#include <QList>
namespace libqcross {
namespace libqnono {
class CNonogram;
typedef QList<CNonogram *> QMonoPictureList;

8
libqnono/ccrosspackagelistmodel.cpp

@ -22,10 +22,10 @@
#include "cnonogram.h"
#include "ccrosspackagelistmodel.h"
namespace libqcross {
namespace libqnono {
//public:
CCrossPackageListModel::CCrossPackageListModel(QString dataPath, QObject * parent) : QAbstractItemModel(parent),
m_DataPath(dataPath) {
CCrossPackageListModel::CCrossPackageListModel(QString dataPath, QString dataFilter, QObject * parent) : QAbstractItemModel(parent),
m_DataPath(dataPath), m_DataFilter(dataFilter) {
update();
}
@ -43,7 +43,7 @@ namespace libqcross {
CCrossPackage * newPackage;
QDir workDir(m_DataPath);
QStringList fileNames = workDir.entryList(QStringList("*.cpk"), QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
QStringList fileNames = workDir.entryList(QStringList(m_DataFilter), QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
foreach (QString fileName, fileNames) {
newPackage = new CCrossPackage(workDir.filePath(fileName));
if (newPackage->open())

7
libqnono/ccrosspackagelistmodel.h

@ -23,7 +23,7 @@
#include <QAbstractItemModel>
#include <QList>
namespace libqcross {
namespace libqnono {
class CCrossPackage;
/**
@ -32,7 +32,7 @@ namespace libqcross {
class CCrossPackageListModel : public QAbstractItemModel {
Q_OBJECT
public:
CCrossPackageListModel(QString dataPath, QObject * parent = 0);
CCrossPackageListModel(QString dataPath, QString dataFilter, QObject * parent = 0);
~CCrossPackageListModel();
void update();
@ -47,8 +47,9 @@ namespace libqcross {
virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const;
private:
QString m_DataPath;
QString m_DataFilter;
QList<libqcross::CCrossPackage *> m_PackageList;
QList<libqnono::CCrossPackage *> m_PackageList;
};
}

4
libqnono/ccrosspackagemodel.cpp

@ -28,7 +28,7 @@
#define COL_NAME 0
#define COL_TIME 1
namespace libqcross {
namespace libqnono {
QString formatedTime(quint32 seconds, bool showSeconds = false) {
quint32 strippedSeconds = seconds % 60;
quint8 minutes = seconds / 60;
@ -67,7 +67,7 @@ namespace libqcross {
setPackage(NULL);
}
void CCrossPackageModel::setPackage(libqcross::CCrossPackage * package) {
void CCrossPackageModel::setPackage(libqnono::CCrossPackage * package) {
if (m_Package)
m_Package->pictures().clear();

6
libqnono/ccrosspackagemodel.h

@ -22,7 +22,7 @@
#include <QAbstractItemModel>
namespace libqcross {
namespace libqnono {
class CCrossPackage;
/**
@ -34,7 +34,7 @@ namespace libqcross {
CCrossPackageModel(QObject * parent = 0);
~CCrossPackageModel();
void setPackage(libqcross::CCrossPackage * package);
void setPackage(libqnono::CCrossPackage * package);
virtual QModelIndex index(int row, int column = 0, const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
@ -53,7 +53,7 @@ namespace libqcross {
virtual bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex());
virtual bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
protected:
libqcross::CCrossPackage * m_Package;
libqnono::CCrossPackage * m_Package;
};
}

50
libqnono/cnonogram.cpp

@ -20,7 +20,7 @@
#include "cnonogram.h"
#include <QImage>
namespace libqcross {
namespace libqnono {
CNonogram::CNonogram()
: m_Size(0, 0),
m_Data(NULL),
@ -32,6 +32,14 @@ namespace libqcross {
init();
}
CNonogram::CNonogram(QImage & image) : m_Data(NULL), m_MaximumNumberCount(0) {
loadFromImage(image);
}
CNonogram::CNonogram(QDataStream & stream) : m_Data(NULL), m_MaximumNumberCount(0) {
loadFromStream(stream);
}
CNonogram::~CNonogram() {
cleanup();
}
@ -42,9 +50,24 @@ namespace libqcross {
resize(image.size());
for (int i = 0; i < m_Size.width(); i++)
for (int j = 0; j < m_Size.height(); j++)
m_Data[i][j] = !(image.pixel(i, j) & 0x00FFFFFF);
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);
}
void CNonogram::loadFromStream(QDataStream & stream) {
qDebug("loading picture from stream...");
stream >> m_Name; qDebug("m_Name = %s", qPrintable(m_Name));
int imageWidth, imageHeight;
stream >> imageWidth; qDebug("imageWidth = %i", imageWidth);
stream >> imageHeight; qDebug("imageHeight = %i", imageHeight);
resize(QSize(imageWidth, imageHeight));
stream >> m_Timeout;
for (int iy = 0; iy < m_Size.height(); iy++)
for (int ix = 0; ix < m_Size.width(); ix++)
stream >> m_Data[ix][iy];
}
void CNonogram::resize(QSize size) {
@ -130,4 +153,23 @@ namespace libqcross {
m_RowNumbers = new NumbersVector[m_Size.height()];
m_ColumnNumbers = new NumbersVector[m_Size.width()];
}
QDataStream & operator<<(QDataStream & stream, CNonogram & nonogram) {
stream << nonogram.name();
stream << nonogram.width();
stream << nonogram.height();
stream << nonogram.timeout();
for (int iy = 0; iy < nonogram.height(); iy++)
for (int ix = 0; ix < nonogram.width(); ix++)
stream << nonogram.pixel(ix, iy);
return stream;
}
QDataStream & operator>>(QDataStream & stream, CNonogram & nonogram) {
nonogram.loadFromStream(stream);
return stream;
}
}

29
libqnono/cnonogram.h

@ -1,6 +1,6 @@
/***************************************************************************
* Copyright (C) 2008 by Oliver Groß *
* z.o.gross@gmx.de *
* Copyright (C) 2008 by Oliver Groß *
* z.o.gross@gmx.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 *
@ -26,36 +26,40 @@
class QImage;
namespace libqcross {
namespace libqnono {
class CNonogram {
public:
typedef QVector<quint16> NumbersVector;
CNonogram();
CNonogram(QSize size);
CNonogram(QImage & image);
CNonogram(QDataStream & stream);
~CNonogram();
void loadFromImage(QImage & image);
void loadFromStream(QDataStream & stream);
void resize(QSize size);
QString name() const { return m_Name; }
void setName(QString value) { m_Name = value; }
inline QString name() const { return m_Name; }
inline void setName(QString value) { m_Name = value; }
quint16 timeout() const { return m_Timeout; }
inline quint16 timeout() const { return m_Timeout; }
void setTimeout(quint16 value) { m_Timeout = value; }
int width() const { return m_Size.width(); }
int height() const { return m_Size.height(); }
inline QSize size() const { return m_Size; }
inline int width() const { return m_Size.width(); }
inline int height() const { return m_Size.height(); }
bool pixel(int x, int y) const { return m_Data[x][y]; }
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]; }
int maximumNumberCount() const { return m_MaximumNumberCount; }
inline int maximumNumberCount() const { return m_MaximumNumberCount; }
quint32 blackPixels() const { return m_BlackPixels; }
inline quint32 blackPixels() const { return m_BlackPixels; }
void updateNumbers();
@ -79,6 +83,9 @@ namespace libqcross {
void cleanup();
void init();
};
QDataStream & operator<<(QDataStream & stream, CNonogram & nonogram);
QDataStream & operator>>(QDataStream & stream, CNonogram & nonogram);
}
#endif

532
libqnono/cnonogramsolver.cpp

@ -1,12 +1,65 @@
#include "cnonogramsolver.h"
#include "cnonogram.h"
namespace libqcross {
CNonogramSolver::CNonogramSolver(QObject * parent) : QObject(parent), m_Nonogram(NULL), m_OverlayData(NULL) {
#include <QString>
#include <QDebug>
namespace libqnono {
CNonogramSolver::CNonogramSolver(QObject * parent) : QObject(parent),
m_Nonogram(NULL),
m_RowsOverlay(NULL),
m_ColumnsOverlay(NULL),
m_OverlayData(NULL)
{
}
CNonogramSolver::~CNonogramSolver() {
cleanup();
if (m_Nonogram)
cleanup();
}
void CNonogramSolver::setNonogram(CNonogram * nonogram) {
if (m_Nonogram)
cleanup();
m_Nonogram = nonogram;
}
void CNonogramSolver::printOverlays() {
qDebug("row overlays:");
QString debugoutput;
for (int i = 0; i < m_Nonogram->height(); i++) {
if (i) debugoutput += '\n';
for (int e = 0; e < m_RowsOverlay[i].numbersSize; e++) {
debugoutput += ' ' + QString::number(m_RowsOverlay[i].numbers[e].borderLeft)
+ '-' + QString::number(m_RowsOverlay[i].numbers[e].borderRight - 1);
if (m_RowsOverlay[i].numbers[e].finished)
debugoutput += '*';
debugoutput += ' ';
}
}
qDebug(qPrintable(debugoutput));
debugoutput.clear();
qDebug("column overlays:");
for (int i = 0; i < m_Nonogram->width(); i++) {
if (i) debugoutput += '\n';
for (int e = 0; e < m_ColumnsOverlay[i].numbersSize; e++) {
debugoutput += ' ' + QString::number(m_ColumnsOverlay[i].numbers[e].borderLeft)
+ '-' + QString::number(m_ColumnsOverlay[i].numbers[e].borderRight - 1);
if (m_ColumnsOverlay[i].numbers[e].finished)
debugoutput += '*';
debugoutput += ' ';
}
}
qDebug(qPrintable(debugoutput));
}
bool CNonogramSolver::solve() {
@ -14,52 +67,461 @@ namespace libqcross {
return false;
cleanup();
prepare();
qDebug("clearing trivial lines ...");
// nach trivial lösbaren Reihen suchen und diese füllen bzw. abkreuzen
for (int i = 0; i < m_Nonogram->height(); i++) {
if (m_RowsOverlay[i].numbersSize == 1) {
if (m_RowsOverlay[i].numbers[0].entry == 0) {
fillRow(i, CMT_CROSSED);
m_RowsOverlay[i].numbers[0].finished = true;
}
else if (m_RowsOverlay[i].numbers[0].entry == m_Nonogram->height()) {
fillRow(i, CMT_MARKED);
m_RowsOverlay[i].numbers[0].finished = true;
}
}
}
m_OverlayData = new int *[m_Nonogram->width()];
for (int i = 0; i < m_Nonogram->width(); i++) {
m_OverlayData[i] = new int[m_Nonogram->height()];
for (int j = 0; j < m_Nonogram->height(); j++)
m_OverlayData[i][j] = 0;
if (m_ColumnsOverlay[i].numbersSize == 1) {
if (m_ColumnsOverlay[i].numbers[0].entry == 0) {
fillColumn(i, CMT_CROSSED);
m_ColumnsOverlay[i].numbers[0].finished = true;
}
else if (m_ColumnsOverlay[i].numbers[0].entry == m_Nonogram->width()) {
fillColumn(i, CMT_MARKED);
m_ColumnsOverlay[i].numbers[0].finished = true;
}
}
}
for (int i = 0; i < m_Nonogram->height(); i++)
solveRow(i);
printOverlays();
qDebug("processing non-trivial lines...");
bool changed;
int iter = 0;
const int iter_max = 10;
do {
changed = false;
for (int l = 0; l < m_Nonogram->height(); l++)
if (solveLine_ng(l, true))
changed = true;
for (int i = 0; i < m_Nonogram->width(); i++)
if (solveLine_ng(i, false))
changed = true;
iter++;
} while (changed && iter < iter_max);
// qDebug("needed %i iterations", iter);
printOverlays();
return false;
}
bool CNonogramSolver::solveLine_ng(int index, bool isRow) {
LineOverlay * overlay = isRow ? &(m_RowsOverlay[index]) : &(m_ColumnsOverlay[index]);
int rightMax = isRow ? m_Nonogram->width() : m_Nonogram->height();
NumberOverlay * number;
bool result = false;
for (int i = 0; i < overlay->numbersSize; i++) {
number = &(overlay->numbers[i]);
if (number->finished)
continue;
/*
* |-start- null
*
* null -marked-> markierung
* null -crossed-> kreuze
* null -unmarked-> keine
*
* kreuze -marked-> markierung
* kreuze -crossed-> kreuze
* kreuze -unmarked-> keine
*
* keine -marked-> markierung
* keine -crossed-> kreuzstop
* keine -unmarked-> keine
*
* markierung -marked-> markierung
* markierung -crossed-> kreuzstop
* markierung -unmarked-> keine
*
* kreuzstop -stop-|
* fertig -stop-|
*
* null : keine aktion
* fertig : rechten rand stzen, als fertig markieren und stop
* kreuze : linken rand anpassen
* keine : keine aktion
*
* markierung : rechten rand anpassen
* kreuzstop : rechten rand setzen und stop
*
*/
{
enum{ST_NULL = 0, ST_NONE = 1, ST_CROSSES = 2, ST_MARK = 3, ST_FINISHED = 4, ST_CROSS_STOP = 5} state = ST_NULL;
int f = number->borderLeft;
MarkerType marker;
while (f < number->borderRight && state < ST_FINISHED) {
marker = static_cast<MarkerType>(m_OverlayData[isRow ? f : index][isRow ? index : f]);
switch (state) {
case ST_NULL:
case ST_CROSSES:
switch (marker) {
case CMT_MARKED: state = ST_MARK; break;
case CMT_CROSSED: state = ST_CROSSES; break;
case CMT_UNMARKED: state = ST_NONE; break;
default: break;
}
break;
case ST_NONE:
case ST_MARK:
switch (marker) {
case CMT_MARKED: state = ST_MARK; break;
case CMT_CROSSED: state = ST_CROSS_STOP; break;
case CMT_UNMARKED: state = ST_NONE; break;
default: break;
}
break;
case ST_FINISHED:
case ST_CROSS_STOP:
default:
break;
}
switch (state) {
case ST_NONE:
case ST_NULL:
default:
break;
case ST_CROSSES:
number->borderLeft = f + 1;
break;
case ST_MARK:
if ((f < overlay->borderLeftDef(i + i, rightMax)) &&
(overlay->borderRightDef(i - 1, 0) < f) &&
(f + number->entry < number->borderRight))
number->borderRight = f + number->entry;
break;
case ST_FINISHED:
number->finished = true;
number->borderRight = f + number->entry;
if (i + 1 < overlay->numbersSize && overlay->numbers[i+1].borderLeft < number->borderRight + 1)
overlay->numbers[i+1].borderLeft = number->borderRight + 1;
break;
case ST_CROSS_STOP:
number->borderRight = f;
if (i + 1 < overlay->numbersSize && overlay->numbers[i+1].borderLeft < number->borderRight + 1)
overlay->numbers[i+1].borderLeft = number->borderRight + 1;
break;
}
f++;
}
}
/*
sicher markierbare Steine
r - l < 2m - 1
0 < l + m - r + m - 1
0 < (l + m - 1) - (r - m)
*/
for (int k = number->borderRight - number->entry; k < number->borderLeft + number->entry; k++)
mark(isRow ? k : index, isRow ? index : k, CMT_MARKED);
if (!number->finished && number->borderRight - number->borderLeft == number->entry)
number->finished = true;
for (int g = overlay->borderRightDef(i - 1, 0); g < number->borderLeft; g++)
mark(isRow ? g : index, isRow ? index : g, CMT_CROSSED);
for (int g = number->borderRight; g < overlay->borderLeftDef(i + 1, rightMax); g++)
mark(isRow ? g : index, isRow ? index : g, CMT_CROSSED);
result = result || overlay->dirty;
}
return result;
}
void CNonogramSolver::cleanup() {
if (m_OverlayData && m_Nonogram) {
if (m_OverlayData) {
for (int i = 0; i < m_Nonogram->width(); i++)
delete[] m_OverlayData[i];
delete[] m_OverlayData;
}
if (m_RowsOverlay)
delete[] m_RowsOverlay;
if (m_ColumnsOverlay)
delete[] m_ColumnsOverlay;
}
bool CNonogramSolver::solveRow(int /*y*/) {
return false;
// int left = 0;
// int right = m_Nonogram->width()-1;
//
// foreach (int k, m_Nonogram->rowNumbers(y)) {
// if (k == m_Nonogram->width())
// for (int i = 0; )
// m_OverlayData[i][y] = 1;
// }
//
// while (m_OverlayData[right][y] && left < right)
// right++;
//
// if (left == right)
// return true;
//
// if (m_Nonogram->rowNumbers(y)[0] == 0)
// for (int i = left; i <= right; i++)
// m_OverlayData[i][y] = MT_CROSSED;
//
// foreach (int k, m_Nonogram->rowNumbers(y)) {
//
// }
inline void CNonogramSolver::mark(int x, int y, int marker) {
if ((x < 0) || (y < 0) || (x > m_Nonogram->width() - 1) || (y > m_Nonogram->height() - 1) || m_OverlayData[x][y] == marker)
return;
emit markRequested(x, y, marker);
m_OverlayData[x][y] = marker;
m_RowsOverlay[y].dirty = true;
m_ColumnsOverlay[x].dirty = true;
}
inline void CNonogramSolver::fillRow(int index, int marker) {
for (int i = 0; i < m_Nonogram->width(); i++)
mark(i, index, marker);
}
inline void CNonogramSolver::fillColumn(int index, int marker) {
for (int i = 0; i < m_Nonogram->height(); i++)
mark(index, i, marker);
}
void CNonogramSolver::prepare() {
m_OverlayData = new int *[m_Nonogram->width()];
for (int i = 0; i < m_Nonogram->width(); i++) {
m_OverlayData[i] = new int[m_Nonogram->height()];
for (int j = 0; j < m_Nonogram->height(); j++)
m_OverlayData[i][j] = static_cast<int>(CMT_UNMARKED);
}
int leftSum, rightSum, numSize;
m_RowsOverlay = new LineOverlay [m_Nonogram->height()];
for (int i = 0; i < m_Nonogram->height(); i++) {
m_RowsOverlay[i].numbersSize = m_Nonogram->rowNumbers(i).size();
m_RowsOverlay[i].numbers = new NumberOverlay [m_RowsOverlay[i].numbersSize];
numSize = m_Nonogram->rowNumbers(i).size();
leftSum = 0;
for (int j = 0; j < numSize; j++) {
m_RowsOverlay[i].numbers[j].entry = m_Nonogram->rowNumbers(i).at(j);
m_RowsOverlay[i].numbers[j].finished = false;
m_RowsOverlay[i].numbers[j].borderRight = m_Nonogram->width();
m_RowsOverlay[i].numbers[j].borderLeft = leftSum + j;
leftSum += m_RowsOverlay[i].numbers[j].entry;
}
rightSum = 0;
for (int j = numSize - 1; j > -1; j--) {
m_RowsOverlay[i].numbers[j].borderRight -= rightSum;
rightSum += m_RowsOverlay[i].numbers[j].entry + 1;
}
}
m_ColumnsOverlay = new LineOverlay [m_Nonogram->width()];
for (int i = 0; i < m_Nonogram->width(); i++) {
m_ColumnsOverlay[i].numbersSize = m_Nonogram->columnNumbers(i).size();
m_ColumnsOverlay[i].numbers = new NumberOverlay [m_ColumnsOverlay[i].numbersSize];
numSize = m_Nonogram->columnNumbers(i).size();
leftSum = 0;
for (int j = 0; j < numSize; j++) {
m_ColumnsOverlay[i].numbers[j].borderRight = m_Nonogram->height();
m_ColumnsOverlay[i].numbers[j].finished = false;
m_ColumnsOverlay[i].numbers[j].entry = m_Nonogram->columnNumbers(i).at(j);
m_ColumnsOverlay[i].numbers[j].borderLeft = leftSum + j;
leftSum += m_ColumnsOverlay[i].numbers[j].entry;
}
rightSum = 0;
for (int j = numSize - 1; j > -1; j--) {
m_ColumnsOverlay[i].numbers[j].borderRight -= rightSum;
rightSum += m_ColumnsOverlay[i].numbers[j].entry + 1;
}
}
}
inline bool CNonogramSolver::solveLine(int index, bool isRow) {
LineOverlay * overlay = isRow ? &(m_RowsOverlay[index]) : &(m_ColumnsOverlay[index]);
bool result = false;
QString dbgOut;
QTextStream dbg(&dbgOut);
dbg << "overlay ";
if (isRow)
dbg << "row ";
else
dbg << "col ";
dbg << index << ": ";
int length = 0;
int offset = 0;
for (int i = 0; i < overlay->numbersSize; i++) {
if (overlay->numbers[i].finished) {
dbg << "(fin)";
continue;
}
dbg << '(' << overlay->numbers[i].borderLeft << ", " << overlay->numbers[i].borderRight << "; ";
length = safeLength(&(overlay->numbers[i]));
offset = overlay->numbers[i].entry - length + overlay->numbers[i].borderLeft;
dbg << length << ", " << offset << ") ";
if (length > 0) {
if (isRow) {
for (int j = 0; j < length; j++)
mark(offset + j, index, CMT_MARKED);
}
else {
for (int j = 0; j < length; j++)
mark(index, offset + j, CMT_MARKED);
}
if (length == overlay->numbers[i].entry) {
if (isRow) {
mark(overlay->numbers[i].borderLeft - 1, index, CMT_CROSSED);
mark(overlay->numbers[i].borderRight, index, CMT_CROSSED);
}
else {
mark(index, overlay->numbers[i].borderLeft - 1, CMT_CROSSED);
mark(index, overlay->numbers[i].borderRight, CMT_CROSSED);
}
overlay->numbers[i].finished = true;
}
result = true;
}
}
qDebug() << dbgOut;
if (fillGaps(index, isRow))
return true;
return result;
}
inline bool CNonogramSolver::fillGaps(int index, bool isRow) {
LineOverlay * overlay = isRow ? &(m_RowsOverlay[index]) : &(m_ColumnsOverlay[index]);
bool result = false;
if (isRow) {
for (int i = 0; i < overlay->numbersSize + 1; i++) {
for (int j = overlay->borderRightDef(i - 1, 0); j < overlay->borderLeftDef(i, m_Nonogram->width()); j++) {
if (m_OverlayData[j][index] == 0) {
mark(j, index, CMT_CROSSED);
result = true;
}
}
}
}
else {
for (int i = 0; i < overlay->numbersSize + 1; i++) {
for (int j = overlay->borderRightDef(i - 1, 0); j < overlay->borderLeftDef(i, m_Nonogram->height()); j++) {
if (m_OverlayData[index][j] == 0) {
mark(index, j, CMT_CROSSED);
result = true;
}
}
}
}
return result;
}
inline void gtSet(int & target, int value) {
if (target > value)
target = value;
}
inline void ltSet(int & target, int value) {
if (target < value)
target = value;
}
inline void CNonogramSolver::prepareBorders(LineOverlay * overlay) {
for (int i = 1; i < overlay->numbersSize; i++)
ltSet(overlay->numbers[i].borderLeft, overlay->numbers[i - 1].borderLeft + overlay->numbers[i - 1].entry + 1);
for (int i = overlay->numbersSize - 1; i > 0; i--)
gtSet(overlay->numbers[i - 1].borderRight, overlay->numbers[i].borderRight - overlay->numbers[i].entry - 1);
}
inline void CNonogramSolver::updateBorders(int index, bool isRow) {
LineOverlay * overlay = isRow ? &(m_RowsOverlay[index]) : &(m_ColumnsOverlay[index]);
if (!overlay->dirty)
return;
int marker = 0;
for (int i = 0; i < overlay->numbersSize; i++) {
if (overlay->numbers[i].finished)
continue;
int j = overlay->numbers[i].borderLeft;
// int markedCount = 0;
while (j < overlay->numbers[i].borderRight) {
marker = (isRow ? m_OverlayData[j][index] : m_OverlayData[index][j]);
switch (marker) {
case CMT_CROSSED:
if (overlay->numbers[i].borderLeft == j)
overlay->numbers[i].borderLeft = j+1;
else {
if (j - overlay->numbers[i].borderLeft + 1 < j + overlay->numbers[i].entry)
overlay->numbers[i].borderLeft = j+1;
else
overlay->numbers[i].borderRight = j;
if (i < overlay->numbersSize-1)
ltSet(overlay->numbers[i + 1].borderLeft, j);
}
break;
case CMT_MARKED:
gtSet(overlay->numbers[i].borderRight, j + overlay->numbers[i].entry);
/* if (markedCount != -1)
markedCount++;*/
break;
default:
/* if (markedCount > 0) {
markedCount = -1;
ltSet(overlay->numbers[i].borderLeft, j - overlay->numbers[i].entry + 1);
}*/
break;
}
j++;
}
}
overlay->dirty = false;
}
void CNonogramSolver::updateBorders() {
for (int i = 0; i < m_Nonogram->height(); i++)
updateBorders(i, true);
for (int i = 0; i < m_Nonogram->width(); i++)
updateBorders(i, false);
}
}

62
libqnono/cnonogramsolver.h

@ -3,7 +3,7 @@
#include <QObject>
namespace libqcross {
namespace libqnono {
class CNonogram;
class CNonogramSolver : public QObject {
@ -12,27 +12,67 @@ namespace libqcross {
CNonogramSolver(QObject * parent = 0);
~CNonogramSolver();
void setNonogram(CNonogram * nonogram) { m_Nonogram = nonogram; }
void setNonogram(CNonogram * nonogram);
public slots:
bool solve();
signals:
void markRequested(int x, int y, int type);
protected:
// struct Range {
// int left;
// int right;
//
// Range() : left(-1), right(-1) {}
// bool isNULL() { return left == -1; }
// };
enum MarkerType {CMT_UNMARKED = 0, CMT_MARKED = 1, CMT_CROSSED = 2, CMT_NONE = 3};
struct NumberOverlay {
int borderLeft;
int borderRight;
quint16 entry;
bool finished;
};
struct LineOverlay {
NumberOverlay * numbers;
int numbersSize;
bool dirty;
inline int borderLeftDef(int index, int def) {
return (index > -1 && index < numbersSize) ? numbers[index].borderLeft : def;
}
inline int borderRightDef(int index, int def) {
return (index > -1 && index < numbersSize) ? numbers[index].borderRight : def;
}
LineOverlay() : numbers(NULL), dirty(false) {}
~LineOverlay() { if (numbers) delete[] numbers; }
};
CNonogram * m_Nonogram;
LineOverlay * m_RowsOverlay;
LineOverlay * m_ColumnsOverlay;
int ** m_OverlayData;
void cleanup();
void prepare();
inline void mark(int x, int y, int marker);
inline void fillRow(int index, int marker);
inline void fillColumn(int index, int marker);
void printOverlays();
bool solveLine_ng(int index, bool isRow);
inline bool solveLine(int index, bool isRow);
inline bool fillGaps(int index, bool isRow);
inline void prepareBorders(LineOverlay * overlay);
inline void updateBorders(int index, bool isRow);
void updateBorders();
bool solveRow(int y);
bool solveColumn(int x);
inline int safeLength(NumberOverlay * overlay) {
return (overlay->entry * 2) + overlay->borderLeft - overlay->borderRight;
}
};
}

341
qcross/ccrossfieldwidget.cpp

@ -18,7 +18,10 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "ccrossfieldwidget.h"
#include <libqcross/cnonogram.h>
#include <libqnono/cnonogram.h>
#include <libqnono/cnonogramsolver.h>
#include <QPainter>
#include <QMouseEvent>
#include <QLCDNumber>
@ -30,7 +33,7 @@
#include <QApplication>
namespace qcross {
using namespace libqcross;
using namespace libqnono;
int min(int a, int b) {
return (a < b) ? a : b;
@ -54,28 +57,28 @@ namespace qcross {
m_MessageTimeoutId(-1),
m_Clock(NULL),
m_ErrorCount(0),
m_LastErrorMark(-1,-1)
m_LastErrorMark(-1, -1)
{
m_Notifier = new QFrame(this);
m_Notifier->setLineWidth(10);
m_Notifier->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
// m_Notifier = new QFrame(this);
// m_Notifier->setLineWidth(10);
// m_Notifier->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
QPalette notifierPalette(QApplication::palette().color(QPalette::ToolTipBase));
notifierPalette.setColor(QPalette::Text, QApplication::palette().color(QPalette::ToolTipText));
m_Notifier->setPalette(notifierPalette);
m_Notifier->setAutoFillBackground(true);
// m_Notifier->setPalette(notifierPalette);
// m_Notifier->setAutoFillBackground(true);
QHBoxLayout * notifierLayout = new QHBoxLayout(m_Notifier);
m_NotifierIcon = new QLabel(m_Notifier);
m_NotifierText = new QLabel(m_Notifier);
// QHBoxLayout * notifierLayout = new QHBoxLayout(m_Notifier);
// m_NotifierIcon = new QLabel(m_Notifier);
// m_NotifierText = new QLabel(m_Notifier);
m_NotifierText->setWordWrap(true);
notifierLayout->addWidget(m_NotifierIcon);
notifierLayout->addWidget(m_NotifierText);
notifierLayout->addStretch();
m_Notifier->setLayout(notifierLayout);
// m_NotifierText->setWordWrap(true);
// notifierLayout->addWidget(m_NotifierIcon);
// notifierLayout->addWidget(m_NotifierText);
// notifierLayout->addStretch();
// m_Notifier->setLayout(notifierLayout);
m_Notifier->hide();
// m_Notifier->hide();
m_Clock = new QLCDNumber(8, this);
m_Clock->setVisible(m_Picture);
@ -92,12 +95,15 @@ namespace qcross {
CCrossFieldWidget::~CCrossFieldWidget() {
cleanup();
delete m_Clock;
// delete m_Clock;
}
void CCrossFieldWidget::setPicture(CNonogram * picture) {
cleanup();
if (m_Picture)
cleanup();
m_Picture = picture;
if (m_Picture) {
m_LastErrorMark.setX(-1);
m_LastErrorMark.setY(-1);
@ -120,7 +126,7 @@ namespace qcross {
}
m_Clock->setVisible(m_Picture);
}
//beim Laden eines neuen Spiels nach einem beendeten wird das neue nicht automatisch gestartet (Restart nötig)
void CCrossFieldWidget::showMessage(const QString message, int timeout, MessageType type) {
if (!message.isEmpty()) {
Message newMessage;
@ -134,15 +140,85 @@ namespace qcross {
nextMessage();
}
void CCrossFieldWidget::applyState(QDataStream & stream) {
qDebug("trying to apply state...");
qDebug("reading basic stuff:");
// error state
stream >> m_ErrorAware; qDebug("m_ErrorAware = %i", int(m_ErrorAware));
stream >> m_ErrorCount; qDebug("m_ErrorCount = %i", m_ErrorCount);
// time(out)
stream >> m_Time; qDebug("m_Time = %i", m_Time);
updateTimeDisplay();
emit timeChanged(m_Time);
// messages
int messageCount;
stream >> messageCount; qDebug("messageCount = %i", messageCount);
m_Messages.clear();
unsigned char byteBuffer;
for (int i = 0; i < messageCount; i++) {
qDebug("message #%i:", i);
stream >> byteBuffer; qDebug("type = %i", byteBuffer);
m_Message.type = MessageType(byteBuffer);
if (m_Message.type != Invalid) {
stream >> m_Message.timeout; qDebug("timeout = %i", m_Message.timeout);
stream >> m_Message.text; qDebug("text = %s", qPrintable(m_Message.text));
m_Messages.enqueue(m_Message);
}
}
nextMessage();
// overlaydata
for (int iy = 0; iy < m_Picture->height(); iy++) {
for (int ix = 0; ix < m_Picture->width(); ix++) {
stream >> byteBuffer;
m_OverlayData[ix][iy] = MarkerType(byteBuffer);
}
}
}
void CCrossFieldWidget::dumpState(QDataStream & stream) {
if (!m_OverlayData || !m_Picture)
return;
// error state
stream << m_ErrorAware;
stream << m_ErrorCount;
// time(out)
stream << m_Time;
// messages
// actually we should dump the message queue
// for now only dump last displayed
// TODO dump message queue
stream << 1; // dummy message count
stream << (unsigned char)(m_Message.type);
if (m_Message.type != Invalid) {
stream << m_Message.timeout;
stream << m_Message.text;
}
// overlaydata
for (int iy = 0; iy < m_Picture->height(); iy++) {
for (int ix = 0; ix < m_Picture->width(); ix++)
stream << (unsigned char)(m_OverlayData[ix][iy]);
}
}
//public slots
void CCrossFieldWidget::mark(int x, int y, qcross::MarkerType type) {
void CCrossFieldWidget::mark(int x, int y, int type) {
if (type == CMT_NONE || x < 0 || x >= m_Picture->width() || y < 0 || y >= m_Picture->height())
return;
if (type == CMT_MARKED && checkNoError(x, y))
m_OverlayData[x][y] = CMT_MARKED;
else
m_OverlayData[x][y] = type;
MarkerType marker = static_cast<qcross::MarkerType>(type);
execMark(x, y, marker);
}
void CCrossFieldWidget::setTime(int value) {
@ -154,6 +230,7 @@ namespace qcross {
if (m_ErrorAware && !m_Time) {
killTimer(m_TimerId);
m_TimerId = -1;
m_Paused = true;
emit timeUp();
}
@ -182,6 +259,17 @@ namespace qcross {
return;
killTimer(m_TimerId);
m_TimerId = -1;
if (m_Message.type != Invalid) {
m_Messages.enqueue(m_Message);
if (m_MessageTimeoutId != -1) {
killTimer(m_MessageTimeoutId);
m_MessageTimeoutId = -1;
}
}
m_Paused = true;
setEnabled(false);
}
@ -205,31 +293,33 @@ namespace qcross {
if (m_Messages.isEmpty()) {
m_Message.type = Invalid;
// m_Notifier->hide();
// updateMetrics();
update();
return;
}
Message m_Next = m_Messages.dequeue();
m_Message = m_Messages.dequeue();
update();
// switch (next.type) {
// case Information:
// m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxInformation).pixmap(22, 22));
// break;
// case Warning:
// m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(22, 22));
// break;
// case Critical:
// m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(22, 22));
// break;
// default:
// break;
// }
// m_NotifierText->setText(next.text);
// m_Notifier->show();
// updateMetrics();
/* switch (m_Message.type) {
case Information:
m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxInformation).pixmap(22, 22));
break;
case Warning:
m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(22, 22));
break;
case Critical:
m_NotifierIcon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(22, 22));
break;
default:
break;
}
m_NotifierText->setText(m_Message.text);
m_Notifier->show();
updateMetrics();*/
if (m_Message.timeout)
m_MessageTimeoutId = startTimer(m_Message.timeout);
@ -259,8 +349,16 @@ namespace qcross {
}
void CCrossFieldWidget::cleanup() {
killTimer(m_TimerId);
killTimer(m_MessageTimeoutId);
qDebug("m_TimerId = %i m_MessageTimeoutId = %i", m_TimerId, m_MessageTimeoutId);
if (m_TimerId != -1) {
killTimer(m_TimerId);
m_TimerId = -1;
}
if (m_MessageTimeoutId != -1) {
killTimer(m_MessageTimeoutId);
m_MessageTimeoutId = -1;
}
if (m_OverlayData) {
for (int i = 0; i < m_Picture->width(); i++)
@ -321,27 +419,24 @@ namespace qcross {
m_Clock->display(time);
}
void CCrossFieldWidget::execMark(int x, int y) {
/* if (m_OverlayData[x][y] == m_MouseMark)
return;*/
switch (m_MouseMark) {
void CCrossFieldWidget::execMark(int x, int y, MarkerType & marker) {
switch (marker) {
case CMT_MARKED:
if (m_Picture->pixel(x, y)) {
m_RemainingPixels--;
m_OverlayData[x][y] = m_MouseMark;
}
else if (m_ErrorAware) {
m_ErrorCount++;
m_MouseMark = CMT_NONE;
m_OverlayData[x][y] = CMT_CROSSED;
m_LastErrorMark.setX(x);
m_LastErrorMark.setY(y);
emit markError();
m_OverlayData[x][y] = marker;
}
else {
m_ErrorCount++;
m_OverlayData[x][y] = m_MouseMark;
if (m_ErrorAware) {
m_OverlayData[x][y] = CMT_CROSSED;
marker = CMT_NONE;
m_LastErrorMark.setX(x);
m_LastErrorMark.setY(y);
emit markError();
}
else
m_OverlayData[x][y] = marker;
}
break;
case CMT_CROSSED:
@ -354,11 +449,11 @@ namespace qcross {
if (m_OverlayData[x][y] == CMT_MARKED) {
if (m_Picture->pixel(x, y))
m_RemainingPixels++;
else
else if (!m_ErrorAware)
m_ErrorCount--;
}
m_OverlayData[x][y] = m_MouseMark;
m_OverlayData[x][y] = marker;
break;
default:
break;
@ -366,6 +461,7 @@ namespace qcross {
if (!m_RemainingPixels && (m_ErrorAware || !m_ErrorCount)) {
killTimer(m_TimerId);
m_TimerId = -1;
m_Paused = true;
m_Solved = true;
emit solved();
@ -387,6 +483,7 @@ namespace qcross {
int gridWidth = m_RasterWidth - m_HeaderWidth;
int gridHeight = m_RasterHeight - m_HeaderHeight;
// draw background
{
const int delta = 5 * m_BoxSize;
@ -395,76 +492,81 @@ namespace qcross {
int deltaX = delta;
int deltaY;
bool useBase = false;
bool useBaseX;
bool useBaseY = true;
while (offsetX < gridWidth) {
if (gridWidth - offsetX < delta)
deltaX = gridWidth - offsetX;
offsetY = 0;
deltaY = delta;
useBaseX = useBaseY;
while (offsetY < gridHeight) {
painter.fillRect(originX + offsetX, originY + offsetY, deltaX, deltaY,
palette().color(useBase ? QPalette::Base : QPalette::AlternateBase));
if (gridHeight - offsetY < delta)
deltaY = gridHeight - offsetY;
useBase = !useBase;
painter.fillRect(originX + offsetX, originY + offsetY, deltaX, deltaY,
palette().color(useBaseX ? QPalette::Base : QPalette::AlternateBase));
offsetY += delta;
useBaseX = !useBaseX;
if (gridWidth - offsetY < delta)
deltaY = gridWidth - offsetY;
offsetY += deltaY;
}
useBase = !useBase;
offsetX += delta;
useBaseY = !useBaseY;
if (gridWidth - offsetX < delta)
deltaX = gridWidth - offsetX;
offsetX += deltaX;
}
}
painter.setBrush(palette().color(QPalette::Highlight));
QFont font = painter.font();
font.setPixelSize(m_MarkerSize * 0.9);
// draw markers and crosses
{
QPen pen;
pen.setWidth(m_MarkerSize / 8);
painter.setPen(pen);
pen.setWidth(m_MarkerSize / 10);
QRectF markerRect(m_MarkerOffset, m_MarkerOffset, m_MarkerSize, m_MarkerSize);
painter.setPen(pen);
}
QRectF markerRect(m_MarkerOffset, m_MarkerOffset, m_MarkerSize, m_MarkerSize);
for (int i = 0; i < m_Picture->width(); i++) {
originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize;
markerRect.moveLeft(originX + m_MarkerOffset);
QColor markerColor = palette().color(QPalette::Highlight);
QColor errorColor = markerColor;
errorColor.setAlpha(128);
for (int j = 0; j < m_Picture->height(); j++) {
originY = m_OffsetY + m_HeaderHeight + j * m_BoxSize;
markerRect.moveTop(originY + m_MarkerOffset);
painter.setBrush(markerColor);
for (int i = 0; i < m_Picture->width(); i++) {
originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize;
markerRect.moveLeft(originX + m_MarkerOffset);
switch (m_OverlayData[i][j]) {
case CMT_MARKED:
painter.fillRect(markerRect, painter.brush());
break;
case CMT_CROSSED:
if (m_Solved)
break;
for (int j = 0; j < m_Picture->height(); j++) {
originY = m_OffsetY + m_HeaderHeight + j * m_BoxSize;
markerRect.moveTop(originY + m_MarkerOffset);
if (m_ErrorAware && m_ErrorCount && i == m_LastErrorMark.x() && j == m_LastErrorMark.y()) {
switch (m_OverlayData[i][j]) {
case CMT_MARKED:
painter.fillRect(markerRect, painter.brush());
break;
case CMT_CROSSED:
if (m_Solved)
break;
if (m_ErrorAware && m_ErrorCount && i == m_LastErrorMark.x() && j == m_LastErrorMark.y()) {
painter.setBrush(errorColor);
painter.fillRect(markerRect, painter.brush());
painter.setBrush(markerColor);
}
painter.drawLine(markerRect.topLeft(), markerRect.bottomRight());
painter.drawLine(markerRect.bottomLeft(), markerRect.topRight());
break;
default:
break;
}
painter.drawLine(markerRect.topLeft(), markerRect.bottomRight());
painter.drawLine(markerRect.bottomLeft(), markerRect.topRight());
break;
default:
break;
}
}
}
// draw grid lines
painter.setPen(palette().color(QPalette::Dark));
for (int i = m_OffsetX + m_HeaderWidth; i < m_OffsetX + m_RasterWidth; i += m_BoxSize) {
@ -475,9 +577,13 @@ namespace qcross {
painter.drawLine(m_OffsetX + m_HeaderWidth, i, m_OffsetX + m_RasterWidth-1, i);
}
// draw numbers area
if (m_Paused)
painter.setPen(palette().color(QPalette::Shadow));
QFont font = painter.font();
font.setPixelSize(m_MarkerSize * 0.9);
painter.setFont(font);
for (int i = 0; i < m_Picture->width(); i++) {
@ -514,10 +620,19 @@ namespace qcross {
painter.drawLine(m_OffsetX, originY, m_OffsetX + m_RasterWidth-1, originY);
}
// draw message if needed
if (m_Message.type != Invalid) {
painter.drawText(m_HeaderWidth + m_OffsetX, m_HeaderHeight + m_OffsetY, m_Message.text);
QRect boxrect(m_HeaderWidth + m_OffsetX, m_HeaderHeight + m_OffsetY, m_RasterWidth - m_HeaderWidth, m_RasterHeight - m_HeaderHeight);
QColor fillColor = palette().color(QPalette::Base);
fillColor.setAlpha(220);
painter.fillRect(boxrect, fillColor);
font.setPixelSize(font.pixelSize() * 2);
painter.setFont(font);
painter.drawText(boxrect, Qt::AlignVCenter | Qt::AlignCenter | Qt::TextWordWrap, m_Message.text);
}
// painter.drawLine(m_OffsetX, m_OffsetY + m_RasterHeight, m_OffsetX + m_RasterWidth, m_OffsetY + m_RasterHeight);
}
/* void CCrossFieldWidget::updateCell(int x, int y) {
@ -570,12 +685,12 @@ namespace qcross {
m_OffsetX = (width() - m_RasterWidth) / 2;