Fixed all pieces movable after shuffle
[impuzzle] / src / gameview.cpp
1 /*
2   Image Puzzle - A set your pieces straight game
3   Copyright (C) 2009  Timo Härkönen
4
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.
9
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.
14
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/>.
17  */
18
19 #include "gameview.h"
20 #include "puzzleitem.h"
21 #include "defines.h"
22 #include "introitem.h"
23
24 #include <QGraphicsScene>
25 #include <QDateTime>
26 #include <QTimer>
27 #include <QPropertyAnimation>
28 #include <QParallelAnimationGroup>
29 #include <QFont>
30 #include <QMessageBox>
31
32 #include <QDebug>
33
34 GameView *GameView::instance_ = 0;
35
36 GameView::GameView(QWidget *parent) :
37         QGraphicsView(parent)
38 {
39     scene_ = new QGraphicsScene;
40     hiddenIndex_ = -1;
41     setScene(scene_);
42
43     introItem_ = new IntroItem;
44     introItem_->setText("Select new game from menu to play");
45     scene_->addItem(introItem_);
46
47     verticalStep_ = 0;
48     horizontalStep_ = 0;
49
50     qsrand(QDateTime::currentDateTime().toTime_t());
51 }
52
53 GameView *GameView::instance()
54 {
55     if(!instance_) {
56         instance_ = new GameView;
57     }
58
59     return instance_;
60 }
61
62 QList<PuzzleItem *> GameView::pieces() const
63 {
64     return pieces_;
65 }
66
67 void GameView::setPieces(const QList<PuzzleItem *> pieces)
68 {
69     if(pieces.isEmpty()) {
70         qDebug() << "Empty list @ GameView::setPieces";
71         return;
72     }
73
74     QList<QGraphicsItem *> previousItems = scene_->items();
75     if(!previousItems.isEmpty()) {
76         foreach(QGraphicsItem *item, previousItems) {
77             scene_->removeItem(item);
78         }
79     }
80
81     pieces_ = pieces;
82
83     int horizontalCount = 0;
84
85     // Find out board size
86     if(pieces_.count() == 12) {
87         horizontalCount = 4;
88     }
89     else if(pieces_.count() == 20) {
90         horizontalCount = 5;
91     }
92     else {
93         qDebug() << "Invalid piece count @ GameView::setPieces";
94         qDebug() << QString("Count was %1").arg(pieces_.count());
95         return;
96     }
97
98     int verticalCount = pieces_.count() / horizontalCount;
99     horizontalStep_ = IMAGE_WIDTH / horizontalCount + 5;
100     verticalStep_ = IMAGE_HEIGHT / verticalCount + 5;
101
102     int pieceNumber = 0;
103
104     // Set pieces to their correct positions
105     for(int i = 0; i < verticalCount; ++i) {
106         for(int j = 0; j < horizontalCount; ++j) {
107             scene_->addItem(pieces_.at(pieceNumber));
108             QPointF point(j * horizontalStep_, i * verticalStep_);
109             pieces_.at(pieceNumber)->setPos(point);
110             pieces_.at(pieceNumber)->setCorrectPlace(point);
111             pieces_.at(pieceNumber)->setCurrentPlace(point);
112             pieceNumber++;
113         }
114     }
115
116     // Wait a second
117     QTimer::singleShot(750, this, SLOT(shufflePieces()));
118 }
119
120 void GameView::shufflePieces()
121 {
122     if(pieces_.isEmpty()) {
123         qDebug() << "Empty list @ GameView::shufflePieces";
124         return;
125     }
126
127     // Give pieces ramdom locations
128     int rounds = 5; //TODO
129     for(int j = 0; j < rounds; ++j) {
130         for(int i = 0; i < pieces_.count(); ++i) {
131             QPointF tmp;
132             int changeIndex = 0;
133             while(changeIndex == i) {
134                 changeIndex = qrand() % pieces_.count();
135             }
136             tmp = pieces_.at(changeIndex)->currentPlace();
137             pieces_.at(changeIndex)->setCurrentPlace(pieces_.at(i)->currentPlace());
138             pieces_.at(i)->setCurrentPlace(tmp);
139         }
140     }
141
142     // TODO Animate transitions to new locations
143     QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup(this);
144     for(int i = 0; i < pieces_.count(); ++i) {
145         QPropertyAnimation *animation = new QPropertyAnimation(pieces_.at(i), "pos");
146         animation->setStartValue(pieces_.at(i)->correctPlace());
147         animation->setEndValue(pieces_.at(i)->currentPlace());
148         animation->setDuration(750);
149         animation->setEasingCurve(QEasingCurve::InOutCirc);
150         animationGroup->addAnimation(animation);
151     }
152     animationGroup->start();
153
154     // Hide random piece
155     int hiddenPiece = qrand() % pieces_.count();
156     emptyPlace_ = pieces_.at(hiddenPiece)->currentPlace();
157     pieces_.at(hiddenPiece)->hide();
158     hiddenIndex_ = hiddenPiece;
159
160     setMovingPieces();
161 }
162
163 QPointF GameView::emptyPlace()
164 {
165     return emptyPlace_;
166 }
167
168 void GameView::setEmptyPlace(const QPointF &place)
169 {
170     emptyPlace_ = place;
171 }
172
173 bool GameView::areAllPiecesOk() const
174 {
175     for(int i = 0; i < pieces_.count(); ++i) {
176         // Skip hidden piece
177         if(i == hiddenIndex_) {
178             continue;
179         }
180         // Id piece is not in it's place
181         else if(pieces_.at(i)->correctPlace() != pieces_.at(i)->currentPlace()) {
182             return false;
183         }
184     }
185     // Show hidden piece and move it to it's place
186     pieces_.at(hiddenIndex_)->show();
187     pieces_.at(hiddenIndex_)->moveMeTo(emptyPlace_);
188
189     // Set all pieces not movable
190     for(int i = 0; i < pieces_.count(); ++i) {
191         pieces_.at(i)->setMovable(false);
192     }
193
194     // Show dialog with move count
195     QMessageBox::about(const_cast<GameView *>(this), tr("You won"), QString("Puzzle completed with %1 moves").arg(PuzzleItem::moveCount()));
196
197     return true;
198 }
199
200 void GameView::setMovingPieces()
201 {
202     if(pieces_.isEmpty()) {
203         qDebug() << "Empty list @ GameView::setMovingPieces";
204         return;
205     }
206
207     QPointF point = QPointF();
208     for(int i = 0; i < pieces_.count(); ++i) {
209         point = pieces_.at(i)->currentPlace();
210
211         // Is piece on the left side of the empty space
212         if(emptyPlace_.y() == point.y() && point.x() + horizontalStep_ == emptyPlace_.x()) {
213             pieces_.at(i)->setMovable(true);
214         }
215
216         // Is piece on the right side of the empty space
217         else if(emptyPlace_.y() == point.y() && point.x() - horizontalStep_ == emptyPlace_.x()) {
218             pieces_.at(i)->setMovable(true);
219         }
220
221         // Is piece below the empty space
222         else if(emptyPlace_.x() == point.x() && point.y() - verticalStep_ == emptyPlace_.y()) {
223             pieces_.at(i)->setMovable(true);
224         }
225
226         // Is piece on top of the empty space
227         else if(emptyPlace_.x() == point.x() && point.y() + verticalStep_ == emptyPlace_.y()) {
228             pieces_.at(i)->setMovable(true);
229         }
230
231         // The piece is somewhere else
232         else {
233             pieces_.at(i)->setMovable(false);
234         }
235     }
236 }