move game logic to libqnono::CNonogram
This commit is contained in:
parent
281109d1e4
commit
0aa2842b95
@ -7,14 +7,13 @@ include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_S
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
set(SOURCES_MOC_H
|
||||
cnonogramsolver.h
|
||||
cnonogram.h
|
||||
ccrosspackagemodel.h
|
||||
ccrosspackagelistmodel.h
|
||||
)
|
||||
|
||||
set(SOURCES_CPP
|
||||
cnonogram.cpp
|
||||
cnonogramsolver.cpp
|
||||
ccrosspackage.cpp
|
||||
ccrosspackagemodel.cpp
|
||||
ccrosspackagelistmodel.cpp
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "nonogramproblem.h"
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
namespace libqnono {
|
||||
//public:
|
||||
@ -71,10 +72,12 @@ namespace libqnono {
|
||||
}
|
||||
in >> pictureCount;
|
||||
|
||||
QString id(identifier());
|
||||
|
||||
int i;
|
||||
for (i = 0; i < pictureCount && !in.atEnd(); i++) {
|
||||
m_PictureList.append(NonogramProblem());
|
||||
if (!m_PictureList.last().readFromStream(in)) {
|
||||
if (!m_PictureList.last().readFromStream(in, id, i)) {
|
||||
m_PictureList.pop_back();
|
||||
qCritical("invalid package file - invalid picture");
|
||||
return false;
|
||||
@ -174,4 +177,10 @@ namespace libqnono {
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
QString CCrossPackage::identifierFromPath(const QString &fileName) {
|
||||
QString id(QDir::toNativeSeparators(fileName).section(QDir::separator(), -1, -1, QString::SectionSkipEmpty));
|
||||
while (id.endsWith(".cpk", Qt::CaseInsensitive) || id.endsWith(".hsc", Qt::CaseInsensitive)) id.chop(4);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ namespace libqnono {
|
||||
|
||||
void setFileName(QString & value) { m_FileName = value; }
|
||||
QString fileName() const { return m_FileName; }
|
||||
QString identifier() const { return identifierFromPath(m_FileName); }
|
||||
|
||||
void setName(QString value) { m_Name = value; }
|
||||
QString name() const { return m_Name; }
|
||||
@ -52,6 +53,9 @@ namespace libqnono {
|
||||
static CCrossPackage * read(QString fileName);
|
||||
static CCrossPackage * readHeader(QString fileName);
|
||||
|
||||
/* identifier is basename without extension; the function is idempotent and can be used on identifiers and highscore paths too */
|
||||
static QString identifierFromPath(const QString &fileName);
|
||||
|
||||
protected:
|
||||
bool doReadHeader(QDataStream & in);
|
||||
bool doReadData(QDataStream & in);
|
||||
|
@ -21,8 +21,8 @@
|
||||
#include <QIcon>
|
||||
|
||||
#include "ccrosspackage.h"
|
||||
#include "cnonogram.h"
|
||||
#include "ccrosspackagemodel.h"
|
||||
#include "nonogramproblem.h"
|
||||
#include "constants.h"
|
||||
|
||||
#define COL_NAME 0
|
||||
@ -37,23 +37,15 @@ namespace libqnono {
|
||||
QString result;
|
||||
|
||||
if (hours) {
|
||||
if (hours < 10)
|
||||
result += '0';
|
||||
result += QString::number(hours);
|
||||
result += ':';
|
||||
result = QString("%1:").arg(hours, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
minutes %= 60;
|
||||
|
||||
if (minutes < 10)
|
||||
result += '0';
|
||||
result += QString::number(minutes);
|
||||
result += QString("%1").arg(minutes, 2, 10, QLatin1Char('0'));
|
||||
|
||||
if (showSeconds) {
|
||||
result += ':';
|
||||
if (strippedSeconds < 10)
|
||||
result += '0';
|
||||
result += QString::number(strippedSeconds);
|
||||
result += QString(":%1").arg(strippedSeconds, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -93,19 +85,19 @@ namespace libqnono {
|
||||
case COL_NAME:
|
||||
switch (role) {
|
||||
case Qt::DecorationRole:
|
||||
return static_cast<CNonogram *>(index.internalPointer())->timeout() ?
|
||||
return static_cast<NonogramProblem *>(index.internalPointer())->timeout() ?
|
||||
QIcon(LIBQCROSS_ICON_TIMEOUT) :
|
||||
QIcon(LIBQCROSS_ICON_TIMETRIAL);
|
||||
case Qt::EditRole:
|
||||
case Qt::DisplayRole:
|
||||
return static_cast<CNonogram *>(index.internalPointer())->name();
|
||||
return static_cast<NonogramProblem *>(index.internalPointer())->name();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case COL_TIME:
|
||||
if (role == Qt::DisplayRole)
|
||||
return formatedTime(static_cast<CNonogram *>(index.internalPointer())->timeout());
|
||||
return formatedTime(static_cast<NonogramProblem *>(index.internalPointer())->timeout());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -18,46 +18,336 @@
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include "cnonogram.h"
|
||||
#include "nonogramsolver.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace libqnono {
|
||||
static const quint64 CNonogram_MAGIC = Q_UINT64_C(0x35a8bca32006c5a9);
|
||||
static const char CNonogram_HEADER[] = {'C', 'R', 'S', 'V'}; /* readable magic */
|
||||
static const quint64 CNonogram_MAGIC = Q_UINT64_C(0x35a8bca32006c5a9); /* binary magic */
|
||||
|
||||
CNonogram::CNonogram() {
|
||||
CNonogram::CNonogram(QObject *parent) : QObject(parent), m_markedPixels(0), m_time(0), m_errorCount(0), m_preventErrors(true), m_finished(true), m_paused(true) {
|
||||
}
|
||||
|
||||
CNonogram::CNonogram(const NonogramProblem & problem)
|
||||
: m_problem(problem), m_marker(problem.size()) {
|
||||
void CNonogram::start(const NonogramProblem & problem) {
|
||||
m_problem = problem;
|
||||
m_marker.reset(problem.size());
|
||||
m_markedPixels = 0;
|
||||
m_time = problem.timeout();
|
||||
m_errorCount = 0;
|
||||
m_preventErrors = problem.timeout() > 0;
|
||||
m_finished = true;
|
||||
m_paused = true;
|
||||
|
||||
if (!valid()) {
|
||||
emit loaded();
|
||||
emit tick(0);
|
||||
emit timeup();
|
||||
return;
|
||||
}
|
||||
internRestart(true);
|
||||
emit loaded();
|
||||
}
|
||||
|
||||
bool CNonogram::readFromStream(QDataStream & stream) {
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
if (CNonogram_MAGIC != magic || QDataStream::Ok != stream.status()) {
|
||||
if (QDataStream::ReadPastEnd != stream.status()) stream.setStatus(QDataStream::ReadCorruptData);
|
||||
void CNonogram::solve() {
|
||||
if (!valid() || m_finished || m_paused) return;
|
||||
|
||||
QList<NonogramImage> solutions = libqnono::solve(m_problem.numbers());
|
||||
if (!solutions.empty()) {
|
||||
NonogramImage &sol(solutions.first());
|
||||
m_errorCount = 0;
|
||||
//m_time = -1;
|
||||
m_markedPixels = sol.blackPixels();
|
||||
for (int i = 0; i < width(); ++i) {
|
||||
for (int j = 0; j < height(); ++j) {
|
||||
NonogramMarker::Mark mark = sol.pixel(i, j) ? NonogramMarker::MARKED : NonogramMarker::CROSSED;
|
||||
if (mark != m_marker.pixel(i, j)) {
|
||||
m_marker.setPixel(i, j, mark);
|
||||
emit changedMark(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_finished = true;
|
||||
m_timer.stop();
|
||||
emit won(m_time);
|
||||
}
|
||||
}
|
||||
|
||||
bool CNonogram::saveGame(QString fileName) {
|
||||
qDebug("saving game state file: %s", fileName.toAscii().data());
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qDebug("couldn't open file for writing");
|
||||
return false;
|
||||
}
|
||||
|
||||
NonogramProblem problem;
|
||||
NonogramMarker marker;
|
||||
QDataStream out(&file);
|
||||
out.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
if (!problem.readFromStream(stream)) return false;
|
||||
if (!marker.readFromStream(stream)) return false;
|
||||
writeToStream(out);
|
||||
|
||||
if (problem.size() != marker.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNonogram::loadGame(QString fileName) {
|
||||
qDebug("opening game state file: %s", fileName.toAscii().data());
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug("couldn't open file for writing: %s", file.errorString().toAscii().data());
|
||||
return false;
|
||||
}
|
||||
|
||||
QDataStream in(&file);
|
||||
in.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
if (!readFromStream(in)) {
|
||||
switch (in.status()) {
|
||||
case QDataStream::ReadPastEnd:
|
||||
qDebug("Unexpected end of file");
|
||||
break;
|
||||
case QDataStream::ReadCorruptData:
|
||||
default:
|
||||
qDebug("Corrupted save game");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNonogram::readFromStream(QDataStream & stream) {
|
||||
char magicHeader[sizeof(CNonogram_HEADER)];
|
||||
stream.readRawData(magicHeader, sizeof(CNonogram_HEADER));
|
||||
if (QDataStream::Ok != stream.status()) return false;
|
||||
if (0 != memcmp(magicHeader, CNonogram_HEADER, sizeof(CNonogram_HEADER))) {
|
||||
qDebug("Invalid text magic");
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
if (QDataStream::Ok != stream.status()) return false;
|
||||
if (CNonogram_MAGIC != magic) {
|
||||
qDebug("Invalid binary magic");
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString packageIdentifier;
|
||||
int packageIndex;
|
||||
|
||||
stream >> packageIdentifier >> packageIndex;
|
||||
if (QDataStream::Ok != stream.status()) return false;
|
||||
|
||||
NonogramProblem problem;
|
||||
NonogramMarker marker;
|
||||
|
||||
if (!problem.readFromStream(stream, packageIdentifier, packageIndex)) return false;
|
||||
if (!marker.readFromStream(stream)) return false;
|
||||
|
||||
if (problem.size() != marker.size()) {
|
||||
qDebug("Problem size doesn't match maker size");
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
int time, errorCount;
|
||||
bool preventErrors;
|
||||
stream >> time >> errorCount >> preventErrors;
|
||||
if (QDataStream::Ok != stream.status()) return false;
|
||||
|
||||
if (preventErrors && (0 == problem.timeout())) {
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
|
||||
NonogramImage markerImage(marker);
|
||||
const NonogramImage &solution(problem.solution());
|
||||
int markedPixels = markerImage.blackPixels();
|
||||
bool finished;
|
||||
if (preventErrors) {
|
||||
/* check no wrong mark is set */
|
||||
for (int x = 0; x < problem.width(); ++x) {
|
||||
for (int y = 0; y < problem.height(); ++y) {
|
||||
if (markerImage.pixel(x, y) && !solution.pixel(x, y)) {
|
||||
qDebug("Wrong marked pixel and preventErrors is on: %i, %i", x, y);
|
||||
stream.setStatus(QDataStream::ReadCorruptData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finished = (markedPixels == solution.blackPixels());
|
||||
} else {
|
||||
finished = (markedPixels == solution.blackPixels() && problem.numbers().check(markerImage));
|
||||
}
|
||||
|
||||
m_problem = problem;
|
||||
m_marker = marker;
|
||||
m_markedPixels = markedPixels;
|
||||
m_time = time;
|
||||
m_errorCount = errorCount;
|
||||
m_preventErrors = preventErrors;
|
||||
m_finished = finished;
|
||||
m_paused = false;
|
||||
|
||||
if (time == 0) {
|
||||
m_finished = true;
|
||||
emit loaded();
|
||||
emit tick(m_time);
|
||||
emit timeup();
|
||||
} else if (finished) {
|
||||
emit loaded();
|
||||
emit tick(m_time);
|
||||
emit won(m_time);
|
||||
} else {
|
||||
m_timer.start(1000, this);
|
||||
emit tick(m_time);
|
||||
emit loaded();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNonogram::writeToStream(QDataStream & stream) const {
|
||||
stream << CNonogram_MAGIC << m_problem << m_marker;
|
||||
stream.writeRawData(CNonogram_HEADER, sizeof(CNonogram_HEADER));
|
||||
stream << CNonogram_MAGIC;
|
||||
/* problem meta data; not stored by m_problem itself */
|
||||
stream << m_problem.packageIdentifier() << m_problem.packageIndex();
|
||||
stream << m_problem << m_marker;
|
||||
|
||||
stream << m_time << m_errorCount << m_preventErrors;
|
||||
}
|
||||
|
||||
// public slots
|
||||
void CNonogram::setMark(int x, int y, NonogramMarker::Mark mark) {
|
||||
if (x < 0 || x >= width() || y < 0 || y >= height()) return;
|
||||
if (m_finished || m_paused) return;
|
||||
|
||||
if (m_preventErrors) {
|
||||
// if pixel already is marked it must be correct - no reason to undo it
|
||||
if (m_marker.pixel(x, y) == NonogramMarker::MARKED) return;
|
||||
|
||||
if (m_marker.pixel(x, y) == mark) return; /* nothing to change */
|
||||
|
||||
if (mark == NonogramMarker::MARKED && !m_problem.solution().pixel(x, y)) {
|
||||
int penalty;
|
||||
m_marker.setPixel(x, y, NonogramMarker::CROSSED);
|
||||
++m_errorCount;
|
||||
switch (m_errorCount) {
|
||||
case 1: penalty = 2*60; break;
|
||||
case 2: penalty = 4*60; break;
|
||||
default: penalty = 8*60; break;
|
||||
}
|
||||
if (penalty >= m_time) {
|
||||
// lost
|
||||
m_finished = true;
|
||||
m_timer.stop();
|
||||
m_time = 0;
|
||||
emit wrongMark(x, y, penalty);
|
||||
emit changedMark(x, y);
|
||||
emit tick(m_time);
|
||||
emit timeup();
|
||||
} else {
|
||||
m_time -= penalty;
|
||||
emit wrongMark(x, y, penalty);
|
||||
emit changedMark(x, y);
|
||||
emit tick(m_time);
|
||||
}
|
||||
} else {
|
||||
// unmarking is prevented above
|
||||
// if (m_marker.pixel(x, y) == NonogramMarker::MARKED) --m_markedPixels;
|
||||
|
||||
m_marker.setPixel(x, y, mark);
|
||||
if (mark == NonogramMarker::MARKED) ++m_markedPixels;
|
||||
|
||||
if (m_markedPixels == m_problem.solution().blackPixels()) {
|
||||
|
||||
m_finished = true;
|
||||
m_timer.stop();
|
||||
emit changedMark(x, y);
|
||||
emit won(m_time);
|
||||
return;
|
||||
}
|
||||
|
||||
emit changedMark(x, y);
|
||||
}
|
||||
} else {
|
||||
if (m_marker.pixel(x, y) == mark) return; /* nothing to change */
|
||||
|
||||
if (m_marker.pixel(x, y) == NonogramMarker::MARKED) --m_markedPixels;
|
||||
|
||||
m_marker.setPixel(x, y, mark);
|
||||
if (mark == NonogramMarker::MARKED) ++m_markedPixels;
|
||||
|
||||
// blackPixels is an invariant on all solutions for a set of numbers
|
||||
if (m_markedPixels == m_problem.solution().blackPixels() && m_problem.numbers().check(NonogramImage(m_marker))) {
|
||||
m_finished = true;
|
||||
m_timer.stop();
|
||||
emit changedMark(x, y);
|
||||
emit won(m_time);
|
||||
return;
|
||||
}
|
||||
|
||||
emit changedMark(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void CNonogram::restart() {
|
||||
internRestart(false);
|
||||
}
|
||||
|
||||
void CNonogram::pause() {
|
||||
if (m_finished || m_paused) return;
|
||||
m_paused = true;
|
||||
m_timer.stop();
|
||||
emit paused(m_time);
|
||||
}
|
||||
|
||||
void CNonogram::resume() {
|
||||
if (m_finished || !m_paused) return;
|
||||
m_paused = false;
|
||||
m_timer.start(1000, this);
|
||||
emit resumed(m_time);
|
||||
}
|
||||
|
||||
// protected
|
||||
void CNonogram::timerEvent(QTimerEvent * event) {
|
||||
if (m_finished) {
|
||||
m_timer.stop();
|
||||
return;
|
||||
}
|
||||
m_time = m_time + (timeout() ? -1 : 1);
|
||||
if (m_time <= 0) m_time = 0;
|
||||
if (0 == m_time) {
|
||||
// lost
|
||||
m_finished = true;
|
||||
m_timer.stop();
|
||||
emit tick(m_time);
|
||||
emit timeup();
|
||||
} else {
|
||||
emit tick(m_time);
|
||||
}
|
||||
}
|
||||
|
||||
void CNonogram::internRestart(bool newGame) {
|
||||
if (!valid()) {
|
||||
emit tick(0);
|
||||
emit timeup();
|
||||
return;
|
||||
}
|
||||
m_finished = false;
|
||||
m_errorCount = 0;
|
||||
m_time = timeout();
|
||||
m_marker.reset();
|
||||
m_markedPixels = 0;
|
||||
m_paused = false;
|
||||
m_timer.start(1000, this);
|
||||
emit tick(m_time);
|
||||
if (!newGame) emit restarted();
|
||||
}
|
||||
|
||||
QDataStream & operator<<(QDataStream& stream, const libqnono::CNonogram& nonogram) {
|
||||
|
@ -20,9 +20,11 @@
|
||||
#ifndef LIBQCROSS_CMONOPICTURE_H
|
||||
#define LIBQCROSS_CMONOPICTURE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QBasicTimer>
|
||||
|
||||
#include "nonogramproblem.h"
|
||||
#include "nonogrammarker.h"
|
||||
@ -30,12 +32,14 @@
|
||||
class QImage;
|
||||
|
||||
namespace libqnono {
|
||||
class CNonogram {
|
||||
class CNonogram : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QVector<quint16> NumbersVector;
|
||||
|
||||
CNonogram();
|
||||
CNonogram(const NonogramProblem & problem);
|
||||
explicit CNonogram(QObject *parent = 0);
|
||||
|
||||
void start(const NonogramProblem & problem);
|
||||
|
||||
const NonogramProblem & problem() const { return m_problem; }
|
||||
QString name() const { return m_problem.name(); }
|
||||
@ -45,19 +49,59 @@ namespace libqnono {
|
||||
|
||||
const NonogramMarker & marker() const { return m_marker; }
|
||||
|
||||
bool valid() const { return (!size().isEmpty()) && (0 != m_problem.solution().blackPixels()); }
|
||||
|
||||
QSize size() const { return m_problem.size(); }
|
||||
int width() const { return size().width(); }
|
||||
int height() const { return size().height(); }
|
||||
|
||||
/* returns false if set MARKED on white solution pixel (it still sets the mark) */
|
||||
bool setMark(int x, int y, NonogramMarker::Mark mark);
|
||||
int time() const { return m_time; }
|
||||
|
||||
bool isPaused() const { return m_paused; }
|
||||
bool isFinished() const { return m_finished; }
|
||||
|
||||
bool saveGame(QString fileName);
|
||||
bool loadGame(QString fileName);
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
void writeToStream(QDataStream& stream) const;
|
||||
|
||||
public slots:
|
||||
void setMark(int x, int y, NonogramMarker::Mark mark);
|
||||
void restart();
|
||||
void pause();
|
||||
void resume();
|
||||
void solve();
|
||||
|
||||
signals:
|
||||
void loaded(); /* new problem/loaded savegame */
|
||||
|
||||
void won(int score); /* score is the current time */
|
||||
void timeup();
|
||||
|
||||
void wrongMark(int x, int y, int penaltyTime);
|
||||
void changedMark(int x, int y);
|
||||
|
||||
void restarted();
|
||||
void paused(int time);
|
||||
void resumed(int time);
|
||||
void tick(int time);
|
||||
|
||||
protected:
|
||||
virtual void timerEvent(QTimerEvent * event);
|
||||
|
||||
void internRestart(bool newGame);
|
||||
|
||||
QBasicTimer m_timer;
|
||||
|
||||
NonogramProblem m_problem;
|
||||
NonogramMarker m_marker;
|
||||
int m_markedPixels;
|
||||
|
||||
int m_time; /* timeout() == 0: used time, timeout() > 0: remaining time */
|
||||
int m_errorCount;
|
||||
bool m_preventErrors; /* (timeout() > 0) for now - must be false if timeout() == 0 */
|
||||
bool m_finished, m_paused;
|
||||
};
|
||||
|
||||
QDataStream & operator<<(QDataStream & stream, const CNonogram & nonogram);
|
||||
|
@ -1,37 +0,0 @@
|
||||
#include "cnonogramsolver.h"
|
||||
#include "cnonogram.h"
|
||||
|
||||
#include "nonogramsolver.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
namespace libqnono {
|
||||
CNonogramSolver::CNonogramSolver(QObject * parent)
|
||||
: QObject(parent), m_Nonogram(0) {
|
||||
}
|
||||
|
||||
CNonogramSolver::~CNonogramSolver() {
|
||||
}
|
||||
|
||||
void CNonogramSolver::setNonogram(CNonogram * nonogram) {
|
||||
m_Nonogram = nonogram;
|
||||
}
|
||||
|
||||
bool CNonogramSolver::solve() {
|
||||
if (!m_Nonogram) return false;
|
||||
|
||||
QList<NonogramImage> solutions = libqnono::solve(m_Nonogram->numbers());
|
||||
if (!solutions.empty()) {
|
||||
NonogramImage &sol(solutions.first());
|
||||
for (int i = 0; i < m_Nonogram->width(); ++i) {
|
||||
for (int j = 0; j < m_Nonogram->height(); ++j) {
|
||||
emit markRequested(i, j, sol.pixel(i, j) ? CMT_MARKED : CMT_CROSSED);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#ifndef LIBQCROSS_CNONOGRAMSOLVER_H
|
||||
#define LIBQCROSS_CNONOGRAMSOLVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QList>
|
||||
|
||||
namespace libqnono {
|
||||
class CNonogram;
|
||||
|
||||
class CNonogramSolver : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CNonogramSolver(QObject * parent = 0);
|
||||
~CNonogramSolver();
|
||||
|
||||
void setNonogram(CNonogram * nonogram);
|
||||
public slots:
|
||||
bool solve();
|
||||
signals:
|
||||
void markRequested(int x, int y, int type);
|
||||
protected:
|
||||
enum MarkerType {CMT_UNMARKED = 0, CMT_MARKED = 1, CMT_CROSSED = 2, CMT_NONE = 3};
|
||||
|
||||
CNonogram * m_Nonogram;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -24,34 +24,6 @@
|
||||
namespace libqnono {
|
||||
static const quint64 NonogramImage_DataStream_MAGIC = Q_UINT64_C(0xe47028650d925b33);
|
||||
|
||||
class NonogramImageViewRowColumn : public NonogramImageView {
|
||||
public:
|
||||
NonogramImageViewRowColumn(NonogramImage & img) : m_img(img) { }
|
||||
|
||||
virtual bool pixel(int row, int col) const {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
virtual bool & pixel(int row, int col) {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
private:
|
||||
NonogramImage &m_img;
|
||||
};
|
||||
|
||||
class NonogramImageViewColumnRow : public NonogramImageView {
|
||||
public:
|
||||
NonogramImageViewColumnRow(NonogramImage & img) : m_img(img) { }
|
||||
|
||||
virtual bool pixel(int col, int row) const {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
virtual bool & pixel(int col, int row) {
|
||||
return m_img.pixel(col, row);
|
||||
}
|
||||
private:
|
||||
NonogramImage &m_img;
|
||||
};
|
||||
|
||||
NonogramImage::NonogramImage() : m_size(0,0), m_data(0), m_blackPixels(0) {
|
||||
}
|
||||
|
||||
@ -74,6 +46,11 @@ namespace libqnono {
|
||||
load(image);
|
||||
}
|
||||
|
||||
NonogramImage::NonogramImage(const NonogramMarker & marker)
|
||||
: m_size(0, 0), m_data(0), m_blackPixels(0) {
|
||||
load(marker);
|
||||
}
|
||||
|
||||
NonogramImage::~NonogramImage() {
|
||||
delete [] m_data; m_data = 0;
|
||||
}
|
||||
@ -127,14 +104,6 @@ namespace libqnono {
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
NonogramImageView* NonogramImage::viewRowColumn() {
|
||||
return new NonogramImageViewRowColumn(*this);
|
||||
}
|
||||
|
||||
NonogramImageView* NonogramImage::viewColumnRow() {
|
||||
return new NonogramImageViewColumnRow(*this);
|
||||
}
|
||||
|
||||
bool NonogramImage::readFromStream(QDataStream & stream) {
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
|
@ -25,24 +25,18 @@
|
||||
namespace libqnono {
|
||||
class NonogramMarker;
|
||||
|
||||
class NonogramImageView {
|
||||
public:
|
||||
virtual bool pixel(int coord1, int coord2) const = 0;
|
||||
virtual bool & pixel(int coord1, int coord2) = 0;
|
||||
};
|
||||
|
||||
class NonogramImage {
|
||||
public:
|
||||
NonogramImage();
|
||||
NonogramImage(QSize size);
|
||||
explicit NonogramImage(QSize size);
|
||||
NonogramImage(const NonogramImage & other);
|
||||
NonogramImage(const QImage & image);
|
||||
explicit NonogramImage(const QImage & image);
|
||||
explicit NonogramImage(const NonogramMarker & marker);
|
||||
~NonogramImage();
|
||||
NonogramImage& operator=(const NonogramImage & other);
|
||||
bool operator==(const NonogramImage & other) const;
|
||||
|
||||
bool pixel(int x, int y) const { return m_data[y*m_size.width()+x]; }
|
||||
bool & pixel(int x, int y) { return m_data[y*m_size.width()+x]; }
|
||||
void setPixel(int x, int y, bool value) { m_data[y*m_size.width()+x] = value; }
|
||||
void fill(bool value);
|
||||
|
||||
@ -53,9 +47,6 @@ namespace libqnono {
|
||||
int height() const { return m_size.height(); }
|
||||
void resize(QSize size);
|
||||
|
||||
NonogramImageView* viewRowColumn();
|
||||
NonogramImageView* viewColumnRow();
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
void writeToStream(QDataStream & stream) const;
|
||||
|
||||
|
@ -62,6 +62,20 @@ namespace libqnono {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NonogramMarker::reset() {
|
||||
int n = m_size.width() * m_size.height();
|
||||
for (int i = 0; i < n; ++i) m_data[i] = NONE;
|
||||
}
|
||||
|
||||
void NonogramMarker::reset(QSize size) {
|
||||
delete [] m_data; m_data = 0;
|
||||
|
||||
m_size = size;
|
||||
int n = m_size.width() * m_size.height();
|
||||
m_data = new Mark[n];
|
||||
for (int i = 0; i < n; ++i) m_data[i] = NONE;
|
||||
}
|
||||
|
||||
bool NonogramMarker::readFromStream(QDataStream & stream) {
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
|
@ -29,7 +29,7 @@ namespace libqnono {
|
||||
enum Mark { NONE = 0, MARKED = 1, CROSSED = 2 };
|
||||
|
||||
NonogramMarker();
|
||||
NonogramMarker(QSize size);
|
||||
explicit NonogramMarker(QSize size);
|
||||
NonogramMarker(const NonogramMarker& other);
|
||||
~NonogramMarker();
|
||||
NonogramMarker& operator=(const NonogramMarker& other);
|
||||
@ -43,6 +43,9 @@ namespace libqnono {
|
||||
int width() const { return m_size.width(); }
|
||||
int height() const { return m_size.height(); }
|
||||
|
||||
void reset();
|
||||
void reset(QSize size);
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
void writeToStream(QDataStream & stream) const;
|
||||
|
||||
|
@ -33,8 +33,8 @@ namespace libqnono {
|
||||
|
||||
NonogramNumbers();
|
||||
NonogramNumbers(const vv_num & rows, const vv_num & columns);
|
||||
NonogramNumbers(const NonogramImage & image);
|
||||
NonogramNumbers(const QImage & image);
|
||||
explicit NonogramNumbers(const NonogramImage & image);
|
||||
explicit NonogramNumbers(const QImage & image);
|
||||
|
||||
void calcFromImage(const NonogramImage & image);
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace libqnono {
|
||||
m_numbers.calcFromImage(m_solution);
|
||||
}
|
||||
|
||||
bool NonogramProblem::readFromStream(QDataStream & stream) {
|
||||
bool NonogramProblem::readFromStream(QDataStream & stream, QString packageIdentifier, int packageIndex) {
|
||||
quint64 magic;
|
||||
stream >> magic;
|
||||
if (NonogramProblem_MAGIC != magic || QDataStream::Ok != stream.status()) {
|
||||
@ -70,6 +70,8 @@ namespace libqnono {
|
||||
m_name = name;
|
||||
m_timeout = timeout;
|
||||
m_numbers.calcFromImage(m_solution);
|
||||
m_PackageIdentifier = packageIdentifier;
|
||||
m_PackageIndex = packageIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,9 @@ namespace libqnono {
|
||||
const NonogramImage & solution() const { return m_solution; }
|
||||
const NonogramNumbers & numbers() const { return m_numbers; }
|
||||
|
||||
QString packageIdentifier() const { return m_PackageIdentifier; }
|
||||
int packageIndex() const { return m_PackageIndex; }
|
||||
|
||||
QSize size() const { return m_solution.size(); }
|
||||
int width() const { return size().width(); }
|
||||
int height() const { return size().height(); }
|
||||
@ -50,7 +53,7 @@ namespace libqnono {
|
||||
void fill(bool value);
|
||||
void loadFromImage(const NonogramImage & solution);
|
||||
|
||||
bool readFromStream(QDataStream & stream);
|
||||
bool readFromStream(QDataStream & stream, QString packageIdentifier = QString(), int packageIndex = -1);
|
||||
void writeToStream(QDataStream & stream) const;
|
||||
|
||||
private:
|
||||
@ -59,6 +62,10 @@ namespace libqnono {
|
||||
|
||||
QString m_name;
|
||||
quint16 m_timeout;
|
||||
|
||||
/* not saved directly */
|
||||
QString m_PackageIdentifier;
|
||||
int m_PackageIndex;
|
||||
};
|
||||
|
||||
QDataStream & operator<<(QDataStream & stream, const NonogramProblem & problem);
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "ccrossfieldwidget.h"
|
||||
|
||||
#include <libqnono/cnonogram.h>
|
||||
#include <libqnono/cnonogramsolver.h>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
@ -44,19 +43,13 @@ namespace qcross {
|
||||
}
|
||||
|
||||
//public
|
||||
CCrossFieldWidget::CCrossFieldWidget(CNonogram * picture, QWidget * parent) : QWidget(parent),
|
||||
m_Picture(picture),
|
||||
m_OverlayData(NULL),
|
||||
m_MouseMark(CMT_NONE),
|
||||
m_ErrorAware(false),
|
||||
m_NumbersMarkable(true),
|
||||
m_Time(0),
|
||||
m_Paused(true),
|
||||
m_Solved(false),
|
||||
m_TimerId(-1),
|
||||
CCrossFieldWidget::CCrossFieldWidget(QWidget * parent) : QWidget(parent),
|
||||
m_MessageTimeoutId(-1),
|
||||
m_Clock(NULL),
|
||||
m_ErrorCount(0),
|
||||
m_Nonogram(new CNonogram(this)),
|
||||
m_MouseMark(NonogramMarker::NONE),
|
||||
m_MouseDown(false),
|
||||
m_NumbersMarkable(true),
|
||||
m_Clock(new QLCDNumber(8, this)),
|
||||
m_LastErrorMark(-1, -1)
|
||||
{
|
||||
// m_Notifier = new QFrame(this);
|
||||
@ -80,51 +73,27 @@ namespace qcross {
|
||||
|
||||
// m_Notifier->hide();
|
||||
|
||||
m_Clock = new QLCDNumber(8, this);
|
||||
m_Clock->setVisible(m_Picture);
|
||||
|
||||
if (m_Picture) {
|
||||
initialize();
|
||||
m_RemainingPixels = m_Picture->solution().blackPixels();
|
||||
updateTimeDisplay();
|
||||
}
|
||||
m_Clock->setVisible(false);
|
||||
|
||||
setEnabled(false);
|
||||
|
||||
connect(m_Nonogram, SIGNAL(loaded()), SLOT(loaded()));
|
||||
connect(m_Nonogram, SIGNAL(won(int)), SLOT(won(int)));
|
||||
connect(m_Nonogram, SIGNAL(timeup()), SLOT(timeup()));
|
||||
connect(m_Nonogram, SIGNAL(wrongMark(int,int,int)), SLOT(wrongMark(int,int,int)));
|
||||
connect(m_Nonogram, SIGNAL(changedMark(int,int)), SLOT(changedMark(int,int)));
|
||||
connect(m_Nonogram, SIGNAL(restarted()), SLOT(restarted()));
|
||||
connect(m_Nonogram, SIGNAL(paused(int)), SLOT(paused(int)));
|
||||
connect(m_Nonogram, SIGNAL(resumed(int)), SLOT(resumed(int)));
|
||||
connect(m_Nonogram, SIGNAL(tick(int)), SLOT(tick(int)));
|
||||
}
|
||||
|
||||
CCrossFieldWidget::~CCrossFieldWidget() {
|
||||
cleanup();
|
||||
|
||||
// delete m_Clock;
|
||||
qDebug("m_MessageTimeoutId = %i", m_MessageTimeoutId);
|
||||
if (m_MessageTimeoutId != -1) {
|
||||
killTimer(m_MessageTimeoutId);
|
||||
m_MessageTimeoutId = -1;
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::setPicture(CNonogram * picture) {
|
||||
if (m_Picture)
|
||||
cleanup();
|
||||
|
||||
m_Picture = picture;
|
||||
|
||||
if (m_Picture) {
|
||||
m_LastErrorMark.setX(-1);
|
||||
m_LastErrorMark.setY(-1);
|
||||
m_ErrorCount = 0;
|
||||
m_Time = picture->timeout();
|
||||
m_ErrorAware = picture->timeout();
|
||||
m_Solved = false;
|
||||
|
||||
updateTimeDisplay();
|
||||
emit timeChanged(m_Time);
|
||||
|
||||
initialize();
|
||||
|
||||
m_Messages.clear();
|
||||
nextMessage();
|
||||
|
||||
updateMetrics();
|
||||
|
||||
m_RemainingPixels = m_Picture->solution().blackPixels();
|
||||
}
|
||||
m_Clock->setVisible(m_Picture);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::showMessage(const QString message, int timeout, MessageType type) {
|
||||
@ -140,134 +109,54 @@ 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);
|
||||
|
||||
// paused
|
||||
stream >> m_Paused; qDebug("m_Paused = %i", int(m_Paused));
|
||||
setDisabled(m_Paused);
|
||||
|
||||
// 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;
|
||||
|
||||
// paused state
|
||||
stream << m_Paused;
|
||||
|
||||
// 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, int type) {
|
||||
if (type == CMT_NONE || x < 0 || x >= m_Picture->width() || y < 0 || y >= m_Picture->height())
|
||||
return;
|
||||
|
||||
MarkerType marker = static_cast<qcross::MarkerType>(type);
|
||||
|
||||
execMark(x, y, marker);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::setTime(int value) {
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
|
||||
if (m_Time != value) {
|
||||
m_Time = value;
|
||||
|
||||
if (m_ErrorAware && !m_Time) {
|
||||
killTimer(m_TimerId);
|
||||
m_TimerId = -1;
|
||||
m_Paused = true;
|
||||
emit timeUp();
|
||||
}
|
||||
|
||||
updateTimeDisplay();
|
||||
|
||||
emit timeChanged(m_Time);
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::setErrorAware(bool value) {
|
||||
m_ErrorAware = value;
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::setNumbersMarkable(bool value) {
|
||||
m_NumbersMarkable = value;
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::start() {
|
||||
reset();
|
||||
resume();
|
||||
//protected slots:
|
||||
void CCrossFieldWidget::loaded() {
|
||||
m_LastErrorMark = QPoint(-1, -1);
|
||||
initialize();
|
||||
updateTimeDisplay();
|
||||
m_Messages.clear();
|
||||
if (m_Nonogram->valid()) {
|
||||
if (m_Nonogram->time() == m_Nonogram->timeout()) {
|
||||
showMessage(tr("Game started!"), 1000);
|
||||
} else {
|
||||
showMessage(tr("Save game loaded!"), 1000);
|
||||
}
|
||||
setEnabled(true);
|
||||
} else {
|
||||
nextMessage();
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::pause() {
|
||||
if (m_Solved)
|
||||
return;
|
||||
void CCrossFieldWidget::won(int score) {
|
||||
showMessage(tr("Congratulations! You've solved the puzzle."));
|
||||
}
|
||||
|
||||
killTimer(m_TimerId);
|
||||
m_TimerId = -1;
|
||||
void CCrossFieldWidget::timeup() {
|
||||
showMessage(tr("Too bad! Time's up."), 0, CCrossFieldWidget::Critical);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::wrongMark(int x, int y, int penaltyTime) {
|
||||
m_LastErrorMark = QPoint(x, y);
|
||||
showMessage(tr("Sorry this was not correct: -%1min").arg(penaltyTime/60), 1000, CCrossFieldWidget::Warning);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::changedMark(int x, int y) {
|
||||
update();
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::restarted() {
|
||||
m_Messages.clear();
|
||||
m_LastErrorMark = QPoint(-1, -1);
|
||||
showMessage(tr("Game restarted."), 1000);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::paused(int time) {
|
||||
if (m_Message.type != Invalid) {
|
||||
m_Messages.enqueue(m_Message);
|
||||
|
||||
@ -276,19 +165,18 @@ namespace qcross {
|
||||
m_MessageTimeoutId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
m_Paused = true;
|
||||
setEnabled(false);
|
||||
showMessage(tr("Game paused."));
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::resume() {
|
||||
if (m_Solved)
|
||||
return;
|
||||
|
||||
m_Paused = false;
|
||||
void CCrossFieldWidget::resumed(int time) {
|
||||
setEnabled(true);
|
||||
showMessage(tr("Game resumed."), 1000);
|
||||
update();
|
||||
m_TimerId = startTimer(1000);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::tick(int time) {
|
||||
updateTimeDisplay();
|
||||
}
|
||||
|
||||
//protected
|
||||
@ -333,78 +221,34 @@ namespace qcross {
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::initialize() {
|
||||
m_OverlayData = new MarkerType *[m_Picture->width()];
|
||||
for (int i = 0; i < m_Picture->width(); i++) {
|
||||
m_OverlayData[i] = new MarkerType[m_Picture->height()];
|
||||
for (int j = 0; j < m_Picture->height(); j++)
|
||||
m_OverlayData[i][j] = CMT_UNMARKED;
|
||||
}
|
||||
|
||||
int minimumW = max(m_Picture->numbers().maximumNumberCount()+1, 5);
|
||||
int minimumW = max(m_Nonogram->numbers().maximumNumberCount()+1, 5);
|
||||
int minimumH = minimumW;
|
||||
|
||||
minimumW += m_Picture->width();
|
||||
minimumH += m_Picture->height();
|
||||
minimumW += m_Nonogram->width();
|
||||
minimumH += m_Nonogram->height();
|
||||
|
||||
int minimumBoxSize = max(fontMetrics().width(QString::number(m_Picture->height())), fontMetrics().width(QString::number(m_Picture->width())));
|
||||
int minimumBoxSize = max(fontMetrics().width(QString::number(m_Nonogram->height())), fontMetrics().width(QString::number(m_Nonogram->width())));
|
||||
minimumBoxSize = max(minimumBoxSize, fontInfo().pixelSize() * 1.5);
|
||||
|
||||
minimumW *= minimumBoxSize;
|
||||
minimumH *= minimumBoxSize;
|
||||
|
||||
setMinimumSize(minimumW, minimumH);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::cleanup() {
|
||||
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++)
|
||||
delete[] m_OverlayData[i];
|
||||
delete[] m_OverlayData;
|
||||
m_OverlayData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::reset() {
|
||||
m_Messages.clear();
|
||||
nextMessage();
|
||||
|
||||
for (int i = 0; i < m_Picture->width(); i++) {
|
||||
for (int j = 0; j < m_Picture->height(); j++)
|
||||
m_OverlayData[i][j] = CMT_UNMARKED;
|
||||
}
|
||||
|
||||
m_Solved = false;
|
||||
m_ErrorCount = 0;
|
||||
m_LastErrorMark.setX(-1);
|
||||
m_LastErrorMark.setY(-1);
|
||||
m_RemainingPixels = m_Picture->solution().blackPixels();
|
||||
}
|
||||
|
||||
bool CCrossFieldWidget::checkNoError(int x, int y) {
|
||||
return (m_OverlayData[x][y] == CMT_MARKED) || m_Picture->solution().pixel(x, y);
|
||||
updateMetrics();
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::timerEvent(QTimerEvent * event) {
|
||||
if (event->timerId() == m_TimerId)
|
||||
setTime(m_Time + (m_ErrorAware ? -1 : +1));
|
||||
else if (event->timerId() == m_MessageTimeoutId)
|
||||
if (event->timerId() == m_MessageTimeoutId) {
|
||||
nextMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::updateTimeDisplay() {
|
||||
quint8 seconds = m_Time % 60;
|
||||
quint8 minutes = m_Time / 60;
|
||||
m_Clock->setVisible(m_Nonogram->valid());
|
||||
|
||||
int secs = m_Nonogram->time();
|
||||
quint8 seconds = secs % 60;
|
||||
quint8 minutes = secs / 60;
|
||||
quint8 hours = minutes / 60;
|
||||
minutes %= 60;
|
||||
|
||||
@ -426,59 +270,8 @@ namespace qcross {
|
||||
m_Clock->display(time);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::execMark(int x, int y, MarkerType & marker) {
|
||||
if (m_Solved) return;
|
||||
|
||||
switch (marker) {
|
||||
case CMT_MARKED:
|
||||
if (m_Picture->solution().pixel(x, y)) {
|
||||
m_RemainingPixels--;
|
||||
m_OverlayData[x][y] = marker;
|
||||
}
|
||||
else {
|
||||
m_ErrorCount++;
|
||||
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:
|
||||
case CMT_UNMARKED:
|
||||
if (m_ErrorAware && x == m_LastErrorMark.x() && y == m_LastErrorMark.y()) {
|
||||
m_LastErrorMark.setX(-1);
|
||||
m_LastErrorMark.setY(-1);
|
||||
}
|
||||
|
||||
if (m_OverlayData[x][y] == CMT_MARKED) {
|
||||
if (m_Picture->solution().pixel(x, y))
|
||||
m_RemainingPixels++;
|
||||
else if (!m_ErrorAware)
|
||||
m_ErrorCount--;
|
||||
}
|
||||
|
||||
m_OverlayData[x][y] = marker;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_RemainingPixels && (m_ErrorAware || !m_ErrorCount)) {
|
||||
killTimer(m_TimerId);
|
||||
m_TimerId = -1;
|
||||
m_Paused = true;
|
||||
m_Solved = true;
|
||||
emit solved();
|
||||
}
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::paintEvent(QPaintEvent *) {
|
||||
if (!m_Picture) {
|
||||
if (!m_Nonogram) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -541,23 +334,23 @@ namespace qcross {
|
||||
errorColor.setAlpha(128);
|
||||
|
||||
painter.setBrush(markerColor);
|
||||
for (int i = 0; i < m_Picture->width(); i++) {
|
||||
for (int i = 0; i < m_Nonogram->width(); i++) {
|
||||
originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize;
|
||||
markerRect.moveLeft(originX + m_MarkerOffset);
|
||||
|
||||
for (int j = 0; j < m_Picture->height(); j++) {
|
||||
for (int j = 0; j < m_Nonogram->height(); j++) {
|
||||
originY = m_OffsetY + m_HeaderHeight + j * m_BoxSize;
|
||||
markerRect.moveTop(originY + m_MarkerOffset);
|
||||
|
||||
switch (m_OverlayData[i][j]) {
|
||||
case CMT_MARKED:
|
||||
switch (m_Nonogram->marker().pixel(i, j)) {
|
||||
case NonogramMarker::MARKED:
|
||||
painter.fillRect(markerRect, painter.brush());
|
||||
break;
|
||||
case CMT_CROSSED:
|
||||
if (m_Solved)
|
||||
case NonogramMarker::CROSSED:
|
||||
if (m_Nonogram->isFinished())
|
||||
break;
|
||||
|
||||
if (m_ErrorAware && m_ErrorCount && i == m_LastErrorMark.x() && j == m_LastErrorMark.y()) {
|
||||
if (i == m_LastErrorMark.x() && j == m_LastErrorMark.y()) {
|
||||
painter.setBrush(errorColor);
|
||||
painter.fillRect(markerRect, painter.brush());
|
||||
painter.setBrush(markerColor);
|
||||
@ -585,7 +378,7 @@ namespace qcross {
|
||||
}
|
||||
|
||||
// draw numbers area
|
||||
if (m_Paused)
|
||||
if (m_Nonogram->isPaused())
|
||||
painter.setPen(palette().color(QPalette::Shadow));
|
||||
|
||||
QFont font = painter.font();
|
||||
@ -593,13 +386,13 @@ namespace qcross {
|
||||
|
||||
painter.setFont(font);
|
||||
|
||||
for (int i = 0; i < m_Picture->width(); i++) {
|
||||
for (int i = 0; i < m_Nonogram->width(); i++) {
|
||||
originX = m_OffsetX + m_HeaderWidth + i * m_BoxSize;
|
||||
painter.fillRect(originX, m_OffsetY, m_BoxSize, m_HeaderHeight, palette().color((i % 2) ? QPalette::AlternateBase : QPalette::Base));
|
||||
if (!m_Paused) {
|
||||
if (!m_Nonogram->isPaused()) {
|
||||
painter.setPen(palette().color(QPalette::WindowText));
|
||||
int j = m_Picture->numbers().columns()[i].count();;
|
||||
foreach (quint16 block, m_Picture->numbers().columns()[i]) {
|
||||
int j = m_Nonogram->numbers().columns()[i].count();;
|
||||
foreach (quint16 block, m_Nonogram->numbers().columns()[i]) {
|
||||
originY = m_OffsetY + m_HeaderHeight - j * m_BoxSize;
|
||||
|
||||
painter.drawText(originX, originY, m_BoxSize, m_BoxSize,
|
||||
@ -613,13 +406,13 @@ namespace qcross {
|
||||
}
|
||||
// painter.drawLine(m_OffsetX + m_RasterWidth, m_OffsetY, m_OffsetX + m_RasterWidth, m_OffsetY + m_RasterHeight);
|
||||
|
||||
for (int i = 0; i < m_Picture->height(); i++) {
|
||||
for (int i = 0; i < m_Nonogram->height(); i++) {
|
||||
originY = m_OffsetY + m_HeaderHeight + i * m_BoxSize;
|
||||
painter.fillRect(m_OffsetX, originY, m_HeaderWidth, m_BoxSize, palette().color((i % 2) ? QPalette::AlternateBase : QPalette::Base));
|
||||
if (!m_Paused) {
|
||||
if (!m_Nonogram->isPaused()) {
|
||||
painter.setPen(palette().color(QPalette::WindowText));
|
||||
int j = m_Picture->numbers().rows()[i].count();
|
||||
foreach (quint16 block, m_Picture->numbers().rows()[i]) {
|
||||
int j = m_Nonogram->numbers().rows()[i].count();
|
||||
foreach (quint16 block, m_Nonogram->numbers().rows()[i]) {
|
||||
originX = m_OffsetX + m_HeaderWidth - j * m_BoxSize;
|
||||
painter.drawText(originX, originY, m_BoxSize, m_BoxSize,
|
||||
Qt::AlignVCenter | Qt::AlignCenter, QString::number(block));
|
||||
@ -666,19 +459,16 @@ namespace qcross {
|
||||
}*/
|
||||
|
||||
void CCrossFieldWidget::resizeEvent(QResizeEvent * /*event*/) {
|
||||
if (!m_Picture)
|
||||
return;
|
||||
|
||||
updateMetrics();
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::updateMetrics() {
|
||||
m_HeaderWidth = max(m_Picture->numbers().maximumNumberCount()+1, 5);
|
||||
m_HeaderWidth = max(m_Nonogram->numbers().maximumNumberCount()+1, 5);
|
||||
/* m_HeaderWidth = max(m_Picture->width() / 2 + 1, m_Picture->height() / 2 + 1);
|
||||
m_HeaderWidth = m_Picture->width() / 2 + 1;
|
||||
m_HeaderHeight = m_Picture->height() / 2 + 1;*/
|
||||
|
||||
int fieldsize = min((m_Picture->width() + m_HeaderWidth), (m_Picture->height() + m_HeaderWidth));
|
||||
int fieldsize = min((m_Nonogram->width() + m_HeaderWidth), (m_Nonogram->height() + m_HeaderWidth));
|
||||
int widgetsize = min(width(), height());
|
||||
|
||||
m_BoxSize = ((double)widgetsize / fieldsize);
|
||||
@ -690,8 +480,8 @@ namespace qcross {
|
||||
m_HeaderWidth *= m_BoxSize;
|
||||
m_HeaderHeight = m_HeaderWidth;
|
||||
|
||||
m_RasterWidth = m_BoxSize * m_Picture->width() + m_HeaderWidth;
|
||||
m_RasterHeight = m_BoxSize * m_Picture->height() + m_HeaderHeight;
|
||||
m_RasterWidth = m_BoxSize * m_Nonogram->width() + m_HeaderWidth;
|
||||
m_RasterHeight = m_BoxSize * m_Nonogram->height() + m_HeaderHeight;
|
||||
|
||||
m_OffsetX = (width() - m_RasterWidth) / 2;
|
||||
m_OffsetY = (height() - m_RasterHeight) / 2;
|
||||
@ -702,12 +492,10 @@ namespace qcross {
|
||||
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());
|
||||
update();
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::mousePressEvent(QMouseEvent * event) {
|
||||
if (m_Paused)
|
||||
return;
|
||||
|
||||
int pressedX = event->x() - m_OffsetX;
|
||||
int pressedY = event->y() - m_OffsetY;
|
||||
|
||||
@ -717,24 +505,23 @@ namespace qcross {
|
||||
pressedX = (pressedX - m_HeaderWidth) / m_BoxSize;
|
||||
pressedY = (pressedY - m_HeaderHeight) / m_BoxSize;
|
||||
|
||||
m_MouseLastX = pressedX; m_MouseLastY = pressedY;
|
||||
|
||||
if (event->button() == Qt::RightButton)
|
||||
m_MouseMark = CMT_CROSSED;
|
||||
m_MouseMark = NonogramMarker::CROSSED;
|
||||
else
|
||||
m_MouseMark = CMT_MARKED;
|
||||
m_MouseMark = NonogramMarker::MARKED;
|
||||
|
||||
if (m_OverlayData[pressedX][pressedY] == m_MouseMark)
|
||||
m_MouseMark = CMT_UNMARKED;
|
||||
m_MouseDown = true;
|
||||
|
||||
execMark(pressedX, pressedY, m_MouseMark);
|
||||
update();
|
||||
if (m_Nonogram->marker().pixel(pressedX, pressedY) == m_MouseMark)
|
||||
m_MouseMark = NonogramMarker::NONE;
|
||||
|
||||
m_Nonogram->setMark(pressedX, pressedY, m_MouseMark);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::mouseMoveEvent(QMouseEvent * event) {
|
||||
if (m_Paused)
|
||||
return;
|
||||
|
||||
if (m_MouseMark == CMT_NONE)
|
||||
if (!m_MouseDown)
|
||||
return;
|
||||
|
||||
int pressedX = event->x() - m_OffsetX;
|
||||
@ -746,13 +533,13 @@ namespace qcross {
|
||||
pressedX = (pressedX - m_HeaderWidth) / m_BoxSize;
|
||||
pressedY = (pressedY - m_HeaderHeight) / m_BoxSize;
|
||||
|
||||
if (m_OverlayData[pressedX][pressedY] != m_MouseMark) {
|
||||
execMark(pressedX, pressedY, m_MouseMark);
|
||||
update();
|
||||
}
|
||||
if (pressedX == m_MouseLastX && pressedY == m_MouseLastY) return;
|
||||
m_MouseLastX = pressedX; m_MouseLastY = pressedY;
|
||||
|
||||
m_Nonogram->setMark(pressedX, pressedY, m_MouseMark);
|
||||
}
|
||||
|
||||
void CCrossFieldWidget::mouseReleaseEvent(QMouseEvent *) {
|
||||
m_MouseMark = CMT_NONE;
|
||||
m_MouseDown = false;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <QWidget>
|
||||
#include <QQueue>
|
||||
|
||||
#include <libqnono/nonogrammarker.h>
|
||||
|
||||
class QLCDNumber;
|
||||
class QFrame;
|
||||
class QLabel;
|
||||
@ -33,46 +35,35 @@ namespace libqnono {
|
||||
}
|
||||
|
||||
namespace qcross {
|
||||
enum MarkerType {CMT_UNMARKED = 0, CMT_MARKED = 1, CMT_CROSSED = 2, CMT_NONE = 3};
|
||||
|
||||
class CCrossFieldWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum MessageType {Information = 1, Warning = 2, Critical = 3, Invalid = 0};
|
||||
|
||||
bool isPaused() const { return m_Paused; }
|
||||
bool isSolved() const { return m_Solved; }
|
||||
bool isErrorAware() const { return m_ErrorAware; }
|
||||
|
||||
int time() const { return m_Time; }
|
||||
qint32 errorCount() const { return m_ErrorCount; }
|
||||
|
||||
CCrossFieldWidget(libqnono::CNonogram * picture, QWidget * parent = 0);
|
||||
CCrossFieldWidget(QWidget * parent = 0);
|
||||
~CCrossFieldWidget();
|
||||
|
||||
QPoint lastErrorMark() const { return m_LastErrorMark; }
|
||||
|
||||
void setPicture(libqnono::CNonogram * picture);
|
||||
libqnono::CNonogram * nonogram() { return m_Nonogram; }
|
||||
|
||||
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, int type);
|
||||
void setTime(int value);
|
||||
void setErrorAware(bool value);
|
||||
void setNumbersMarkable(bool value);
|
||||
void start();
|
||||
void pause();
|
||||
void resume();
|
||||
|
||||
signals:
|
||||
void markError();
|
||||
void solved();
|
||||
void timeUp();
|
||||
void timeChanged(int value);
|
||||
protected slots:
|
||||
/* signals from nonogram */
|
||||
void loaded();
|
||||
|
||||
void won(int score);
|
||||
void timeup();
|
||||
|
||||
void wrongMark(int x, int y, int penaltyTime);
|
||||
void changedMark(int x, int y);
|
||||
|
||||
void restarted();
|
||||
void paused(int time);
|
||||
void resumed(int time);
|
||||
void tick(int time);
|
||||
|
||||
protected:
|
||||
struct Message {
|
||||
@ -84,10 +75,10 @@ namespace qcross {
|
||||
};
|
||||
|
||||
QQueue<Message> m_Messages;
|
||||
int m_MessageTimeoutId;
|
||||
Message m_Message;
|
||||
|
||||
libqnono::CNonogram * m_Picture;
|
||||
MarkerType ** m_OverlayData;
|
||||
quint32 m_RemainingPixels;
|
||||
libqnono::CNonogram * m_Nonogram;
|
||||
|
||||
int m_BoxSize;
|
||||
qreal m_MarkerSize;
|
||||
@ -99,15 +90,11 @@ namespace qcross {
|
||||
int m_RasterWidth;
|
||||
int m_RasterHeight;
|
||||
|
||||
MarkerType m_MouseMark;
|
||||
libqnono::NonogramMarker::Mark m_MouseMark;
|
||||
bool m_MouseDown;
|
||||
int m_MouseLastX, m_MouseLastY;
|
||||
|
||||
bool m_ErrorAware;
|
||||
bool m_NumbersMarkable;
|
||||
int m_Time;
|
||||
bool m_Paused;
|
||||
bool m_Solved;
|
||||
int m_TimerId;
|
||||
int m_MessageTimeoutId;
|
||||
|
||||
QLCDNumber * m_Clock;
|
||||
|
||||
@ -115,22 +102,14 @@ namespace qcross {
|
||||
// QLabel * m_NotifierIcon;
|
||||
// QLabel * m_NotifierText;
|
||||
|
||||
qint32 m_ErrorCount;
|
||||
QPoint m_LastErrorMark;
|
||||
|
||||
Message m_Message;
|
||||
void nextMessage();
|
||||
|
||||
inline void initialize();
|
||||
inline void cleanup();
|
||||
inline void reset();
|
||||
|
||||
inline bool checkNoError(int x, int y);
|
||||
void initialize(); /** update metrics for new problem */
|
||||
|
||||
void timerEvent(QTimerEvent * event);
|
||||
inline void updateTimeDisplay();
|
||||
|
||||
inline void execMark(int x, int y, MarkerType & marker);
|
||||
void updateTimeDisplay();
|
||||
|
||||
void paintEvent(QPaintEvent * event);
|
||||
|
||||
@ -138,7 +117,7 @@ namespace qcross {
|
||||
*/
|
||||
void resizeEvent(QResizeEvent * event);
|
||||
|
||||
void updateMetrics();
|
||||
void updateMetrics(); /** update metrics after resize */
|
||||
|
||||
void mousePressEvent(QMouseEvent * event);
|
||||
void mouseMoveEvent(QMouseEvent * event);
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <libqnono/cnonogram.h>
|
||||
#include <libqnono/cnonogramsolver.h>
|
||||
#include <libqnono/ccrosspackage.h>
|
||||
|
||||
#include "constants.h"
|
||||
@ -42,32 +41,24 @@ namespace qcross {
|
||||
using namespace libqnono;
|
||||
//public
|
||||
CGameWindow::CGameWindow(QWidget * parent) : QMainWindow(parent) {
|
||||
m_Highscore = NULL;
|
||||
m_PictureIndex = -1;
|
||||
m_Picture = NULL;
|
||||
|
||||
// CNonogram * dummyPic = new CNonogram(QSize(10, 10));
|
||||
// dummyPic->updateNumbers();
|
||||
|
||||
m_Field = new CCrossFieldWidget(NULL, this);
|
||||
// m_Field->setTime(42 * 60 + 23);
|
||||
m_Field = new CCrossFieldWidget(this);
|
||||
m_Nonogram = m_Field->nonogram();
|
||||
|
||||
setCentralWidget(m_Field);
|
||||
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()));
|
||||
connect(m_Nonogram, SIGNAL(won(int)), this, SLOT(wonGame(int)));
|
||||
connect(m_Nonogram, SIGNAL(timeup()), this, SLOT(lostGame()));
|
||||
|
||||
connect(m_Nonogram, SIGNAL(loaded()), SLOT(updateStates()));
|
||||
connect(m_Nonogram, SIGNAL(restarted()), SLOT(updateStates()));
|
||||
connect(m_Nonogram, SIGNAL(paused(int)), SLOT(updateStates()));
|
||||
connect(m_Nonogram, SIGNAL(resumed(int)), SLOT(updateStates()));
|
||||
}
|
||||
|
||||
CGameWindow::~CGameWindow() {
|
||||
m_Field->setPicture(NULL);
|
||||
delete m_Picture;
|
||||
}
|
||||
|
||||
//protected
|
||||
@ -83,7 +74,7 @@ namespace qcross {
|
||||
m_SaveGameAction->setEnabled(false);
|
||||
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 = currentMenu->addAction(tr("&Restart"), m_Nonogram, SLOT(restart()), Qt::CTRL + Qt::Key_R);
|
||||
m_RestartGameAction->setEnabled(false);
|
||||
m_PauseGameAction = currentMenu->addAction(tr("&Pause"));
|
||||
m_PauseGameAction->setEnabled(false);
|
||||
@ -93,7 +84,7 @@ namespace qcross {
|
||||
currentMenu->addSeparator();
|
||||
currentMenu->addAction(tr("&Quit"), this, SLOT(close()), Qt::CTRL + Qt::Key_Q);
|
||||
currentMenu->addSeparator();
|
||||
currentMenu->addAction(tr("debug: solve"), this, SLOT(startSolver()));
|
||||
currentMenu->addAction(tr("debug: solve"), m_Nonogram, SLOT(solve()));
|
||||
|
||||
currentToolBar = addToolBar(currentMenu->title());
|
||||
currentToolBar->addActions(currentMenu->actions());
|
||||
@ -104,242 +95,95 @@ namespace qcross {
|
||||
currentMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
|
||||
}
|
||||
|
||||
const char magicSaveGameHeader[] = {'C', 'R', 'S', 'V'};
|
||||
|
||||
bool CGameWindow::readSaveGame(QDataStream & stream) {
|
||||
QString stringBuffer;
|
||||
qint32 intBuffer;
|
||||
QSize sizeBuffer;
|
||||
|
||||
// picture index
|
||||
stream >> intBuffer; qDebug("picture index = %i", m_PictureIndex);
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid savegame");
|
||||
return false;
|
||||
}
|
||||
m_PictureIndex = intBuffer;
|
||||
|
||||
// package name
|
||||
if (m_PictureIndex > -1) {
|
||||
stream >> stringBuffer; qDebug("package name = %s", qPrintable(stringBuffer));
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid savegame");
|
||||
return false;
|
||||
}
|
||||
m_PackageName = stringBuffer;
|
||||
|
||||
stream >> stringBuffer; qDebug("highscoreFileName = %s", qPrintable(stringBuffer));
|
||||
if (stream.atEnd()) {
|
||||
qCritical("invalid savegame");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Highscore)
|
||||
delete m_Highscore;
|
||||
|
||||
m_Highscore = new CHighscore(0);
|
||||
m_Highscore->setFileName(QCROSS_STRING_DATAPATH + QDir::separator() + stringBuffer);
|
||||
m_Highscore->open();
|
||||
}
|
||||
|
||||
// picture
|
||||
if (m_Picture)
|
||||
delete m_Picture;
|
||||
|
||||
m_Picture = new CNonogram();
|
||||
|
||||
if (!m_Picture->readFromStream(stream)) {
|
||||
qCritical("invalid savegame");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO handle save game porting properly
|
||||
// TODO check for matching nonogram
|
||||
|
||||
m_Field->setPicture(m_Picture);
|
||||
m_Field->applyState(stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGameWindow::writeSaveGame(QDataStream & stream) {
|
||||
// picture index
|
||||
stream << qint32(m_PictureIndex);
|
||||
if (m_PictureIndex > -1) {
|
||||
stream << m_PackageName;
|
||||
stream << m_Highscore->fileName().section(QDir::separator(), -1);
|
||||
}
|
||||
|
||||
m_Picture->writeToStream(stream);
|
||||
m_Field->dumpState(stream);
|
||||
}
|
||||
|
||||
void CGameWindow::newGame() {
|
||||
bool notPaused = !m_Field->isPaused();
|
||||
bool notPaused = !m_Nonogram->isPaused();
|
||||
if (notPaused)
|
||||
m_Field->pause();
|
||||
m_Nonogram->pause();
|
||||
|
||||
CNewGameDialog dialog;
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
if (m_Highscore)
|
||||
delete m_Highscore;
|
||||
|
||||
m_Highscore = dialog.takeHighscore();
|
||||
m_PackageName = dialog.selectedPackageName();
|
||||
m_PictureIndex = m_Highscore ? dialog.nonogramIndex() : -1;
|
||||
|
||||
CNonogram * newPicture = dialog.takeNonogram();
|
||||
|
||||
m_SaveGameAction->setEnabled(true);
|
||||
m_PauseGameAction->setEnabled(true);
|
||||
m_RestartGameAction->setEnabled(true);
|
||||
|
||||
m_Field->setPicture(newPicture);
|
||||
|
||||
if (m_Picture)
|
||||
delete m_Picture;
|
||||
|
||||
m_Picture = newPicture;
|
||||
|
||||
m_Field->resume();
|
||||
m_Field->showMessage(tr("Game started!"), 1000);
|
||||
m_Nonogram->start(dialog.getProblem());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Picture && !m_PauseGameAction->isChecked())
|
||||
m_Field->resume();
|
||||
if (notPaused)
|
||||
m_Nonogram->resume();
|
||||
}
|
||||
|
||||
|
||||
void CGameWindow::saveGame() {
|
||||
if (!m_Picture)
|
||||
return;
|
||||
|
||||
if (!m_Field->isPaused())
|
||||
m_Field->pause();
|
||||
bool notPaused = !m_Nonogram->isPaused();
|
||||
if (notPaused)
|
||||
m_Nonogram->pause();
|
||||
|
||||
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;
|
||||
m_Nonogram->saveGame(fileName);
|
||||
|
||||
QDataStream out(&file);
|
||||
out.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
if (out.writeRawData(magicSaveGameHeader, 4) == -1)
|
||||
qDebug("could not write magic save game header");
|
||||
else
|
||||
writeSaveGame(out);
|
||||
|
||||
if(!m_PauseGameAction->isChecked())
|
||||
m_Field->resume();
|
||||
if (notPaused)
|
||||
m_Nonogram->resume();
|
||||
}
|
||||
|
||||
void CGameWindow::loadGame() {
|
||||
if (!m_Field->isPaused())
|
||||
m_Field->pause();
|
||||
bool notPaused = !m_Nonogram->isPaused();
|
||||
if (notPaused)
|
||||
m_Nonogram->pause();
|
||||
|
||||
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 savegame header");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (magicHeader[i] != magicSaveGameHeader[i])
|
||||
if (m_Nonogram->loadGame(fileName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (readSaveGame(in)) {
|
||||
m_PauseGameAction->setEnabled(true);
|
||||
m_PauseGameAction->setChecked(m_Field->isPaused());
|
||||
m_SaveGameAction->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
m_Field->setPicture(NULL);
|
||||
m_PictureIndex = -1;
|
||||
|
||||
if (m_Picture) {
|
||||
delete m_Picture;
|
||||
m_Picture = NULL;
|
||||
}
|
||||
|
||||
if (m_Highscore) {
|
||||
delete m_Highscore;
|
||||
m_Highscore = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Picture && !m_PauseGameAction->isChecked())
|
||||
m_Field->resume();
|
||||
}
|
||||
|
||||
void CGameWindow::restartGame() {
|
||||
m_Field->setTime(m_Picture->timeout());
|
||||
|
||||
m_PauseGameAction->setChecked(false);
|
||||
m_Field->start();
|
||||
|
||||
m_Field->showMessage(tr("Game restarted."), 1000);
|
||||
if (notPaused)
|
||||
m_Nonogram->resume();
|
||||
}
|
||||
|
||||
void CGameWindow::pauseGame(bool value) {
|
||||
if (value == m_Field->isPaused())
|
||||
if (value == m_Nonogram->isPaused())
|
||||
return;
|
||||
|
||||
if (value) {
|
||||
m_Field->pause();
|
||||
m_Field->showMessage(tr("Game paused."));
|
||||
m_Nonogram->pause();
|
||||
}
|
||||
else {
|
||||
m_Field->showMessage(tr("Game resumed."), 1000);
|
||||
m_Field->resume();
|
||||
m_Nonogram->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void CGameWindow::wonGame() {
|
||||
void CGameWindow::wonGame(int score) {
|
||||
m_PauseGameAction->setEnabled(false);
|
||||
m_Field->showMessage(tr("Congratulations! You've solved the puzzle."));
|
||||
if (m_Highscore) {
|
||||
if (score < 0) return;
|
||||
const NonogramProblem &problem(m_Nonogram->problem());
|
||||
if (!problem.packageIdentifier().isEmpty() && problem.packageIndex() >= 0) {
|
||||
CHighscore highscore;
|
||||
highscore.setPackageIdentifier(problem.packageIdentifier());
|
||||
if (highscore.open()) {
|
||||
int oldScore = highscore[problem.packageIndex()];
|
||||
if (0 == oldScore || (problem.timeout() > 0 ? score > oldScore : score < oldScore)) {
|
||||
qDebug("attempting to save highscore");
|
||||
(*m_Highscore)[m_PictureIndex] = m_Field->time();
|
||||
m_Highscore->save();
|
||||
highscore[problem.packageIndex()] = score;
|
||||
highscore.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameWindow::lostGame() {
|
||||
m_PauseGameAction->setEnabled(false);
|
||||
m_Field->showMessage(tr("Too bad! Time's up."), 0, CCrossFieldWidget::Critical);
|
||||
}
|
||||
|
||||
void CGameWindow::handleErrorMark() {
|
||||
int timeDiff = (m_Field->errorCount() == 1) ? 2 : ((m_Field->errorCount() == 2) ? 4 : 8);
|
||||
|
||||
m_Field->showMessage(tr("Sorry this was not correct: -%1min").arg(timeDiff), 1000, CCrossFieldWidget::Warning);
|
||||
|
||||
m_Field->setTime(m_Field->time() - timeDiff*60);
|
||||
}
|
||||
|
||||
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();
|
||||
void CGameWindow::updateStates() {
|
||||
bool valid = m_Nonogram->valid();
|
||||
m_SaveGameAction->setEnabled(valid);
|
||||
m_PauseGameAction->setEnabled(valid);
|
||||
m_RestartGameAction->setEnabled(valid);
|
||||
m_PauseGameAction->setChecked(m_Nonogram->isPaused());
|
||||
}
|
||||
}
|
||||
|
@ -41,14 +41,8 @@ namespace qcross {
|
||||
~CGameWindow();
|
||||
|
||||
protected:
|
||||
libqnono::CNonogramSolver * m_Solver;
|
||||
|
||||
CCrossFieldWidget * m_Field;
|
||||
libqnono::CNonogram * m_Picture;
|
||||
|
||||
QString m_PackageName;
|
||||
int m_PictureIndex;
|
||||
CHighscore * m_Highscore;
|
||||
libqnono::CNonogram * m_Nonogram;
|
||||
|
||||
QAction * m_SaveGameAction;
|
||||
QAction * m_RestartGameAction;
|
||||
@ -56,26 +50,20 @@ namespace qcross {
|
||||
|
||||
void createActions();
|
||||
|
||||
bool readSaveGame(QDataStream & stream);
|
||||
void writeSaveGame(QDataStream & stream);
|
||||
|
||||
protected slots:
|
||||
void newGame();
|
||||
|
||||
void saveGame();
|
||||
void loadGame();
|
||||
|
||||
void restartGame();
|
||||
void pauseGame(bool value);
|
||||
|
||||
void wonGame();
|
||||
void wonGame(int score);
|
||||
void lostGame();
|
||||
|
||||
void handleErrorMark();
|
||||
|
||||
void about();
|
||||
|
||||
void startSolver();
|
||||
void updateStates();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,14 @@
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
|
||||
#include "chighscore.h"
|
||||
#include "constants.h"
|
||||
#include <libqnono/ccrosspackage.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace qcross {
|
||||
CHighscore::CHighscore(int initialSize) : QList<quint32>() {
|
||||
@ -28,12 +32,18 @@ namespace qcross {
|
||||
append(0);
|
||||
}
|
||||
|
||||
void CHighscore::setPackageIdentifier(QString PackageIdentifier) {
|
||||
m_PackageIdentifier = libqnono::CCrossPackage::identifierFromPath(PackageIdentifier);
|
||||
}
|
||||
|
||||
bool CHighscore::open() {
|
||||
if (m_FileName.isEmpty())
|
||||
if (m_PackageIdentifier.isEmpty())
|
||||
return false;
|
||||
|
||||
qDebug("reading highscore file: %s", m_FileName.toAscii().data());
|
||||
QFile file(m_FileName);
|
||||
QString fileName = (QCROSS_STRING_DATAPATH + QDir::separator() + m_PackageIdentifier + ".hsc");
|
||||
|
||||
qDebug("reading highscore file: %s", fileName.toAscii().data());
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
@ -55,11 +65,13 @@ namespace qcross {
|
||||
}
|
||||
|
||||
bool CHighscore::save() {
|
||||
if (m_FileName.isEmpty())
|
||||
if (m_PackageIdentifier.isEmpty())
|
||||
return false;
|
||||
|
||||
qDebug("saving highcore file: %s", m_FileName.toAscii().data());
|
||||
QFile file(m_FileName);
|
||||
QString fileName = (QCROSS_STRING_DATAPATH + QDir::separator() + m_PackageIdentifier + ".hsc");
|
||||
|
||||
qDebug("saving highcore file: %s", fileName.toAscii().data());
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return false;
|
||||
|
||||
|
@ -31,13 +31,13 @@ namespace qcross {
|
||||
public:
|
||||
CHighscore(int size = 0);
|
||||
|
||||
void setFileName(QString value) { m_FileName = value; }
|
||||
QString fileName() const { return m_FileName; }
|
||||
void setPackageIdentifier(QString PackageIdentifier);
|
||||
QString packageIdentifier() const { return m_PackageIdentifier; }
|
||||
|
||||
bool open();
|
||||
bool save();
|
||||
protected:
|
||||
QString m_FileName;
|
||||
QString m_PackageIdentifier;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ namespace qcross {
|
||||
CNewGameDialog::CNewGameDialog(QWidget * parent, Qt::WindowFlags f) :
|
||||
QDialog(parent, f),
|
||||
m_Highscore(NULL),
|
||||
m_Nonogram(NULL)
|
||||
m_Problem(NULL)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
@ -79,8 +79,8 @@ namespace qcross {
|
||||
if (m_Highscore)
|
||||
delete m_Highscore;
|
||||
|
||||
if (m_Nonogram)
|
||||
delete m_Nonogram;
|
||||
if (m_Problem)
|
||||
delete m_Problem;
|
||||
}
|
||||
|
||||
/* libqnono::CNonogram * CNewGameDialog::selectedNonogramData() const {
|
||||
@ -94,45 +94,29 @@ namespace qcross {
|
||||
}
|
||||
|
||||
libqnono::CCrossPackage * CNewGameDialog::selectedPackage() const {
|
||||
if (m_Nonogram) return NULL;
|
||||
if (m_Problem) return NULL;
|
||||
QModelIndex selected = ui.packageList->selectionModel()->selectedIndexes()[0];
|
||||
return static_cast<CCrossPackage *>(selected.internalPointer());
|
||||
}
|
||||
|
||||
QString CNewGameDialog::selectedPackageName() const {
|
||||
if (m_Nonogram) return QString();
|
||||
if (m_Problem) return QString();
|
||||
libqnono::CCrossPackage *p = selectedPackage();
|
||||
return p ? p->name() : QString();
|
||||
}
|
||||
|
||||
libqnono::CNonogram * CNewGameDialog::takeNonogram() {
|
||||
libqnono::CNonogram * result;
|
||||
|
||||
if (m_Nonogram) {
|
||||
result = m_Nonogram;
|
||||
m_Nonogram = NULL;
|
||||
NonogramProblem CNewGameDialog::getProblem() {
|
||||
if (m_Problem) {
|
||||
return *m_Problem;
|
||||
}
|
||||
else {
|
||||
QModelIndexList selected = ui.packageList->selectionModel()->selectedIndexes();
|
||||
if (selected.isEmpty())
|
||||
result = NULL;
|
||||
else {
|
||||
result = new CNonogram(static_cast<CCrossPackage *>(selected[0].internalPointer())->getPicture(nonogramIndex()));
|
||||
m_PicModel->setPackage(NULL);
|
||||
if (!selected.isEmpty()) {
|
||||
return selectedPackage()->getPicture(nonogramIndex());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CHighscore * CNewGameDialog::takeHighscore() {
|
||||
if (m_Nonogram)
|
||||
return NULL;
|
||||
|
||||
CHighscore * result = m_Highscore;
|
||||
m_Highscore = NULL;
|
||||
|
||||
return result;
|
||||
return NonogramProblem();
|
||||
}
|
||||
|
||||
//protected:
|
||||
@ -163,16 +147,13 @@ namespace qcross {
|
||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Select a picture to open"),
|
||||
QString(), tr("Images (*.png *.xpm *.xbm *.jpg)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
if (m_Nonogram)
|
||||
delete m_Nonogram;
|
||||
delete m_Problem; m_Problem = 0;
|
||||
|
||||
QImage image(fileName);
|
||||
if (!image.isNull()) {
|
||||
m_Nonogram = new CNonogram(NonogramProblem(NonogramImage(image)));
|
||||
m_Problem = new NonogramProblem(NonogramImage(image));
|
||||
accept();
|
||||
}
|
||||
else
|
||||
m_Nonogram = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,7 +168,7 @@ namespace qcross {
|
||||
if (!m_Highscore)
|
||||
m_Highscore = new CHighscore(0);
|
||||
|
||||
m_Highscore->setFileName(getHighscoreFileName(package->fileName()));
|
||||
m_Highscore->setPackageIdentifier(package->identifier());
|
||||
if (!m_Highscore->open()) {
|
||||
qDebug("opening highscore file failed. will create a new one when nonogram is solved");
|
||||
m_Highscore->clear();
|
||||
@ -197,6 +178,7 @@ namespace qcross {
|
||||
m_PicModel->setHighscore(m_Highscore);
|
||||
}
|
||||
void CNewGameDialog::handlePictureSelectionChanged(const QItemSelection & selected, const QItemSelection &) {
|
||||
delete m_Problem; m_Problem = 0;
|
||||
ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(selected.isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
namespace libqnono {
|
||||
class CNonogram;
|
||||
class CCrossPackage;
|
||||
class NonogramProblem;
|
||||
}
|
||||
|
||||
namespace qcross {
|
||||
@ -46,15 +47,14 @@ namespace qcross {
|
||||
libqnono::CCrossPackage * selectedPackage() const;
|
||||
QString selectedPackageName() const;
|
||||
|
||||
libqnono::CNonogram * takeNonogram();
|
||||
CHighscore * takeHighscore();
|
||||
libqnono::NonogramProblem getProblem();
|
||||
protected slots:
|
||||
void importPackage();
|
||||
void openPictureFile();
|
||||
private:
|
||||
Ui::picselect ui;
|
||||
CHighscore * m_Highscore;
|
||||
libqnono::CNonogram * m_Nonogram; /* if != NULL package wasn't used */
|
||||
libqnono::NonogramProblem * m_Problem; /* if != NULL package wasn't used */
|
||||
CMaskedCrossPackageModel * m_PicModel;
|
||||
CMaskedCrossPackageProxyModel * m_PicProxyModel;
|
||||
private slots:
|
||||
|
@ -27,32 +27,15 @@ QString formatedTime(quint32 seconds, bool showSeconds) {
|
||||
QString result;
|
||||
|
||||
if (hours) {
|
||||
if (hours < 10)
|
||||
result += '0';
|
||||
result += QString::number(hours);
|
||||
result += ':';
|
||||
result = QString("%1:").arg(hours, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
minutes %= 60;
|
||||
|
||||
if (minutes < 10)
|
||||
result += '0';
|
||||
result += QString::number(minutes);
|
||||
result += QString("%1").arg(minutes, 2, 10, QLatin1Char('0'));
|
||||
|
||||
if (showSeconds) {
|
||||
result += ':';
|
||||
if (strippedSeconds < 10)
|
||||
result += '0';
|
||||
result += QString::number(strippedSeconds);
|
||||
result += QString(":%1").arg(strippedSeconds, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString getHighscoreFileName(QString packageFileName) {
|
||||
if (packageFileName.isEmpty())
|
||||
return QString();
|
||||
|
||||
int tagPosition = packageFileName.lastIndexOf('.');
|
||||
|
||||
return ((tagPosition == -1) ? packageFileName : packageFileName.left(tagPosition)) + ".hsc";
|
||||
}
|
||||
|
@ -20,4 +20,3 @@
|
||||
#include <QString>
|
||||
|
||||
QString formatedTime(quint32 seconds, bool showSeconds = false);
|
||||
QString getHighscoreFileName(QString packageFileName);
|
||||
|
Loading…
Reference in New Issue
Block a user