2 Image Puzzle - A set your pieces straight game
3 Copyright (C) 2009 Timo Härkönen
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "puzzleitem.h"
22 #include "introitem.h"
23 #include "imageimporter.h"
26 #include <QGraphicsScene>
29 #include <QPropertyAnimation>
30 #include <QParallelAnimationGroup>
32 #include <QMessageBox>
35 #include <QTextStream>
36 #include <QCloseEvent>
41 #include <QMaemo5InformationBox>
46 GameView *GameView::instance_ = 0;
48 GameView::GameView(QWidget *parent) :
51 scene_ = new QGraphicsScene;
55 introItem_ = new IntroItem;
56 introItem_->setText("Select new game from menu to play");
61 qsrand(QDateTime::currentDateTime().toTime_t());
63 if(QFile::exists(QString("%1/%2/%3")
64 .arg(QDir::homePath()).arg(HOME_DIRECTORY).arg(RESTORE_FILE))) {
66 setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()));
67 PuzzleItem::setMoveCount(0);
71 scene_->addItem(introItem_);
75 GameView *GameView::instance()
78 instance_ = new GameView;
84 QList<PuzzleItem *> GameView::pieces() const
89 void GameView::setPieces(const QList<PuzzleItem *> pieces, bool shuffle)
91 if(pieces.isEmpty()) {
92 qDebug() << "Empty list @ GameView::setPieces";
96 QList<QGraphicsItem *> previousItems = scene_->items();
97 if(!previousItems.isEmpty()) {
98 foreach(QGraphicsItem *item, previousItems) {
99 scene_->removeItem(item);
105 int horizontalCount = 0;
107 // Find out board size
108 if(pieces_.count() == EASY_PIECE_COUNT) {
109 horizontalCount = EASY_HORIZONTAL_COUNT;
111 else if(pieces_.count() == HARD_PIECE_COUNT) {
112 horizontalCount = HARD_HORIZONTAL_COUNT;
115 qDebug() << "Invalid piece count @ GameView::setPieces";
116 qDebug() << QString("Count was %1").arg(pieces_.count());
120 int verticalCount = pieces_.count() / horizontalCount;
121 horizontalStep_ = IMAGE_WIDTH / horizontalCount + 5;
122 verticalStep_ = IMAGE_HEIGHT / verticalCount + 5;
126 // Set pieces to their correct positions
127 for(int i = 0; i < verticalCount; ++i) {
128 for(int j = 0; j < horizontalCount; ++j) {
129 scene_->addItem(pieces_.at(pieceNumber));
130 QPointF point(j * horizontalStep_, i * verticalStep_);
131 pieces_.at(pieceNumber)->setPos(point);
132 pieces_.at(pieceNumber)->setCorrectPlace(point);
133 pieces_.at(pieceNumber)->setCurrentPlace(point);
134 pieces_.at(pieceNumber)->setDrawNumber(true);
139 // Wait and shuffle if desired
141 QTimer::singleShot(750, this, SLOT(shufflePieces()));
145 void GameView::shufflePieces()
147 if(pieces_.isEmpty()) {
148 qDebug() << "Empty list @ GameView::shufflePieces";
152 // Give pieces ramdom locations
153 int rounds = 5; //TODO
154 for(int j = 0; j < rounds; ++j) {
155 for(int i = 0; i < pieces_.count(); ++i) {
158 while(changeIndex == i) {
159 changeIndex = qrand() % pieces_.count();
161 tmp = pieces_.at(changeIndex)->currentPlace();
162 pieces_.at(changeIndex)->setCurrentPlace(pieces_.at(i)->currentPlace());
163 pieces_.at(i)->setCurrentPlace(tmp);
167 QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup(this);
168 for(int i = 0; i < pieces_.count(); ++i) {
169 QPropertyAnimation *animation = new QPropertyAnimation(pieces_.at(i), "pos");
170 animation->setStartValue(pieces_.at(i)->correctPlace());
171 animation->setEndValue(pieces_.at(i)->currentPlace());
172 animation->setDuration(750);
173 animation->setEasingCurve(QEasingCurve::InOutCirc);
174 animationGroup->addAnimation(animation);
176 animationGroup->start();
179 int hiddenPiece = qrand() % pieces_.count();
180 emptyPlace_ = pieces_.at(hiddenPiece)->currentPlace();
181 pieces_.at(hiddenPiece)->hide();
182 hiddenIndex_ = hiddenPiece;
187 QPointF GameView::emptyPlace()
192 void GameView::setEmptyPlace(const QPointF &place)
197 bool GameView::areAllPiecesOk()
199 for(int i = 0; i < pieces_.count(); ++i) {
201 if(i == hiddenIndex_) {
204 // Id piece is not in it's place
205 else if(pieces_.at(i)->correctPlace() != pieces_.at(i)->currentPlace()) {
209 // Show hidden piece and move it to it's place
210 pieces_.at(hiddenIndex_)->show();
211 pieces_.at(hiddenIndex_)->moveMeTo(emptyPlace_);
213 // Set all pieces not movable and hide numbers
214 for(int i = 0; i < pieces_.count(); ++i) {
215 pieces_.at(i)->setMovable(false);
216 pieces_.at(i)->setDrawNumber(false);
219 // Show dialog with move count
220 QMessageBox::about(const_cast<GameView *>(this), tr("You won"), QString("Puzzle completed with %1 moves").arg(PuzzleItem::moveCount()));
226 void GameView::setMovingPieces()
228 if(pieces_.isEmpty()) {
229 qDebug() << "Empty list @ GameView::setMovingPieces";
233 QPointF point = QPointF();
234 for(int i = 0; i < pieces_.count(); ++i) {
235 point = pieces_.at(i)->currentPlace();
237 // Is piece on the left side of the empty space
238 if(emptyPlace_.y() == point.y() && point.x() + horizontalStep_ == emptyPlace_.x()) {
239 pieces_.at(i)->setMovable(true);
242 // Is piece on the right side of the empty space
243 else if(emptyPlace_.y() == point.y() && point.x() - horizontalStep_ == emptyPlace_.x()) {
244 pieces_.at(i)->setMovable(true);
247 // Is piece below the empty space
248 else if(emptyPlace_.x() == point.x() && point.y() - verticalStep_ == emptyPlace_.y()) {
249 pieces_.at(i)->setMovable(true);
252 // Is piece on top of the empty space
253 else if(emptyPlace_.x() == point.x() && point.y() + verticalStep_ == emptyPlace_.y()) {
254 pieces_.at(i)->setMovable(true);
257 // The piece is somewhere else
259 pieces_.at(i)->setMovable(false);
264 bool GameView::restoreGame()
266 // Read settings from file
267 QFile file(QString("%1/%2/%3")
268 .arg(QDir::homePath())
272 if(!file.open(QIODevice::ReadOnly)) {
273 qDebug() << "Failed to open restore file for reading";
277 QTextStream in(&file);
281 list = in.readLine().split(";;");
283 qDebug() << "restore list count: " << list.count();
285 if(!list.isEmpty()) {
287 int pieces = list.at(0).toInt(&ok);
292 QString im = list.at(1);
293 if(!QFile::exists(im) && im != "default") {
297 int moveCount = list.at(2).toInt(&ok);
302 Settings::instance()->setPieceCount(pieces);
303 PuzzleItem::setMoveCount(moveCount);
305 if(im == "default" || im.isEmpty()) {
306 Settings::instance()->setImage(0);
307 Settings::instance()->setImagePath("default");
310 Settings::instance()->setImagePath(im);
311 Settings::instance()->setImage(QPixmap(im));
314 setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()), false);
316 qDebug() << "pieces_ count after restoring image: " << pieces_.count();
318 if(list.count() >= pieces_.count() + 3) {
319 for(int j = 0; j < pieces_.count(); ++j) {
320 if(!list.at(j + 3).isNull()) {
321 QStringList points = list.at(j + 3).split("#");
322 //if(points.count() == 2)
323 int x = points.at(0).toInt(&ok);
328 int y = points.at(1).toInt(&ok);
335 //qDebug() << "Setting piece " << pieces_.at(j)->pieceNumber();
336 //qDebug() << "x: " << point.x() << " y: " << point.y();
338 pieces_.at(j)->setCurrentPlace(point);
351 QStringList hidden = list.last().split("#");
353 if(hidden.count() == 3) {
354 for(int m = 0; m < pieces_.count(); ++m) {
355 pieces_.at(m)->setPos(pieces_.at(m)->currentPlace());
356 if(pieces_.at(m)->pieceNumber() == hidden.at(2).toInt()) {
357 //qDebug() << "Hiding piece number " << hidden;
362 setEmptyPlace(QPointF(hidden.at(0).toInt(), hidden.at(1).toInt()));
364 pieces_.at(hiddenIndex_)->setVisible(false);
369 setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()));
376 qDebug() << "Invalid restore file";
382 QFileInfo fileInfo(file);
384 QDateTime created = fileInfo.created();
385 QString infoTxt = QString("Restored game state from %1")
386 .arg(created.toString(Qt::TextDate));
389 QMaemo5InformationBox::information(this, infoTxt);
398 bool GameView::saveGame()
400 if(pieces_.isEmpty() || pieces_.count() < EASY_PIECE_COUNT) {
405 if(!dir.exists(QString("%1/%2")
406 .arg(QDir::homePath())
407 .arg(HOME_DIRECTORY))) {
408 dir.mkpath(QString("%1/%2")
409 .arg(QDir::homePath())
410 .arg(HOME_DIRECTORY));
413 QFile file(QString("%1/%2/%3")
414 .arg(QDir::homePath())
418 if(!file.open(QIODevice::WriteOnly)) {
419 qDebug() << "Failed to open restore file for writing";
423 QTextStream out(&file);
425 out << Settings::instance()->pieceCount();
426 out << QString(";;");
427 if(Settings::instance()->imagePath().isEmpty()) {
428 out << QString("default");
431 out << Settings::instance()->imagePath();
433 out << QString(";;");
434 out << PuzzleItem::moveCount();
435 out << QString(";;");
441 while(number != pieces_.count()) {
442 for(int i = 0; i < pieces_.count(); ++i) {
443 if(pieces_.at(i)->pieceNumber() == number + 1) {
444 out << pieces_.at(i)->currentPlace().x();
446 out << pieces_.at(i)->currentPlace().y();
447 out << QString(";;");
448 pieces_.at(i)->pieceNumber();
449 if(!pieces_.at(i)->isVisible()) {
450 hiddenNo = number + 1;
458 out << QString("%1#%2#%3").arg(emptyPlace().x()).arg(emptyPlace().y()).arg(hiddenNo);
467 void GameView::closeEvent(QCloseEvent *event)
469 int answer = QMessageBox::question(this, tr("Save game status?"),
470 tr("Saved status will be automatically loaded when you start the application next time"),
471 QMessageBox::Yes, QMessageBox::No);
473 if(answer == QMessageBox::Yes) {