save game query on exit if game is incomplete
[impuzzle] / src / gameview.cpp
index ee44f0a..4164b24 100644 (file)
 #include <QFile>
 #include <QDir>
 #include <QTextStream>
-#include <QCloseEvent>
+#include <QFileInfo>
+#include <QDateTime>
+#include <QTimer>
+#include <QApplication>
+#include <QDateTime>
+
+#ifdef Q_WS_MAEMO_5
+#include <QMaemo5InformationBox>
+#endif
 
 #include <QDebug>
 
@@ -42,12 +50,14 @@ GameView *GameView::instance_ = 0;
 GameView::GameView(QWidget *parent) :
         QGraphicsView(parent)
 {
+    setBackgroundBrush(Qt::black);
+    qsrand(QDateTime::currentDateTime().toTime_t());
     scene_ = new QGraphicsScene;
     hiddenIndex_ = -1;
     setScene(scene_);
 
     introItem_ = new IntroItem;
-    introItem_->setText("Select new game from menu to play");
+    introItem_->setText("- ImPuzzle -");
 
     verticalStep_ = 0;
     horizontalStep_ = 0;
@@ -58,6 +68,10 @@ GameView::GameView(QWidget *parent) :
                     .arg(QDir::homePath()).arg(HOME_DIRECTORY).arg(RESTORE_FILE))) {
         if(!restoreGame()) {
             setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()));
+            PuzzleItem::setMoveCount(0);
+        }
+        else {
+            QTimer::singleShot(0, this, SIGNAL(gameRestored()));
         }
     }
     else {
@@ -81,6 +95,8 @@ QList<PuzzleItem *> GameView::pieces() const
 
 void GameView::setPieces(const QList<PuzzleItem *> pieces, bool shuffle)
 {
+    PuzzleItem::setManuallyMovable(false);
+
     if(pieces.isEmpty()) {
         qDebug() << "Empty list @ GameView::setPieces";
         return;
@@ -133,8 +149,12 @@ void GameView::setPieces(const QList<PuzzleItem *> pieces, bool shuffle)
     if(shuffle) {
         QTimer::singleShot(750, this, SLOT(shufflePieces()));
     }
+    else {
+        PuzzleItem::setManuallyMovable(true);
+    }
 }
 
+//TODO: fixme!
 void GameView::shufflePieces()
 {
     if(pieces_.isEmpty()) {
@@ -143,21 +163,133 @@ void GameView::shufflePieces()
     }
 
     // Give pieces ramdom locations
-    int rounds = 5; //TODO
-    for(int j = 0; j < rounds; ++j) {
-        for(int i = 0; i < pieces_.count(); ++i) {
-            QPointF tmp;
-            int changeIndex = 0;
-            while(changeIndex == i) {
-                changeIndex = qrand() % pieces_.count();
+    hiddenIndex_ = qrand() % pieces_.count();
+    emptyPlace_ = pieces_.at(hiddenIndex_)->currentPlace();
+
+    QPointF topLeft = pieces_.at(0)->correctPlace();
+    QPointF bottomRight = pieces_.last()->correctPlace();
+
+    int moveCount = pieces_.count() * 10;
+    int movesMade = 0;
+
+    PuzzleItem *item = 0;
+
+    for(int i = 0; i < moveCount; ++i) {
+        int rand = qrand() % 4;
+
+        switch(rand) {
+        // up
+        case 0:
+            if(pieces_.at(hiddenIndex_)->currentPlace().y() > topLeft.y()) {
+                QPointF tmp = pieces_.at(hiddenIndex_)->currentPlace();
+                QGraphicsItem *graphicsItem = scene()->itemAt(tmp + QPointF(0, -verticalStep_));
+                if(graphicsItem) {
+                    item = dynamic_cast<PuzzleItem *>(graphicsItem);
+                    if(item->movable()) {
+                        emptyPlace_ = item->currentPlace();
+                        pieces_.at(hiddenIndex_)->setCurrentPlace(item->currentPlace());
+                        pieces_.at(hiddenIndex_)->setPos(item->currentPlace());
+                        item->setCurrentPlace(tmp);
+                        item->setPos(tmp);
+                        invalidateScene();
+                        scene()->update();
+                        setMovingPieces();
+                        movesMade++;
+                    }
+                    else {
+                        qDebug() << "Item right of hidden piece not movable";
+                    }
+                }
+            }
+            else {
+                --i;
+            }
+            break;
+        // down
+        case 1:
+            if(pieces_.at(hiddenIndex_)->currentPlace().y() < bottomRight.y()) {
+                QPointF tmp = pieces_.at(hiddenIndex_)->currentPlace();
+                QGraphicsItem *graphicsItem = scene()->itemAt(tmp + QPointF(0, verticalStep_));
+                if(graphicsItem) {
+                    item = dynamic_cast<PuzzleItem *>(graphicsItem);
+                    if(item->movable()) {
+                    emptyPlace_ = item->currentPlace();
+                        pieces_.at(hiddenIndex_)->setCurrentPlace(item->currentPlace());
+                        pieces_.at(hiddenIndex_)->setPos(item->currentPlace());
+                        item->setCurrentPlace(tmp);
+                        item->setPos(tmp);
+                        setMovingPieces();
+                        movesMade++;
+                    }
+                    else {
+                        qDebug() << "Item down of hidden piece not movable";
+                    }
+                }
             }
-            tmp = pieces_.at(changeIndex)->currentPlace();
-            pieces_.at(changeIndex)->setCurrentPlace(pieces_.at(i)->currentPlace());
-            pieces_.at(i)->setCurrentPlace(tmp);
+            else {
+                --i;
+            }
+            break;
+        // left
+        case 2:
+            if(pieces_.at(hiddenIndex_)->currentPlace().x() > topLeft.x()) {
+                QPointF tmp = pieces_.at(hiddenIndex_)->currentPlace();
+                QGraphicsItem *graphicsItem = scene()->itemAt(tmp + QPointF(-horizontalStep_, 0));
+                if(graphicsItem) {
+                    item = dynamic_cast<PuzzleItem *>(graphicsItem);
+                    if(item->movable()) {
+                        emptyPlace_ = item->currentPlace();
+                        pieces_.at(hiddenIndex_)->setCurrentPlace(item->currentPlace());
+                        pieces_.at(hiddenIndex_)->setPos(item->currentPlace());
+                        item->setCurrentPlace(tmp);
+                        item->setPos(tmp);
+                        setMovingPieces();
+                        movesMade++;
+                    }
+                    else {
+                        qDebug() << "Item left of hidden piece not movable";
+                    }
+                }
+            }
+            else {
+                --i;
+            }
+            break;
+        // right
+        case 3:
+            if(pieces_.at(hiddenIndex_)->currentPlace().x() < bottomRight.x()) {
+                QPointF tmp = pieces_.at(hiddenIndex_)->currentPlace();
+                QGraphicsItem *graphicsItem = scene()->itemAt(tmp + QPointF(horizontalStep_, 0));
+                if(graphicsItem) {
+                    item = dynamic_cast<PuzzleItem *>(graphicsItem);
+                    if(item->movable()) {
+                        emptyPlace_ = item->currentPlace();
+                        pieces_.at(hiddenIndex_)->setCurrentPlace(item->currentPlace());
+                        pieces_.at(hiddenIndex_)->setPos(item->currentPlace());
+                        item->setCurrentPlace(tmp);
+                        item->setPos(tmp);
+                        setMovingPieces();
+                        movesMade++;
+                    }
+                    else {
+                        qDebug() << "Item up of hidden piece not movable";
+                    }
+                }
+            }
+            else {
+                --i;
+            }
+            break;
+        default:
+            qDebug() << "WTF?";
+            break;
         }
     }
 
+    qDebug() << QString("Shuffle moves: %1/%2").arg(movesMade).arg(moveCount);
+
     QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup(this);
+    connect(animationGroup, SIGNAL(finished()), this, SLOT(shuffleAnimationFinished()));
     for(int i = 0; i < pieces_.count(); ++i) {
         QPropertyAnimation *animation = new QPropertyAnimation(pieces_.at(i), "pos");
         animation->setStartValue(pieces_.at(i)->correctPlace());
@@ -167,14 +299,13 @@ void GameView::shufflePieces()
         animationGroup->addAnimation(animation);
     }
     animationGroup->start();
+    pieces_.at(hiddenIndex_)->hide();
+}
 
-    // Hide random piece
-    int hiddenPiece = qrand() % pieces_.count();
-    emptyPlace_ = pieces_.at(hiddenPiece)->currentPlace();
-    pieces_.at(hiddenPiece)->hide();
-    hiddenIndex_ = hiddenPiece;
-
+void GameView::shuffleAnimationFinished()
+{
     setMovingPieces();
+    PuzzleItem::setManuallyMovable(true);
 }
 
 QPointF GameView::emptyPlace()
@@ -187,7 +318,7 @@ void GameView::setEmptyPlace(const QPointF &place)
     emptyPlace_ = place;
 }
 
-bool GameView::areAllPiecesOk() const
+bool GameView::areAllPiecesOk()
 {
     for(int i = 0; i < pieces_.count(); ++i) {
         // Skip hidden piece
@@ -211,6 +342,7 @@ bool GameView::areAllPiecesOk() const
 
     // Show dialog with move count
     QMessageBox::about(const_cast<GameView *>(this), tr("You won"), QString("Puzzle completed with %1 moves").arg(PuzzleItem::moveCount()));
+    emit gameWon();
 
     return true;
 }
@@ -275,9 +407,25 @@ bool GameView::restoreGame()
     qDebug() << "restore list count: " << list.count();
 
     if(!list.isEmpty()) {
-        Settings::instance()->setPieceCount(list.at(0).toInt());
+        bool ok = false;
+        int pieces = list.at(0).toInt(&ok);
+        if(!ok) {
+            return false;
+        }
 
         QString im = list.at(1);
+        if(!QFile::exists(im) && im != "default") {
+            return false;
+        }
+
+        int moveCount = list.at(2).toInt(&ok);
+        if(!ok) {
+            return false;
+        }
+
+        Settings::instance()->setPieceCount(pieces);
+        PuzzleItem::setMoveCount(moveCount);
+
         if(im == "default" || im.isEmpty()) {
             Settings::instance()->setImage(0);
             Settings::instance()->setImagePath("default");
@@ -286,7 +434,6 @@ bool GameView::restoreGame()
             Settings::instance()->setImagePath(im);
             Settings::instance()->setImage(QPixmap(im));
         }
-        PuzzleItem::setMoveCount(list.at(2).toInt());
 
         setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()), false);
 
@@ -296,14 +443,27 @@ bool GameView::restoreGame()
             for(int j = 0; j < pieces_.count(); ++j) {
                 if(!list.at(j + 3).isNull()) {
                     QStringList points = list.at(j + 3).split("#");
-                    //if(points.count() == 2)
-                    QPointF point(points.at(0).toInt(), points.at(1).toInt());
 
-                    qDebug() << "Setting piece " << pieces_.at(j)->pieceNumber();
-                    qDebug() << "x: " << point.x() << " y: " << point.y();
+                    int x = points.at(0).toInt(&ok);
+                    if(!ok) {
+                        return false;
+                    }
+
+                    int y = points.at(1).toInt(&ok);
+                    if(!ok) {
+                        return false;
+                    }
+
+                    QPointF point(x, y);
+
+                    //qDebug() << "Setting piece " << pieces_.at(j)->pieceNumber();
+                    //qDebug() << "x: " << point.x() << " y: " << point.y();
 
                     pieces_.at(j)->setCurrentPlace(point);
                 }
+                else {
+                    return false;
+                }
             }
         }
         else {
@@ -318,7 +478,7 @@ bool GameView::restoreGame()
             for(int m = 0; m < pieces_.count(); ++m) {
                 pieces_.at(m)->setPos(pieces_.at(m)->currentPlace());
                 if(pieces_.at(m)->pieceNumber() == hidden.at(2).toInt()) {
-                    qDebug() << "Hiding piece number " << hidden;
+                    //qDebug() << "Hiding piece number " << hidden;
                     hiddenIndex_ = m;
                 }
             }
@@ -330,7 +490,6 @@ bool GameView::restoreGame()
             setMovingPieces();
         }
         else {
-            // TODO: revert
             setPieces(ImageImporter::instance()->newPieces(Settings::instance()->image(), Settings::instance()->pieceCount()));
             file.close();
             file.remove();
@@ -344,16 +503,26 @@ bool GameView::restoreGame()
         return false;
     }
 
+    QFileInfo fileInfo(file);
+
+    QDateTime created = fileInfo.created();
+    QString infoTxt = QString("Restored game state from %1")
+                      .arg(created.toString(Qt::TextDate));
+
+#ifdef Q_WS_MAEMO_5
+    QMaemo5InformationBox::information(this, infoTxt);
+#endif
+
     file.close();
     file.remove();
 
     return true;
 }
 
-bool GameView::saveGame()
+void GameView::saveGame()
 {
     if(pieces_.isEmpty() || pieces_.count() < EASY_PIECE_COUNT) {
-        return false;
+        return;
     }
 
     QDir dir;
@@ -372,22 +541,22 @@ bool GameView::saveGame()
 
     if(!file.open(QIODevice::WriteOnly)) {
         qDebug() << "Failed to open restore file for writing";
-        return false;
+        return;
     }
 
     QTextStream out(&file);
 
     out << Settings::instance()->pieceCount();
-    out << ";;";
+    out << QString(";;");
     if(Settings::instance()->imagePath().isEmpty()) {
-        out << "default";
+        out << QString("default");
     }
     else {
         out << Settings::instance()->imagePath();
     }
-    out << ";;";
+    out << QString(";;");
     out << PuzzleItem::moveCount();
-    out << ";;";
+    out << QString(";;");
 
     // piece positions
     int number = 0;
@@ -397,9 +566,9 @@ bool GameView::saveGame()
         for(int i = 0; i < pieces_.count(); ++i) {
             if(pieces_.at(i)->pieceNumber() == number + 1) {
                 out << pieces_.at(i)->currentPlace().x();
-                out << "#";
+                out << QString("#");
                 out << pieces_.at(i)->currentPlace().y();
-                out << ";;";
+                out << QString(";;");
                 pieces_.at(i)->pieceNumber();
                 if(!pieces_.at(i)->isVisible()) {
                     hiddenNo = number + 1;
@@ -416,18 +585,31 @@ bool GameView::saveGame()
 
     file.close();
 
-    return true;
+    qApp->quit();
+}
+
+int GameView::correctPlaces() const
+{
+    int c = 0;
+
+    for(int i = 0; i < pieces_.count(); ++i) {
+        if(pieces_.at(i)->currentPlace() == pieces_.at(i)->correctPlace()) {
+            c++;
+        }
+    }
+
+    return c;
 }
 
-void GameView::closeEvent(QCloseEvent *event)
+QList<int> GameView::movingPlaces() const
 {
-    int answer = QMessageBox::question(this, tr("Save game status?"),
-                                       tr("Saved status will be automatically loaded when you start the application next time"),
-                                       QMessageBox::Yes, QMessageBox::No);
+    QList<int> m;
 
-    if(answer == QMessageBox::Yes) {
-        saveGame();
+    for(int i = 0; i < pieces_.count(); ++i) {
+        if(pieces_.at(i)->movable()) {
+            m.append(i);
+        }
     }
 
-    event->accept();
+    return m;
 }