1 /***************************************************************************
2 *   KBlocks, a falling blocks game by KDE                                *
3 *   Copyright (C) 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com>       *
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 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 ***************************************************************************/
10 #include "KBlocksSingleGame.h"
11 #include "utils.h"
12 #include <QRandomGenerator>
13 
KBlocksSingleGame(int gameIndex,int fieldWidth,int fieldHeight,int showPieceCount,int messagePoolSize)14 KBlocksSingleGame::KBlocksSingleGame(int gameIndex, int fieldWidth, int fieldHeight, int showPieceCount, int messagePoolSize)
15 {
16     mGameIndex = gameIndex;
17 
18     mpField = new KBlocksField(fieldWidth, fieldHeight);
19 
20     mPieceCount = showPieceCount;
21     mpPieceList = new KBlocksPiece*[mPieceCount];
22     for (int i = 0; i < mPieceCount; i++) {
23         mpPieceList[i] = new KBlocksPiece();
24     }
25 
26     mpPieceGenerator = new KBlocksPieceGenerator();
27     mpGameMessage = new KBlocksGameMessage(messagePoolSize);
28     mpGameRecorder = nullptr;
29 
30     mCurrentGameState = GameState_Stop;
31 
32     mStandbyMode = false;
33     mStandbyFlag = false;
34 
35     mGameInterval = 0;
36     mGameStartTime = 0;
37 }
38 
~KBlocksSingleGame()39 KBlocksSingleGame::~KBlocksSingleGame()
40 {
41     delete mpGameMessage;
42     delete mpPieceGenerator;
43 
44     for (int i = 0; i < mPieceCount; i++) {
45         delete mpPieceList[i];
46     }
47     delete [] mpPieceList;
48 
49     delete mpField;
50 }
51 
getField()52 KBlocksField *KBlocksSingleGame::getField()
53 {
54     return mpField;
55 }
56 
getPieceCount()57 int KBlocksSingleGame::getPieceCount()
58 {
59     return mPieceCount;
60 }
61 
getPiece(int index)62 KBlocksPiece *KBlocksSingleGame::getPiece(int index)
63 {
64     if ((index < 0) || (index >= mPieceCount)) {
65         return nullptr;
66     }
67     return mpPieceList[index];
68 }
69 
isActive()70 bool KBlocksSingleGame::isActive()
71 {
72     if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
73         return false;
74     }
75     return true;
76 }
77 
isGameRunning()78 bool KBlocksSingleGame::isGameRunning()
79 {
80     if (mCurrentGameState == GameState_Stop) {
81         return false;
82     }
83     return true;
84 }
85 
setGameStandbyMode(bool flag)86 void KBlocksSingleGame::setGameStandbyMode(bool flag)
87 {
88     mStandbyMode = flag;
89 }
90 
setGameInterval(int interval)91 void KBlocksSingleGame::setGameInterval(int interval)
92 {
93     mGameInterval = interval;
94 }
95 
setGameRecorder(KBlocksGameRecorder * p)96 void KBlocksSingleGame::setGameRecorder(KBlocksGameRecorder *p)
97 {
98     mpGameRecorder = p;
99 }
100 
forceUpdateGame()101 int KBlocksSingleGame::forceUpdateGame()
102 {
103     return doUpdateGame(true);
104 }
105 
updateGame()106 int KBlocksSingleGame::updateGame()
107 {
108     return doUpdateGame(false);
109 }
110 
punishGame(int lineCount,int punishSeed)111 int KBlocksSingleGame::punishGame(int lineCount, int punishSeed)
112 {
113     if (mCurrentGameState == GameState_Stop) {
114         return GameResult_None;
115     }
116 
117     int width = mpField->getWidth();
118 
119     int gameResult = GameResult_None;
120 
121     if (mpGameRecorder) {
122         mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineCount, lineCount);
123         mpGameRecorder->append(mGameIndex, RecordDataType_PunishLineSeed, punishSeed);
124     }
125 
126     int punishIndex = 0;
127     auto random = QRandomGenerator::global();
128     for (int i = 0; i < lineCount; i++) {
129         setCurrentPiece(0, -1, 0);
130         punishIndex = random->bounded(width);
131         mpField->addPunishLine(lineCount, punishIndex);
132     }
133 
134     if (lineCount > 0) {
135         mpGameMessage->putGameAction(GameAction_Punish_Line, lineCount);
136     }
137 
138     return gameResult;
139 }
140 
setCurrentPiece(int xPos,int yPos,int rotation)141 bool KBlocksSingleGame::setCurrentPiece(int xPos, int yPos, int rotation)
142 {
143     if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
144         return false;
145     }
146     //FIXME: new without delete (is the new really necessary here?)
147     QScopedPointer<KBlocksPiece> tmpPiece(new KBlocksPiece());
148 
149     tmpPiece->fromValue(mpPieceList[0]->toValue());
150     tmpPiece->setPosX(mpPieceList[0]->getPosX() + xPos);
151     if (mpPieceList[0]->getPosY() + yPos < 0) {
152         tmpPiece->setPosY(0);
153     } else {
154         tmpPiece->setPosY(mpPieceList[0]->getPosY() + yPos);
155     }
156     tmpPiece->setRotation(mpPieceList[0]->getRotation() + rotation);
157 
158     if (checkPieceTouchGround(tmpPiece.data())) {
159         return false;
160     }
161 
162     mpPieceList[0]->setPosX(tmpPiece->getPosX());
163     mpPieceList[0]->setPosY(tmpPiece->getPosY());
164     mpPieceList[0]->setRotation(tmpPiece->getRotation());
165 
166     if (mpGameRecorder) {
167         if (xPos < 0) {
168             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceLeft, 1);
169         }
170         if (xPos > 0) {
171             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceRight, 1);
172         }
173         if (yPos < 0) {
174             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceUp, 1);
175         }
176         if (yPos > 0) {
177             mpGameRecorder->append(mGameIndex, RecordDataType_MovePieceDown, 1);
178         }
179         if (rotation < 0) {
180             mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCW, 1);
181         }
182         if (rotation > 0) {
183             mpGameRecorder->append(mGameIndex, RecordDataType_RotatePieceCCW, 1);
184         }
185     }
186 
187     return true;
188 }
189 
startGame(int seed)190 int KBlocksSingleGame::startGame(int seed)
191 {
192     if (mCurrentGameState != GameState_Stop) {
193         return mCurrentGameState;
194     }
195 
196     mpPieceGenerator->genList(seed);
197 
198     for (int i = 0; i < mPieceCount; i++) {
199         mpPieceList[i]->fromValue(mpPieceGenerator->getPiece());
200     }
201 
202     mpPieceList[0]->setPosX(mpField->getWidth() / 2);
203     mpPieceList[0]->setPosY(0);
204 
205     mpPieceList[1]->setPosX(2);
206     mpPieceList[1]->setPosY(2);
207 
208     mpGameMessage->clearGameResult();
209     mpGameMessage->clearGameAction();
210 
211     mCurrentGameState = GameState_Running;
212 
213     mGameStartTime = Utils::getMillisecOfNow();
214 
215     return mCurrentGameState;
216 }
217 
stopGame()218 int KBlocksSingleGame::stopGame()
219 {
220     if (mCurrentGameState != GameState_Stop) {
221         mCurrentGameState = GameState_Stop;
222         Q_EMIT gameStopped();
223     }
224 
225     return mCurrentGameState;
226 }
227 
pauseGame(bool flag)228 int KBlocksSingleGame::pauseGame(bool flag)
229 {
230     if ((mCurrentGameState == GameState_Running) && flag) {
231         mCurrentGameState = GameState_Pause;
232     } else if ((mCurrentGameState == GameState_Pause) && (!flag)) {
233         mCurrentGameState = GameState_Running;
234 
235         mGameStartTime = Utils::getMillisecOfNow();
236     }
237 
238     return mCurrentGameState;
239 }
240 
continueGame()241 int KBlocksSingleGame::continueGame()
242 {
243     if ((mCurrentGameState != GameState_Stop) && mStandbyFlag) {
244         mStandbyFlag = false;
245 
246         mGameStartTime = Utils::getMillisecOfNow();
247     }
248 
249     return mCurrentGameState;
250 }
251 
pickGameResult(int * result)252 bool KBlocksSingleGame::pickGameResult(int *result)
253 {
254     return mpGameMessage->pickGameResult(result);
255 }
256 
pickGameAction(int * type,int * action)257 bool KBlocksSingleGame::pickGameAction(int *type, int *action)
258 {
259     return mpGameMessage->pickGameAction(type, action);
260 }
261 
doUpdateGame(bool force)262 int KBlocksSingleGame::doUpdateGame(bool force)
263 {
264     if (mCurrentGameState == GameState_Stop) {
265         return GameResult_Game_Over;
266     } else if ((mCurrentGameState != GameState_Running) || mStandbyFlag) {
267         return GameResult_None;
268     }
269 
270     timeLong tmpCurTime = Utils::getMillisecOfNow();
271 
272     int gameResult = GameResult_None;
273 
274     if (force) {
275         runGameOneStep(&gameResult);
276     } else {
277         if (mGameInterval < 0) {
278             return gameResult;
279         }
280 
281         while (1) {
282             if (mGameStartTime + mGameInterval > tmpCurTime) {
283                 break;
284             }
285 
286             mGameStartTime += mGameInterval;
287 
288             if (runGameOneStep(&gameResult) || (mGameInterval == 0)) {
289                 break;
290             }
291         }
292     }
293 
294     return gameResult;
295 }
296 
runGameOneStep(int * gameResult)297 bool KBlocksSingleGame::runGameOneStep(int *gameResult)
298 {
299     if (!setCurrentPiece(0, 1, 0)) {
300         *gameResult = GameResult_Next_Piece;
301 
302         freezePieceToField(mpPieceList[0]);
303 
304         *gameResult += removeFieldLines();
305 
306         if (mStandbyMode) {
307             mStandbyFlag = true;
308         }
309 
310         prepareNextPiece();
311         if (checkPieceTouchGround(mpPieceList[0])) {
312             *gameResult = GameResult_Game_Over;
313             mpGameMessage->putGameResult(-1);
314             stopGame();
315         }
316 
317         if (mpGameRecorder) {
318             mpGameRecorder->append(mGameIndex, RecordDataType_GameOneStep, 1);
319         }
320 
321         return true;
322     } else {
323         *gameResult = GameResult_One_Step;
324 
325         return false;
326     }
327 }
328 
checkPieceTouchGround(KBlocksPiece * p)329 bool KBlocksSingleGame::checkPieceTouchGround(KBlocksPiece *p)
330 {
331     for (int i = 0; i < 4; i++) {
332         int posX = p->getCellPosX(i);
333         int posY = p->getCellPosY(i);
334         if (mpField->getCell(posX, posY)) {
335             return true;
336         }
337     }
338     return false;
339 }
340 
freezePieceToField(KBlocksPiece * p)341 void KBlocksSingleGame::freezePieceToField(KBlocksPiece *p)
342 {
343     mpGameMessage->putGameAction(GameAction_Freeze_Piece_Color, mpPieceList[0]->getType());
344     for (int i = 0; i < 4; i++) {
345         int posX = p->getCellPosX(i);
346         int posY = p->getCellPosY(i);
347         mpField->setCell(posX, posY, true);
348         mpGameMessage->putGameAction(GameAction_Freeze_Piece_X, posX);
349         mpGameMessage->putGameAction(GameAction_Freeze_Piece_Y, posY);
350     }
351 }
352 
removeFieldLines()353 int KBlocksSingleGame::removeFieldLines()
354 {
355     int lineCount = 0;
356 
357     int maxLines = mpField->getHeight();
358     for (int i = 0; i < maxLines; i++) {
359         if (mpField->checkFilledLine(i)) {
360             mpGameMessage->putGameAction(GameAction_Remove_Line, i);
361             mpField->removeFilledLine(i);
362             lineCount++;
363         }
364     }
365 
366     if (lineCount > 0) {
367         mpGameMessage->putGameResult(lineCount);
368     }
369 
370     return lineCount;
371 }
372 
prepareNextPiece()373 void KBlocksSingleGame::prepareNextPiece()
374 {
375     for (int i = 0; i < mPieceCount - 1; i++) {
376         mpPieceList[i]->fromValue(mpPieceList[i + 1]->toValue());
377     }
378 
379     int pieceValue = mpPieceGenerator->getPiece();
380     mpPieceList[mPieceCount - 1]->fromValue(pieceValue);
381 
382     mpPieceList[0]->setPosX(mpField->getWidth() / 2);
383     mpPieceList[0]->setPosY(0);
384 
385     mpPieceList[1]->setPosX(2);
386     mpPieceList[1]->setPosY(2);
387 
388     for (int i = 0; i < 4; i++) {
389         int posX = mpPieceList[0]->getCellPosX(i);
390         int posY = mpPieceList[0]->getCellPosY(i);
391         mpGameMessage->putGameAction(GameAction_New_Piece_X, posX);
392         mpGameMessage->putGameAction(GameAction_New_Piece_Y, posY);
393     }
394 }
395 
396