diff --git a/libqnono/ccrosspackage.cpp b/libqnono/ccrosspackage.cpp index 7a45abd..0e24234 100644 --- a/libqnono/ccrosspackage.cpp +++ b/libqnono/ccrosspackage.cpp @@ -22,7 +22,7 @@ #include #include -namespace libqcross { +namespace libqnono { //public: CCrossPackage::CCrossPackage() {} CCrossPackage::CCrossPackage(QString fileName) : m_FileName(fileName) {} diff --git a/libqnono/ccrosspackage.h b/libqnono/ccrosspackage.h index 1b83575..5625dcf 100644 --- a/libqnono/ccrosspackage.h +++ b/libqnono/ccrosspackage.h @@ -23,7 +23,7 @@ #include #include -namespace libqcross { +namespace libqnono { class CNonogram; typedef QList QMonoPictureList; diff --git a/libqnono/ccrosspackagelistmodel.cpp b/libqnono/ccrosspackagelistmodel.cpp index 1f6575b..adbd0dd 100644 --- a/libqnono/ccrosspackagelistmodel.cpp +++ b/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()) diff --git a/libqnono/ccrosspackagelistmodel.h b/libqnono/ccrosspackagelistmodel.h index 1fa9484..52a8df0 100644 --- a/libqnono/ccrosspackagelistmodel.h +++ b/libqnono/ccrosspackagelistmodel.h @@ -23,7 +23,7 @@ #include #include -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 m_PackageList; + QList m_PackageList; }; } diff --git a/libqnono/ccrosspackagemodel.cpp b/libqnono/ccrosspackagemodel.cpp index 0accb94..0cfefcb 100644 --- a/libqnono/ccrosspackagemodel.cpp +++ b/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(); diff --git a/libqnono/ccrosspackagemodel.h b/libqnono/ccrosspackagemodel.h index 629f360..6f5347d 100644 --- a/libqnono/ccrosspackagemodel.h +++ b/libqnono/ccrosspackagemodel.h @@ -22,7 +22,7 @@ #include -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; }; } diff --git a/libqnono/cnonogram.cpp b/libqnono/cnonogram.cpp index 6a664fc..2b43989 100644 --- a/libqnono/cnonogram.cpp +++ b/libqnono/cnonogram.cpp @@ -20,7 +20,7 @@ #include "cnonogram.h" #include -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; + } } diff --git a/libqnono/cnonogram.h b/libqnono/cnonogram.h index ae3ab11..4ec6fd7 100644 --- a/libqnono/cnonogram.h +++ b/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 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 diff --git a/libqnono/cnonogramsolver.cpp b/libqnono/cnonogramsolver.cpp index 161c786..53f75dc 100644 --- a/libqnono/cnonogramsolver.cpp +++ b/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 +#include + +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(); - 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; + 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; + } + } } - for (int i = 0; i < m_Nonogram->height(); i++) - solveRow(i); + for (int i = 0; i < m_Nonogram->width(); i++) { + 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; + } + } + } + + 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(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; -// } + 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; -// -// 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)) { -// -// } + 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(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); } } diff --git a/libqnono/cnonogramsolver.h b/libqnono/cnonogramsolver.h index b9c80f0..fadd658 100644 --- a/libqnono/cnonogramsolver.h +++ b/libqnono/cnonogramsolver.h @@ -3,7 +3,7 @@ #include -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(); - bool solveRow(int y); - bool solveColumn(int x); + 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(); + + inline int safeLength(NumberOverlay * overlay) { + return (overlay->entry * 2) + overlay->borderLeft - overlay->borderRight; + } }; } diff --git a/qcross/ccrossfieldwidget.cpp b/qcross/ccrossfieldwidget.cpp index 1a08cc6..23493be 100644 --- a/qcross/ccrossfieldwidget.cpp +++ b/qcross/ccrossfieldwidget.cpp @@ -18,7 +18,10 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "ccrossfieldwidget.h" -#include + +#include +#include + #include #include #include @@ -30,7 +33,7 @@ #include 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(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; -// } +/* 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(next.text); + m_NotifierText->setText(m_Message.text); -// m_Notifier->show(); -// updateMetrics(); + 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) { - offsetY = 0; - deltaY = delta; - while (offsetY < gridHeight) { - painter.fillRect(originX + offsetX, originY + offsetY, deltaX, deltaY, - palette().color(useBase ? QPalette::Base : QPalette::AlternateBase)); - - useBase = !useBase; - - offsetY += delta; - - if (gridWidth - offsetY < delta) - deltaY = gridWidth - offsetY; - } - - useBase = !useBase; - - offsetX += delta; - if (gridWidth - offsetX < delta) deltaX = gridWidth - offsetX; + + offsetY = 0; + deltaY = delta; + + useBaseX = useBaseY; + while (offsetY < gridHeight) { + if (gridHeight - offsetY < delta) + deltaY = gridHeight - offsetY; + + painter.fillRect(originX + offsetX, originY + offsetY, deltaX, deltaY, + palette().color(useBaseX ? QPalette::Base : QPalette::AlternateBase)); + + useBaseX = !useBaseX; + + offsetY += deltaY; + } + + useBaseY = !useBaseY; + + 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 / 10); - + pen.setWidth(m_MarkerSize / 8); 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); - for (int j = 0; j < m_Picture->height(); j++) { - originY = m_OffsetY + m_HeaderHeight + j * m_BoxSize; - markerRect.moveTop(originY + m_MarkerOffset); + QRectF markerRect(m_MarkerOffset, m_MarkerOffset, m_MarkerSize, m_MarkerSize); + + QColor markerColor = palette().color(QPalette::Highlight); + QColor errorColor = markerColor; + errorColor.setAlpha(128); + + 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; m_OffsetY = (height() - m_RasterHeight) / 2; - int clockHeight = m_Notifier->isVisible() ? m_HeaderHeight - m_Notifier->sizeHint().height() /* * 3.0 / 4.0 */ : m_HeaderHeight; + int clockHeight = /*m_Notifier->isVisible() ? m_HeaderHeight - m_Notifier->sizeHint().height()*/ /* * 3.0 / 4.0 */ /*:*/ m_HeaderHeight; if (m_Clock) m_Clock->setGeometry(m_OffsetX, m_OffsetY, m_HeaderWidth, clockHeight); - if (m_Notifier && m_Notifier->isVisible()) - m_Notifier->setGeometry(m_OffsetX, m_OffsetY + clockHeight, m_HeaderWidth, m_Notifier->sizeHint().height()); +// if (m_Notifier && m_Notifier->isVisible()) +// m_Notifier->setGeometry(m_OffsetX, m_OffsetY + clockHeight, m_HeaderWidth, m_Notifier->sizeHint().height()); } void CCrossFieldWidget::mousePressEvent(QMouseEvent * event) { @@ -600,7 +715,7 @@ namespace qcross { if (m_OverlayData[pressedX][pressedY] == m_MouseMark) m_MouseMark = CMT_UNMARKED; - execMark(pressedX, pressedY); + execMark(pressedX, pressedY, m_MouseMark); update(); } @@ -621,7 +736,7 @@ namespace qcross { pressedY = (pressedY - m_HeaderHeight) / m_BoxSize; if (m_OverlayData[pressedX][pressedY] != m_MouseMark) { - execMark(pressedX, pressedY); + execMark(pressedX, pressedY, m_MouseMark); update(); } } diff --git a/qcross/ccrossfieldwidget.h b/qcross/ccrossfieldwidget.h index 28f6699..60be903 100644 --- a/qcross/ccrossfieldwidget.h +++ b/qcross/ccrossfieldwidget.h @@ -27,8 +27,9 @@ class QLCDNumber; class QFrame; class QLabel; -namespace libqcross { +namespace libqnono { class CNonogram; + class CNonogramSolver; } namespace qcross { @@ -37,7 +38,7 @@ namespace qcross { class CCrossFieldWidget : public QWidget { Q_OBJECT public: - enum MessageType {Information, Warning, Critical, Invalid}; + enum MessageType {Information = 1, Warning = 2, Critical = 3, Invalid = 0}; bool isPaused() const { return m_Paused; } bool isSolved() const { return m_Solved; } @@ -46,17 +47,20 @@ namespace qcross { int time() const { return m_Time; } qint32 errorCount() const { return m_ErrorCount; } - CCrossFieldWidget(libqcross::CNonogram * picture, QWidget * parent = 0); + CCrossFieldWidget(libqnono::CNonogram * picture, QWidget * parent = 0); ~CCrossFieldWidget(); QPoint lastErrorMark() const { return m_LastErrorMark; } - void setPicture(libqcross::CNonogram * picture); + void setPicture(libqnono::CNonogram * picture); void showMessage(const QString message = QString(), int timeout = 0, MessageType type = Information); + void applyState(QDataStream & stream); + void dumpState(QDataStream & stream); + public slots: - void mark(int x, int y, qcross::MarkerType type); + void mark(int x, int y, int type); void setTime(int value); void setErrorAware(bool value); void setNumbersMarkable(bool value); @@ -81,7 +85,7 @@ namespace qcross { QQueue m_Messages; - libqcross::CNonogram * m_Picture; + libqnono::CNonogram * m_Picture; MarkerType ** m_OverlayData; quint32 m_RemainingPixels; @@ -107,9 +111,9 @@ namespace qcross { QLCDNumber * m_Clock; - QFrame * m_Notifier; - QLabel * m_NotifierIcon; - QLabel * m_NotifierText; +// QFrame * m_Notifier; +// QLabel * m_NotifierIcon; +// QLabel * m_NotifierText; qint32 m_ErrorCount; QPoint m_LastErrorMark; @@ -126,7 +130,7 @@ namespace qcross { void timerEvent(QTimerEvent * event); inline void updateTimeDisplay(); - inline void execMark(int x, int y); + inline void execMark(int x, int y, MarkerType & marker); void paintEvent(QPaintEvent * event); diff --git a/qcross/cgamewindow.cpp b/qcross/cgamewindow.cpp index c5e4b4a..348950d 100644 --- a/qcross/cgamewindow.cpp +++ b/qcross/cgamewindow.cpp @@ -28,16 +28,18 @@ #include #include -#include -#include +#include +#include +#include +#include "constants.h" #include "cgamewindow.h" #include "ccrossfieldwidget.h" #include "cnewgamedialog.h" #include "chighscore.h" namespace qcross { - using namespace libqcross; + using namespace libqnono; //public CGameWindow::CGameWindow(QWidget * parent) : QMainWindow(parent) { m_Highscore = NULL; @@ -49,11 +51,13 @@ namespace qcross { m_Field->setTime(42 * 60 + 23); setCentralWidget(m_Field); -// m_Field->showMessage(tr("Welcome to QCross!")); + m_Field->showMessage(tr("Welcome to QCross!")); createActions(); // statusBar()->show(); + m_Solver = new CNonogramSolver(this); + connect(m_Solver, SIGNAL(markRequested(int, int, int)), m_Field, SLOT(mark(int, int, int))); connect(m_Field, SIGNAL(solved()), this, SLOT(wonGame())); connect(m_Field, SIGNAL(timeUp()), this, SLOT(lostGame())); connect(m_Field, SIGNAL(markError()), this, SLOT(handleErrorMark())); @@ -73,6 +77,9 @@ namespace qcross { currentMenu = menuBar()->addMenu(tr("&Game")); currentMenu->addAction(tr("&New..."), this, SLOT(newGame()), Qt::CTRL + Qt::Key_N); currentMenu->addSeparator(); + currentMenu->addAction(tr("&Save..."), this, SLOT(saveGame()), Qt::CTRL + Qt::Key_N); + currentMenu->addAction(tr("&Load..."), this, SLOT(loadGame()), Qt::CTRL + Qt::Key_N); + currentMenu->addSeparator(); m_RestartGameAction = currentMenu->addAction(tr("&Restart"), this, SLOT(restartGame()), Qt::CTRL + Qt::Key_R); m_RestartGameAction->setEnabled(false); m_PauseGameAction = currentMenu->addAction(tr("&Pause")); @@ -82,8 +89,8 @@ namespace qcross { m_PauseGameAction->setEnabled(false); currentMenu->addSeparator(); currentMenu->addAction(tr("&Quit"), this, SLOT(close()), Qt::CTRL + Qt::Key_Q); -// currentMenu->addSeparator(); -// currentMenu->addAction(tr("debug: win"), this, SLOT(wonGame())); + currentMenu->addSeparator(); + currentMenu->addAction(tr("debug: solve"), this, SLOT(startSolver())); currentToolBar = addToolBar(currentMenu->title()); currentToolBar->addActions(currentMenu->actions()); @@ -97,33 +104,119 @@ namespace qcross { void CGameWindow::newGame() { bool notPaused = !m_Field->isPaused(); if (notPaused) - m_Field->pause(); + pauseGame(true); CNewGameDialog dialog; if (dialog.exec() == QDialog::Accepted) { if (m_Highscore) delete m_Highscore; - m_Field->setPicture(NULL); - if (m_Picture) - delete m_Picture; - m_Highscore = dialog.takeHighscore(); m_PictureIndex = m_Highscore ? dialog.nonogramIndex() : -1; - m_Picture = dialog.takeNonogram(); - m_Picture->updateNumbers(); + CNonogram * newPicture = dialog.takeNonogram(); + newPicture->updateNumbers(); m_PauseGameAction->setEnabled(true); m_RestartGameAction->setEnabled(true); - m_Field->setPicture(m_Picture); + m_Field->setPicture(newPicture); + + if (m_Picture) + delete m_Picture; + + m_Picture = newPicture; m_Field->resume(); m_Field->showMessage(tr("Game started!"), 1000); } - else if (notPaused) - m_Field->resume(); + else + pauseGame(false); + } + + const char magicSaveGameHeader[] = {'C', 'R', 'S', 'V'}; + + void CGameWindow::saveGame() { + if (!m_Field->isPaused()) + m_PauseGameAction->setChecked(true); + + QString fileName = QFileDialog::getSaveFileName(this, tr("Save current game"), + QDir::homePath(), tr("QCross Saved games (*.csg)")); + + qDebug("saving game state file: %s", fileName.toAscii().data()); + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + return; + + QDataStream out(&file); + out.setVersion(QDataStream::Qt_4_0); + + out.writeRawData(magicSaveGameHeader, 4); + + out << int(-1); // debug: m_PicutreIndex + + out << *m_Picture; + + m_Field->dumpState(out); + + m_PauseGameAction->setChecked(false); + } + + void CGameWindow::loadGame() { + if (!m_Field->isPaused()) + m_PauseGameAction->setChecked(true); + + QString fileName = QFileDialog::getOpenFileName(this, tr("Open saved game"), + QDir::homePath(), tr("QCross Saved games (*.csg)")); + + qDebug("opening game state file: %s", fileName.toAscii().data()); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return; + + QDataStream in(&file); + in.setVersion(QDataStream::Qt_4_0); + + char magicHeader[4]; + in.readRawData(magicHeader, 4); + + qDebug("checking magic header"); + for (int i = 0; i < 4; i++) { + if (magicHeader[i] != magicSaveGameHeader[i]) + return; + } + + // picture index + in >> m_PictureIndex; qDebug("m_PictureIndex = %i", m_PictureIndex); + + // delete current highscore + if (m_Highscore) + delete m_Highscore; + + // package name + if (m_PictureIndex > -1) { + QString highscoreFileName, packageName; + in >> packageName; qDebug("packageName = %s", qPrintable(packageName)); + in >> highscoreFileName; qDebug("highscoreFileName = %s", qPrintable(highscoreFileName)); + + // TODO handle save game porting correctly + m_Highscore = new CHighscore(0); + m_Highscore->setFileName(QCROSS_STRING_DATAPATH + QDir::separator() + highscoreFileName); + m_Highscore->open(); + } + + // picture + CNonogram * newPicture = new CNonogram(in); + + m_Field->setPicture(newPicture); + if (m_Picture) + delete m_Picture; + + m_Picture = newPicture; + m_Field->applyState(in); + + m_PauseGameAction->setEnabled(true); + m_PauseGameAction->setChecked(false); } void CGameWindow::restartGame() { @@ -137,8 +230,8 @@ namespace qcross { void CGameWindow::pauseGame(bool value) { if (value) { - m_Field->showMessage(tr("Game paused.")); m_Field->pause(); + m_Field->showMessage(tr("Game paused.")); } else { m_Field->showMessage(tr("Game resumed."), 1000); @@ -172,4 +265,12 @@ namespace qcross { void CGameWindow::about() { QMessageBox::about(this, tr("About"), tr("This is a still unfancy gui for solving nonograms.")); } + + void CGameWindow::startSolver() { + m_Solver->setNonogram(m_Picture); + + restartGame(); + + m_Solver->solve(); + } } diff --git a/qcross/cgamewindow.h b/qcross/cgamewindow.h index 548b80f..fbaf767 100644 --- a/qcross/cgamewindow.h +++ b/qcross/cgamewindow.h @@ -22,8 +22,9 @@ #include -namespace libqcross { +namespace libqnono { class CNonogram; + class CNonogramSolver; } namespace qcross { @@ -40,8 +41,10 @@ namespace qcross { ~CGameWindow(); protected: + libqnono::CNonogramSolver * m_Solver; + CCrossFieldWidget * m_Field; - libqcross::CNonogram * m_Picture; + libqnono::CNonogram * m_Picture; CHighscore * m_Highscore; int m_PictureIndex; @@ -53,8 +56,10 @@ namespace qcross { protected slots: void newGame(); -// void createPackage(); -// void openPackageImage(); + + void saveGame(); + void loadGame(); + void restartGame(); void pauseGame(bool value); @@ -64,6 +69,8 @@ namespace qcross { void handleErrorMark(); void about(); + + void startSolver(); }; } diff --git a/qcross/cmaskedcrosspackagemodel.cpp b/qcross/cmaskedcrosspackagemodel.cpp index f0bf7b2..e3ce704 100644 --- a/qcross/cmaskedcrosspackagemodel.cpp +++ b/qcross/cmaskedcrosspackagemodel.cpp @@ -17,8 +17,8 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include +#include +#include #include "cmaskedcrosspackagemodel.h" #include "chighscore.h" @@ -28,7 +28,7 @@ #define COL_TIME 1 namespace qcross { - using namespace libqcross; + using namespace libqnono; //public: CMaskedCrossPackageModel::CMaskedCrossPackageModel(QObject * parent) : CCrossPackageModel(parent), m_Highscore(NULL) {} diff --git a/qcross/cmaskedcrosspackagemodel.h b/qcross/cmaskedcrosspackagemodel.h index f727601..6111d27 100644 --- a/qcross/cmaskedcrosspackagemodel.h +++ b/qcross/cmaskedcrosspackagemodel.h @@ -20,7 +20,7 @@ #ifndef QCROSS_CMASKEDCROSSPACKAGEMODEL_H #define QCROSS_CMASKEDCROSSPACKAGEMODEL_H -#include +#include #include namespace qcross { @@ -29,7 +29,7 @@ namespace qcross { /** @author Oliver Groß */ - class CMaskedCrossPackageModel : public libqcross::CCrossPackageModel { + class CMaskedCrossPackageModel : public libqnono::CCrossPackageModel { Q_OBJECT public: CMaskedCrossPackageModel(QObject * parent = 0); diff --git a/qcross/cnewgamedialog.cpp b/qcross/cnewgamedialog.cpp index 04068e6..60b60e4 100644 --- a/qcross/cnewgamedialog.cpp +++ b/qcross/cnewgamedialog.cpp @@ -25,10 +25,9 @@ #include #include -#include -// #include -#include -#include +#include +#include +#include #include "cmaskedcrosspackagemodel.h" #include "chighscore.h" #include "cnewgamedialog.h" @@ -36,12 +35,12 @@ #include "common.h" namespace qcross { - using namespace libqcross; + using namespace libqnono; //public: CNewGameDialog::CNewGameDialog(QWidget * parent, Qt::WindowFlags f) : QDialog(parent, f), - m_Highscore(new CHighscore(0)), + m_Highscore(NULL), m_Nonogram(NULL) { ui.setupUi(this); @@ -56,7 +55,7 @@ namespace qcross { ui.buttonBox->addButton(importButton, QDialogButtonBox::ActionRole); ui.buttonBox->addButton(openPicFileButton, QDialogButtonBox::ActionRole); - ui.packageList->setModel(new CCrossPackageListModel(QCROSS_STRING_DATAPATH)); + ui.packageList->setModel(new CCrossPackageListModel(QCROSS_STRING_DATAPATH, "*" QCROSS_STRING_PACKAGE_EXT)); m_PicModel = new CMaskedCrossPackageModel(); m_PicProxyModel = new CMaskedCrossPackageProxyModel(); m_PicProxyModel->setSourceModel(m_PicModel); @@ -84,7 +83,7 @@ namespace qcross { delete m_Nonogram; } -/* libqcross::CNonogram * CNewGameDialog::selectedNonogramData() const { +/* libqnono::CNonogram * CNewGameDialog::selectedNonogramData() const { QModelIndex selected = ui.picList->selectionModel()->selectedIndexes()[0]; return static_cast(selected.internalPointer()); }*/ @@ -94,13 +93,13 @@ namespace qcross { return selected.row(); } - libqcross::CCrossPackage * CNewGameDialog::selectedPackage() const { + libqnono::CCrossPackage * CNewGameDialog::selectedPackage() const { QModelIndex selected = ui.packageList->selectionModel()->selectedIndexes()[0]; return static_cast(selected.internalPointer()); } - libqcross::CNonogram * CNewGameDialog::takeNonogram() { - libqcross::CNonogram * result; + libqnono::CNonogram * CNewGameDialog::takeNonogram() { + libqnono::CNonogram * result; if (m_Nonogram) { result = m_Nonogram; @@ -134,6 +133,9 @@ namespace qcross { QString fileName = QFileDialog::getOpenFileName(this, tr("Select a package to import"), QDir::homePath(), tr("QCross Package (*.cpk)")); if (!fileName.isEmpty()) { +/* if (!File::exists(QCROSS_STRING_DATAPATH)) + QDir::mkpath(QCROSS_STRING_DATAPATH);*/ + QString newFileName = QCROSS_STRING_DATAPATH + QDir::separator() + fileName.section(QDir::separator(), -1); if (QFile::copy(fileName, newFileName)) dynamic_cast(ui.packageList->model())->update(); @@ -169,6 +171,9 @@ namespace qcross { m_PicModel->setHighscore(NULL); m_PicModel->setPackage(package); + if (!m_Highscore) + m_Highscore = new CHighscore(0); + m_Highscore->setFileName(getHighscoreFileName(package->fileName())); if (!m_Highscore->open()) { qDebug("opening highscore file failed. will create a new one when nonogram is solved"); diff --git a/qcross/cnewgamedialog.h b/qcross/cnewgamedialog.h index c33b348..430a398 100644 --- a/qcross/cnewgamedialog.h +++ b/qcross/cnewgamedialog.h @@ -23,7 +23,7 @@ #include #include "ui_picselect.h" -namespace libqcross { +namespace libqnono { class CNonogram; class CCrossPackage; } @@ -43,9 +43,9 @@ namespace qcross { ~CNewGameDialog(); int nonogramIndex() const; - libqcross::CCrossPackage * selectedPackage() const; + libqnono::CCrossPackage * selectedPackage() const; - libqcross::CNonogram * takeNonogram(); + libqnono::CNonogram * takeNonogram(); CHighscore * takeHighscore(); protected slots: void importPackage(); @@ -53,7 +53,7 @@ namespace qcross { private: Ui::picselect ui; CHighscore * m_Highscore; - libqcross::CNonogram * m_Nonogram; + libqnono::CNonogram * m_Nonogram; CMaskedCrossPackageModel * m_PicModel; CMaskedCrossPackageProxyModel * m_PicProxyModel; private slots: diff --git a/qcross/constants.h b/qcross/constants.h index 94bb8fd..aa089f6 100644 --- a/qcross/constants.h +++ b/qcross/constants.h @@ -21,8 +21,9 @@ #ifndef QCROSS_CONSTANTS_H #define QCROSS_CONSTANTS_H -#include +#include #define QCROSS_STRING_DATAPATH ((QDir::homePath() + QDir::separator()) + ".qcross") +#define QCROSS_STRING_PACKAGE_EXT ".cpk" #endif diff --git a/qcrossedit/cmainwindow.cpp b/qcrossedit/cmainwindow.cpp index eba041d..c8127bd 100644 --- a/qcrossedit/cmainwindow.cpp +++ b/qcrossedit/cmainwindow.cpp @@ -16,13 +16,13 @@ #include #include -#include -#include +#include +#include #include "cmainwindow.h" namespace qcrossedit { - using namespace libqcross; + using namespace libqnono; //public: CMainWindow::CMainWindow(QWidget *parent) : QMainWindow(parent), @@ -60,9 +60,11 @@ namespace qcrossedit { //edit menu menu = menuBar()->addMenu("&Edit"); menu->addAction("&Create empty nonogram...", this, SLOT(editCreateEmpty())); - menu->addAction("&Create nonogram from picture...", this, SLOT(editCreateFromPicture())); + menu->addAction("Create nonogram from &picture...", this, SLOT(editCreateFromPicture())); menu->addSeparator(); - menu->addAction("&Delete nongram", this, SLOT(editDelete())); + menu->addAction("&Rename nonogram", this, SLOT(editRename())); + menu->addAction("Set nonogram &time-out", this, SLOT(editSetTimeout())); + menu->addAction("&Delete nonogram", this, SLOT(editDelete())); menu->addSeparator(); menu->addAction("Set Package&name...", this, SLOT(editSetPackageName())); @@ -180,6 +182,13 @@ namespace qcrossedit { } } + void CMainWindow::editRename() { + } + + void CMainWindow::editSetTimeout() { + QInputDialog::getInt(this, tr("Set time-out for selected nonogram (in minutes)")); + } + void CMainWindow::editDelete() { QModelIndexList selectedIndexes = m_PicListView->selectionModel()->selectedIndexes(); foreach (QModelIndex i, selectedIndexes) diff --git a/qcrossedit/cmainwindow.h b/qcrossedit/cmainwindow.h index c65a281..b12c598 100644 --- a/qcrossedit/cmainwindow.h +++ b/qcrossedit/cmainwindow.h @@ -14,7 +14,7 @@ class QTreeView; // class QBitmap; //class QMenu; -namespace libqcross { +namespace libqnono { class CCrossPackage; } @@ -33,7 +33,7 @@ namespace qcrossedit { // QString m_File; bool m_Unsaved; - libqcross::CCrossPackage * m_Package; + libqnono::CCrossPackage * m_Package; /* QBitmap * m_Pics; QString * m_PackName;*/ @@ -51,6 +51,8 @@ namespace qcrossedit { void editCreateEmpty(); void editCreateFromPicture(); + void editRename(); + void editSetTimeout(); void editDelete(); void editSetPackageName();