1 //
2 // Cross-platform free Puyo-Puyo clone.
3 // Copyright (C) 2006, 2007 Emma's Software
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 // 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, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 #if defined (HAVE_CONFIG_H)
20 #include <config.h>
21 #endif // HAVE_CONFIG_H
22 #include <algorithm>
23 #include <assert.h>
24 #include <cmath>
25 #include "ChainLabel.h"
26 #include "File.h"
27 #include "Grid.h"
28 #include "GridStatus.h"
29 #include "PairGenerator.h"
30 #include "System.h"
31 
32 using namespace Amoebax;
33 
34 // Constants.
35 static const float k_Cos[360] = {1.000000f, 0.999848f, 0.999391f, 0.998630f,
36                                  0.997564f, 0.996195f, 0.994522f, 0.992546f,
37                                  0.990268f, 0.987688f, 0.984808f, 0.981627f,
38                                  0.978148f, 0.974370f, 0.970296f, 0.965926f,
39                                  0.961262f, 0.956305f, 0.951057f, 0.945519f,
40                                  0.939693f, 0.933580f, 0.927184f, 0.920505f,
41                                  0.913545f, 0.906308f, 0.898794f, 0.891007f,
42                                  0.882948f, 0.874620f, 0.866025f, 0.857167f,
43                                  0.848048f, 0.838671f, 0.829038f, 0.819152f,
44                                  0.809017f, 0.798636f, 0.788011f, 0.777146f,
45                                  0.766044f, 0.754710f, 0.743145f, 0.731354f,
46                                  0.719340f, 0.707107f, 0.694658f, 0.681998f,
47                                  0.669131f, 0.656059f, 0.642788f, 0.629320f,
48                                  0.615661f, 0.601815f, 0.587785f, 0.573576f,
49                                  0.559193f, 0.544639f, 0.529919f, 0.515038f,
50                                  0.500000f, 0.484810f, 0.469472f, 0.453990f,
51                                  0.438371f, 0.422618f, 0.406737f, 0.390731f,
52                                  0.374607f, 0.358368f, 0.342020f, 0.325568f,
53                                  0.309017f, 0.292372f, 0.275637f, 0.258819f,
54                                  0.241922f, 0.224951f, 0.207912f, 0.190809f,
55                                  0.173648f, 0.156434f, 0.139173f, 0.121869f,
56                                  0.104528f, 0.087156f, 0.069756f, 0.052336f,
57                                  0.034899f, 0.017452f, 0.000000f, -0.017452f,
58                                  -0.034899f, -0.052336f, -0.069756f, -0.087156f,
59                                  -0.104528f, -0.121869f, -0.139173f, -0.156434f,
60                                  -0.173648f, -0.190809f, -0.207912f, -0.224951f,
61                                  -0.241922f, -0.258819f, -0.275637f, -0.292372f,
62                                  -0.309017f, -0.325568f, -0.342020f, -0.358368f,
63                                  -0.374607f, -0.390731f, -0.406737f, -0.422618f,
64                                  -0.438371f, -0.453990f, -0.469472f, -0.484810f,
65                                  -0.500000f, -0.515038f, -0.529919f, -0.544639f,
66                                  -0.559193f, -0.573576f, -0.587785f, -0.601815f,
67                                  -0.615661f, -0.629320f, -0.642788f, -0.656059f,
68                                  -0.669131f, -0.681998f, -0.694658f, -0.707107f,
69                                  -0.719340f, -0.731354f, -0.743145f, -0.754710f,
70                                  -0.766044f, -0.777146f, -0.788011f, -0.798636f,
71                                  -0.809017f, -0.819152f, -0.829038f, -0.838671f,
72                                  -0.848048f, -0.857167f, -0.866025f, -0.874620f,
73                                  -0.882948f, -0.891007f, -0.898794f, -0.906308f,
74                                  -0.913545f, -0.920505f, -0.927184f, -0.933580f,
75                                  -0.939693f, -0.945519f, -0.951057f, -0.956305f,
76                                  -0.961262f, -0.965926f, -0.970296f, -0.974370f,
77                                  -0.978148f, -0.981627f, -0.984808f, -0.987688f,
78                                  -0.990268f, -0.992546f, -0.994522f, -0.996195f,
79                                  -0.997564f, -0.998630f, -0.999391f, -0.999848f,
80                                  -1.000000f, -0.999848f, -0.999391f, -0.998630f,
81                                  -0.997564f, -0.996195f, -0.994522f, -0.992546f,
82                                  -0.990268f, -0.987688f, -0.984808f, -0.981627f,
83                                  -0.978148f, -0.974370f, -0.970296f, -0.965926f,
84                                  -0.961262f, -0.956305f, -0.951057f, -0.945519f,
85                                  -0.939693f, -0.933580f, -0.927184f, -0.920505f,
86                                  -0.913545f, -0.906308f, -0.898794f, -0.891007f,
87                                  -0.882948f, -0.874620f, -0.866025f, -0.857167f,
88                                  -0.848048f, -0.838671f, -0.829038f, -0.819152f,
89                                  -0.809017f, -0.798636f, -0.788011f, -0.777146f,
90                                  -0.766044f, -0.754710f, -0.743145f, -0.731354f,
91                                  -0.719340f, -0.707107f, -0.694658f, -0.681998f,
92                                  -0.669131f, -0.656059f, -0.642788f, -0.629320f,
93                                  -0.615661f, -0.601815f, -0.587785f, -0.573576f,
94                                  -0.559193f, -0.544639f, -0.529919f, -0.515038f,
95                                  -0.500000f, -0.484810f, -0.469472f, -0.453990f,
96                                  -0.438371f, -0.422618f, -0.406737f, -0.390731f,
97                                  -0.374607f, -0.358368f, -0.342020f, -0.325568f,
98                                  -0.309017f, -0.292372f, -0.275637f, -0.258819f,
99                                  -0.241922f, -0.224951f, -0.207912f, -0.190809f,
100                                  -0.173648f, -0.156434f, -0.139173f, -0.121869f,
101                                  -0.104528f, -0.087156f, -0.069756f, -0.052336f,
102                                  -0.034899f, -0.017452f, -0.000000f, 0.017452f,
103                                  0.034899f, 0.052336f, 0.069756f, 0.087156f,
104                                  0.104528f, 0.121869f, 0.139173f, 0.156434f,
105                                  0.173648f, 0.190809f, 0.207912f, 0.224951f,
106                                  0.241922f, 0.258819f, 0.275637f, 0.292372f,
107                                  0.309017f, 0.325568f, 0.342020f, 0.358368f,
108                                  0.374607f, 0.390731f, 0.406737f, 0.422618f,
109                                  0.438371f, 0.453990f, 0.469472f, 0.484810f,
110                                  0.500000f, 0.515038f, 0.529919f, 0.544639f,
111                                  0.559193f, 0.573576f, 0.587785f, 0.601815f,
112                                  0.615661f, 0.629320f, 0.642788f, 0.656059f,
113                                  0.669131f, 0.681998f, 0.694658f, 0.707107f,
114                                  0.719340f, 0.731354f, 0.743145f, 0.754710f,
115                                  0.766044f, 0.777146f, 0.788011f, 0.798636f,
116                                  0.809017f, 0.819152f, 0.829038f, 0.838671f,
117                                  0.848048f, 0.857167f, 0.866025f, 0.874620f,
118                                  0.882948f, 0.891007f, 0.898794f, 0.906308f,
119                                  0.913545f, 0.920505f, 0.927184f, 0.933580f,
120                                  0.939693f, 0.945519f, 0.951057f, 0.956305f,
121                                  0.961262f, 0.965926f, 0.970296f, 0.974370f,
122                                  0.978148f, 0.981627f, 0.984808f, 0.987688f,
123                                  0.990268f, 0.992546f, 0.994522f, 0.996195f,
124                                  0.997564f, 0.998630f, 0.999391f, 0.999848f};
125 
126 static const float k_Sin[360] = {0.000000f, 0.017452f, 0.034899f, 0.052336f,
127                                  0.069756f, 0.087156f, 0.104528f, 0.121869f,
128                                  0.139173f, 0.156434f, 0.173648f, 0.190809f,
129                                  0.207912f, 0.224951f, 0.241922f, 0.258819f,
130                                  0.275637f, 0.292372f, 0.309017f, 0.325568f,
131                                  0.342020f, 0.358368f, 0.374607f, 0.390731f,
132                                  0.406737f, 0.422618f, 0.438371f, 0.453990f,
133                                  0.469472f, 0.484810f, 0.500000f, 0.515038f,
134                                  0.529919f, 0.544639f, 0.559193f, 0.573576f,
135                                  0.587785f, 0.601815f, 0.615661f, 0.629320f,
136                                  0.642788f, 0.656059f, 0.669131f, 0.681998f,
137                                  0.694658f, 0.707107f, 0.719340f, 0.731354f,
138                                  0.743145f, 0.754710f, 0.766044f, 0.777146f,
139                                  0.788011f, 0.798636f, 0.809017f, 0.819152f,
140                                  0.829038f, 0.838671f, 0.848048f, 0.857167f,
141                                  0.866025f, 0.874620f, 0.882948f, 0.891007f,
142                                  0.898794f, 0.906308f, 0.913545f, 0.920505f,
143                                  0.927184f, 0.933580f, 0.939693f, 0.945519f,
144                                  0.951057f, 0.956305f, 0.961262f, 0.965926f,
145                                  0.970296f, 0.974370f, 0.978148f, 0.981627f,
146                                  0.984808f, 0.987688f, 0.990268f, 0.992546f,
147                                  0.994522f, 0.996195f, 0.997564f, 0.998630f,
148                                  0.999391f, 0.999848f, 1.000000f, 0.999848f,
149                                  0.999391f, 0.998630f, 0.997564f, 0.996195f,
150                                  0.994522f, 0.992546f, 0.990268f, 0.987688f,
151                                  0.984808f, 0.981627f, 0.978148f, 0.974370f,
152                                  0.970296f, 0.965926f, 0.961262f, 0.956305f,
153                                  0.951057f, 0.945519f, 0.939693f, 0.933580f,
154                                  0.927184f, 0.920505f, 0.913545f, 0.906308f,
155                                  0.898794f, 0.891007f, 0.882948f, 0.874620f,
156                                  0.866025f, 0.857167f, 0.848048f, 0.838671f,
157                                  0.829038f, 0.819152f, 0.809017f, 0.798636f,
158                                  0.788011f, 0.777146f, 0.766044f, 0.754710f,
159                                  0.743145f, 0.731354f, 0.719340f, 0.707107f,
160                                  0.694658f, 0.681998f, 0.669131f, 0.656059f,
161                                  0.642788f, 0.629320f, 0.615661f, 0.601815f,
162                                  0.587785f, 0.573576f, 0.559193f, 0.544639f,
163                                  0.529919f, 0.515038f, 0.500000f, 0.484810f,
164                                  0.469472f, 0.453990f, 0.438371f, 0.422618f,
165                                  0.406737f, 0.390731f, 0.374607f, 0.358368f,
166                                  0.342020f, 0.325568f, 0.309017f, 0.292372f,
167                                  0.275637f, 0.258819f, 0.241922f, 0.224951f,
168                                  0.207912f, 0.190809f, 0.173648f, 0.156434f,
169                                  0.139173f, 0.121869f, 0.104528f, 0.087156f,
170                                  0.069756f, 0.052336f, 0.034899f, 0.017452f,
171                                  0.000000f, -0.017452f, -0.034899f, -0.052336f,
172                                  -0.069756f, -0.087156f, -0.104528f, -0.121869f,
173                                  -0.139173f, -0.156434f, -0.173648f, -0.190809f,
174                                  -0.207912f, -0.224951f, -0.241922f, -0.258819f,
175                                  -0.275637f, -0.292372f, -0.309017f, -0.325568f,
176                                  -0.342020f, -0.358368f, -0.374607f, -0.390731f,
177                                  -0.406737f, -0.422618f, -0.438371f, -0.453990f,
178                                  -0.469472f, -0.484810f, -0.500000f, -0.515038f,
179                                  -0.529919f, -0.544639f, -0.559193f, -0.573576f,
180                                  -0.587785f, -0.601815f, -0.615661f, -0.629320f,
181                                  -0.642788f, -0.656059f, -0.669131f, -0.681998f,
182                                  -0.694658f, -0.707107f, -0.719340f, -0.731354f,
183                                  -0.743145f, -0.754710f, -0.766044f, -0.777146f,
184                                  -0.788011f, -0.798636f, -0.809017f, -0.819152f,
185                                  -0.829038f, -0.838671f, -0.848048f, -0.857167f,
186                                  -0.866025f, -0.874620f, -0.882948f, -0.891007f,
187                                  -0.898794f, -0.906308f, -0.913545f, -0.920505f,
188                                  -0.927184f, -0.933580f, -0.939693f, -0.945519f,
189                                  -0.951057f, -0.956305f, -0.961262f, -0.965926f,
190                                  -0.970296f, -0.974370f, -0.978148f, -0.981627f,
191                                  -0.984808f, -0.987688f, -0.990268f, -0.992546f,
192                                  -0.994522f, -0.996195f, -0.997564f, -0.998630f,
193                                  -0.999391f, -0.999848f, -1.000000f, -0.999848f,
194                                  -0.999391f, -0.998630f, -0.997564f, -0.996195f,
195                                  -0.994522f, -0.992546f, -0.990268f, -0.987688f,
196                                  -0.984808f, -0.981627f, -0.978148f, -0.974370f,
197                                  -0.970296f, -0.965926f, -0.961262f, -0.956305f,
198                                  -0.951057f, -0.945519f, -0.939693f, -0.933580f,
199                                  -0.927184f, -0.920505f, -0.913545f, -0.906308f,
200                                  -0.898794f, -0.891007f, -0.882948f, -0.874620f,
201                                  -0.866025f, -0.857167f, -0.848048f, -0.838671f,
202                                  -0.829038f, -0.819152f, -0.809017f, -0.798636f,
203                                  -0.788011f, -0.777146f, -0.766044f, -0.754710f,
204                                  -0.743145f, -0.731354f, -0.719340f, -0.707107f,
205                                  -0.694658f, -0.681998f, -0.669131f, -0.656059f,
206                                  -0.642788f, -0.629320f, -0.615661f, -0.601815f,
207                                  -0.587785f, -0.573576f, -0.559193f, -0.544639f,
208                                  -0.529919f, -0.515038f, -0.500000f, -0.484810f,
209                                  -0.469472f, -0.453990f, -0.438371f, -0.422618f,
210                                  -0.406737f, -0.390731f, -0.374607f, -0.358368f,
211                                  -0.342020f, -0.325568f, -0.309017f, -0.292372f,
212                                  -0.275637f, -0.258819f, -0.241922f, -0.224951f,
213                                  -0.207912f, -0.190809f, -0.173648f, -0.156434f,
214                                  -0.139173f, -0.121869f, -0.104528f, -0.087156f,
215                                  -0.069756f, -0.052336f, -0.034899f, -0.017452f};
216 
217 // The speed of the horizontal movement of the queue.
218 static const float k_QueueHorizontalSpeed = 0.10f;
219 // The speed of the vertical movement of the queue.
220 static const float k_QueueVerticalSpeed = 0.25f;
221 // The speed of the satellite rotation, in degress/ms.
222 static const float k_RotationSpeed = 0.5f;
223 
224 ///
225 /// \brief Constructor.
226 ///
227 /// \param gridPositionX The X position of the grid's top-left corner.
228 /// \param gridPositionY The Y position of the grid's top-left corner.
229 /// \param queuePositionX The X position of queue's top-left corner,
230 ///                       where the amoebas wait to fall.
231 /// \param queuePositionY The Y position of queue's top-left corner,
232 ///                       where the amoebas wait to fall.
233 /// \param waitingGhostPositionX The X position of the top-left corner
234 ///                              where the ghost amoebas wait to fall.
235 /// \param waitingGhostPositionY The Y position of the top-left corner
236 ///                              where the ghost amoebas wait to fall.
237 /// \param amoebaSize The size of a single amoeba. The amoebas must be
238 ///                   squared, so the width and the height are the same.
239 /// \param queueSide The side where the queue is located at.
240 /// \param score The initial grid's score.
241 /// \param layout The grid's layout.
242 ///
Grid(uint16_t gridPositionX,uint16_t gridPositionY,uint16_t queuePositionX,uint16_t queuePositionY,uint16_t waitingGhostPositionX,uint16_t waitingGhostPositionY,uint16_t amoebaSize,QueueSide queueSide,uint32_t score,Layout layout)243 Grid::Grid (uint16_t gridPositionX, uint16_t gridPositionY,
244             uint16_t queuePositionX, uint16_t queuePositionY,
245             uint16_t waitingGhostPositionX, uint16_t waitingGhostPositionY,
246             uint16_t amoebaSize, QueueSide queueSide, uint32_t score,
247             Layout layout):
248     m_ActiveAmoebas (0),
249     m_AmoebaSize (amoebaSize),
250     m_BlinkTime (0),
251     m_ChainLabels (0),
252     m_CurrentStepChain (0),
253     m_CurrentRotationDegrees (0.0f),
254     m_DegreesUntilRotationDone (0.0f),
255     m_DyingAmoebas (0),
256     m_DyingTime (0),
257     m_FallingPair (),
258     m_Filled (false),
259     m_FirstFallingPair (true),
260     m_FloatingAmoebas (0),
261     m_FloatingAmoebasVerticalOffset (0),
262     m_Generator (0),
263     m_Grid (k_GridHeight * k_GridWidth, static_cast<Amoeba *>(0)),
264     m_GridPositionX (gridPositionX),
265     m_GridPositionY (gridPositionY),
266     m_HasNewFallingPair (false),
267     m_InactiveAmoebas (0),
268     m_Layout (layout),
269     m_MaxFallingSpeed (false),
270     m_OpponentGhostAmoebas (0),
271     m_Queue (0),
272     m_QueuedAmoebas (0),
273     m_QueueIsMoving (false),
274     m_QueueSide (queueSide),
275     m_QueuePositionX (queuePositionX),
276     m_QueuePositionY (queuePositionY),
277     m_RemainingQueueHorizontalOffset (0),
278     m_RemainingQueueVerticalOffset (0),
279     m_RotationDirection (1.0f),
280     m_Score (score),
281     m_SilhouetteFrame (0),
282     m_SilhouetteFrameDirection (-1),
283     m_SilhouetteTime (0),
284     m_WaitingInitialAmoebas (true),
285     m_WaitingGhostAmoebas (0),
286     m_WaitingGhostAmoebasNumber (0),
287     m_WaitingGhostPositionX (waitingGhostPositionX),
288     m_WaitingGhostPositionY (waitingGhostPositionY)
289 {
290     // Load sounds.
291     m_DieSound.reset (Sound::fromFile (File::getSoundFilePath ("die.wav")));
292 
293     // Create the waiting ghost amoebas.
294     // There's only 6 waiting amoebas, but the "neighbour" state
295     // of each ghost amoebas tells how many "real" ghosts will fall from
296     // this amoeba (max. 5 for each.)
297     for ( uint8_t currentAmoeba = 0 ; currentAmoeba < 6 ; ++currentAmoeba )
298     {
299         m_WaitingGhostAmoebas.push_back (new Amoeba (Amoeba::ColourGhost));
300         m_WaitingGhostAmoebas[currentAmoeba]->setState (Amoeba::StateTopRightBottomLeft);
301         if ( Grid::LayoutVertical == getLayout () )
302         {
303             m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY);
304             m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX +
305                                                         getAmoebaSize () *
306                                                         currentAmoeba);
307         }
308         else
309         {
310             if ( Grid::QueueSideLeft == getQueueSide () )
311             {
312                 m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX -
313                                                             getAmoebaSize ());
314                 m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY +
315                                                             getAmoebaSize () *
316                                                             currentAmoeba);
317 
318             }
319             else
320             {
321                 m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX);
322                 m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY -
323                                                             getAmoebaSize () *
324                                                             (currentAmoeba + 1));
325             }
326         }
327     }
328 }
329 
330 ///
331 /// \brief Destructor.
332 ///
~Grid(void)333 Grid::~Grid (void)
334 {
335     std::for_each (m_ActiveAmoebas.begin (), m_ActiveAmoebas.end (),
336                    DeleteObject<Amoeba> ());
337     m_ActiveAmoebas.clear ();
338 
339     std::for_each (m_ChainLabels.begin (), m_ChainLabels.end (),
340                    DeleteObject<ChainLabel> ());
341     m_ChainLabels.clear ();
342 
343     std::for_each (m_InactiveAmoebas.begin (), m_InactiveAmoebas.end (),
344                    DeleteObject<Amoeba> ());
345     m_InactiveAmoebas.clear ();
346 
347     std::for_each (m_QueuedAmoebas.begin (), m_QueuedAmoebas.end (),
348                    DeleteObject<Amoeba> ());
349     m_QueuedAmoebas.clear ();
350 
351     std::for_each (m_WaitingGhostAmoebas.begin (), m_WaitingGhostAmoebas.end (),
352                    DeleteObject<Amoeba> ());
353     m_WaitingGhostAmoebas.clear ();
354 }
355 
356 ///
357 /// \brief Adds a new step chain label.
358 ///
359 /// An step chain label is the text that appears on the grid
360 /// when a chain is made to let know the user which step the
361 /// chain was made in. The label will move a little to the top
362 /// and then will disappear.
363 ///
364 /// \param stepChain The step in which the chain was made in. It starts
365 ///                  from 1, not 0.
366 /// \param x The screen X position where the label should appear centered at.
367 /// \param y The screen Y position where the label should appear centered at.
368 ///
369 void
addChainLabel(uint8_t stepChain,int16_t x,int16_t y)370 Grid::addChainLabel (uint8_t stepChain, int16_t x, int16_t y)
371 {
372     m_ChainLabels.push_back (new ChainLabel (stepChain, x, y,
373                                              y - getAmoebaSize ()));
374 }
375 
376 ///
377 /// \brief Adds a new amoebas pair.
378 ///
379 /// \param main The main amoeba of the pair.
380 /// \param satellite The satellite amoeba of the pair. This is the one that
381 ///                  will rotate around \a main.
382 ///
383 void
addNewPair(Amoeba * main,Amoeba * satellite)384 Grid::addNewPair (Amoeba *main, Amoeba *satellite)
385 {
386     // Just add the to the list of waiting amoebas.
387     m_InactiveAmoebas.push_back (main);
388     m_InactiveAmoebas.push_back (satellite);
389     // Set up the initial amoebas.
390     // We need to how 6 amoebas for the set up: The falling pair and
391     // the two waiting pairs.
392     if ( m_WaitingInitialAmoebas && 8 == m_InactiveAmoebas.size () )
393     {
394         m_WaitingInitialAmoebas = false;
395         getNextPair ();
396         getNextPair ();
397         setupFallingPair ();
398     }
399 }
400 
401 ///
402 /// \brief Removes all dying amoebas.
403 ///
404 /// Once all dying amoebas are deleted checks for floating amoebas and if
405 /// no floating amoeba is found then sets up the falling pair.
406 ///
407 void
clearDyingAmoebas(void)408 Grid::clearDyingAmoebas (void)
409 {
410     m_DieSound->play ();
411 
412     uint8_t amoebasErased = 0;
413     for (std::vector<FallingAmoeba>::iterator currentAmoeba =
414             m_DyingAmoebas.begin () ;
415          currentAmoeba != m_DyingAmoebas.end () ; ++currentAmoeba )
416     {
417         if ( Amoeba::ColourGhost != currentAmoeba->amoeba->getColour () )
418         {
419             ++amoebasErased;
420         }
421         setAmoebaAt (currentAmoeba->x, currentAmoeba->y, 0);
422         m_ActiveAmoebas.remove (currentAmoeba->amoeba);
423         delete currentAmoeba->amoeba;
424     }
425     m_DyingAmoebas.clear ();
426 
427     // Activates all chain label.
428     std::for_each (getChainLabels ().begin (), getChainLabels ().end (),
429                    std::mem_fun (&ChainLabel::activate));
430 
431     // Increment the score based on the number of non-ghost amoebas erased
432     // and the current step chain (starting from 0.)
433     uint16_t score = ((2 * amoebasErased - 4) *
434                       10) << getCurrentStepChain ();
435     incrementScore (score);
436     incrementCurrentStepChain ();
437     // Compute the number of ghost amoebas the score generates, subtracts
438     // the number of waiting amoebas and then set the remaining
439     // ghost amoebas as ghost amoebas for the opponent.
440     uint8_t newGhostAmoebas = static_cast<uint16_t>(std::ceil (score / 70.0f));
441     uint8_t remainingGhostAmoebas = removeGhostAmoebas (newGhostAmoebas);
442     setOpponentGhostAmoebas (remainingGhostAmoebas);
443 
444     findFloatingAmoebas ();
445     // If there's no floating amoebas, set up the falling pair.
446     if ( m_FloatingAmoebas.empty () )
447     {
448         // If the positions above the grid of the 3rd column (from
449         // the right) is occuped by an amoeba, then the grid is
450         // considered filled.  If any amobea is sitting over the grid in
451         // any other column, this is not considered filled.
452         if ( 0 != getAmoebaAt (3, k_FirstVisibleHeight - 1) )
453         {
454             markAsFilled ();
455         }
456         else
457         {
458             scheduleFallingPair ();
459         }
460     }
461 }
462 
463 ///
464 /// \brief Finds floating amoebas.
465 ///
466 /// A floating amoeba is an amoeaba that just lost its supporting amoeba
467 /// and hence "is floating" on the air and must start to fall.
468 ///
469 /// Checks each and every amoeba, except for those in the last line, to
470 /// see if it's a floating amoeba and adds it to the m_FloatingAmoeba
471 /// list if so.
472 ///
473 void
findFloatingAmoebas(void)474 Grid::findFloatingAmoebas (void)
475 {
476     // Now check which amoebas are "floating" and make them fall.
477     m_FloatingAmoebasVerticalOffset = 0;
478     for ( int16_t row = k_GridHeight - 2 ; row >= 0 ; --row )
479     {
480         for ( int16_t column = 0 ; column < k_GridWidth ; ++column )
481         {
482             Amoeba *amoeba = getAmoebaAt (column, row);
483             if ( 0 != amoeba && 0 == getAmoebaAt (column, row + 1) )
484             {
485                 // This amoeba no longer occupes the current position.
486                 setAmoebaAt (column, row, 0);
487                 // Now is a falling amoeba.
488                 FallingAmoeba floatingAmoeba;
489                 floatingAmoeba.amoeba = amoeba;
490                 floatingAmoeba.x = column;
491                 floatingAmoeba.y = row;
492                 m_FloatingAmoebas.push_back (floatingAmoeba);
493                 // Set the state of the current and neighbour amoebas.
494                 amoeba->setState (Amoeba::StateNone);
495                 setAmoebaStateAt (column, row, true);
496             }
497         }
498     }
499 }
500 
501 ///
502 /// \brief Gets the amoeba at a given grid's position.
503 ///
504 /// \param x The X position of the grid to get the amoeba from.
505 /// \param y The Y position of the grid to get the amoeba from.
506 /// \return The amoeba at position \p x and \p y or 0 if there's
507 ///         no amoeba at this position or the position is incorrect (i.e.,
508 ///         out of grid's limits.)
509 ///
510 Amoeba *
getAmoebaAt(int16_t x,int16_t y) const511 Grid::getAmoebaAt (int16_t x, int16_t y) const
512 {
513     if ( 0 <= x && x < k_GridWidth &&
514          0 <= y && y < k_GridHeight )
515     {
516         return m_Grid[y * k_GridWidth + x];
517     }
518     return 0;
519 }
520 
521 ///
522 /// \brief Gets the current satellite rotation degree.
523 ///
524 /// \return The current satellite rotation degree.
525 ///
526 inline int16_t
getCurrentRotationDegree(void) const527 Grid::getCurrentRotationDegree (void) const
528 {
529     return static_cast<int16_t> (m_CurrentRotationDegrees);
530 }
531 
532 ///
533 /// \brief Gets the main amoeba of the falling pair.
534 ///
535 /// \return The main falling amoeba of the falling pair.
536 ///
537 Grid::FallingAmoeba
getFallingMainAmoeba(void) const538 Grid::getFallingMainAmoeba (void) const
539 {
540     return m_FallingPair.main;
541 }
542 
543 ///
544 /// \brief Gets the satellite amoeba of the falling pair.
545 ///
546 /// \return The satellite falling amoeba of the falling pair.
547 ///
548 Grid::FallingAmoeba
getFallingSatelliteAmoeba(void) const549 Grid::getFallingSatelliteAmoeba (void) const
550 {
551     return m_FallingPair.satellite;
552 }
553 
554 ///
555 /// \brief Gets the main amoeba that is going to fall after the next pair.
556 ///
557 /// Since this amoeba doesn't have a position, yet, we set it to
558 /// bet at the center of the grid at the top position.
559 ///
560 /// \return The main amoeba that is going to fall after the next pair.
561 ///
562 Grid::FallingAmoeba
getFollowingFallingMainAmoeba(void) const563 Grid::getFollowingFallingMainAmoeba (void) const
564 {
565     FallingAmoeba followingMain;
566 
567     followingMain.x = k_GridWidth / 2;
568     followingMain.y = -1;
569     std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
570     ++pair;
571     assert (m_Queue.end () != pair &&
572             "The pairs queue doesn't have two pairs" );
573     followingMain.amoeba = pair->main.amoeba;
574 
575     return followingMain;
576 }
577 
578 ///
579 /// \brief Gets the satellite amoeba that is going to fall after the next pair.
580 ///
581 /// Since this amoeba doesn't have a position, yet, we set it to
582 /// bet at the center of the grid at the top position.
583 ///
584 /// \return The satellite amoeba that is going to fall after the next pair.
585 ///
586 Grid::FallingAmoeba
getFollowingFallingSatelliteAmoeba(void) const587 Grid::getFollowingFallingSatelliteAmoeba (void) const
588 {
589     FallingAmoeba followingSatellite;
590 
591     followingSatellite.x = k_GridWidth / 2;
592     followingSatellite.y = -1;
593     std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
594     ++pair;
595     assert (m_Queue.end () != pair &&
596             "The pairs queue doesn't have two pairs" );
597     followingSatellite.amoeba = pair->satellite.amoeba;
598 
599     return followingSatellite;
600 }
601 
602 ///
603 /// \brief Gets the main amoeba that is going to fall next.
604 ///
605 /// Since this amoeba doesn't have a position, yet, we set it to
606 /// be at the center of the grid at the top position.
607 ///
608 /// \return The main amoeba that is going to fall next.
609 ///
610 Grid::FallingAmoeba
getNextFallingMainAmoeba(void) const611 Grid::getNextFallingMainAmoeba (void) const
612 {
613     FallingAmoeba nextMain;
614 
615     nextMain.x = k_GridWidth / 2;
616     nextMain.y = -1;
617     std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
618     assert (m_Queue.end () != pair && "The pairs queue is empty" );
619     nextMain.amoeba = pair->main.amoeba;
620 
621     return nextMain;
622 }
623 
624 ///
625 /// \brief Gets the main amoeba that is going to fall next.
626 ///
627 /// Since this amoeba doesn't have a position, yet, we set it to
628 /// be at the center of the grid at the top position.
629 ///
630 /// \return The main amoeba that is going to fall next.
631 ///
632 Grid::FallingAmoeba
getNextFallingSatelliteAmoeba(void) const633 Grid::getNextFallingSatelliteAmoeba (void) const
634 {
635     FallingAmoeba nextSatellite;
636 
637     nextSatellite.x = k_GridWidth / 2;
638     nextSatellite.y = -2;
639     std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
640     assert (m_Queue.end () != pair && "The pairs queue is empty" );
641     nextSatellite.amoeba = pair->satellite.amoeba;
642 
643     return nextSatellite;
644 }
645 
646 ///
647 /// \brief Gets the next pair from the inactive list and sets it to the queue.
648 ///
649 /// The next pair will be the last element in the queue, so it should
650 /// be called when the first element of the queue becomes the falling pair.
651 ///
652 /// If the inactive amoebas list becomes empty, this function request
653 /// to the pair generator a new pair.
654 ///
655 void
getNextPair(void)656 Grid::getNextPair (void)
657 {
658     assert ( 0 != m_Generator &&
659             "Tried to get the next pair without a generator.");
660     assert ( 2 <= m_InactiveAmoebas.size () &&
661             "Tried to get the next pair, but the inactive list is empty.");
662 
663     // Gets the next pair from the inactive list.
664     Amoeba *main = m_InactiveAmoebas.front ();
665     m_InactiveAmoebas.pop_front ();
666     Amoeba *satellite = m_InactiveAmoebas.front ();
667     m_InactiveAmoebas.pop_front ();
668     // And set them to queued.
669     m_QueuedAmoebas.push_back (main);
670     m_QueuedAmoebas.push_back (satellite);
671     // Sets they position to be the last pair on the queue.
672     // Take into account the side where the queue is located at
673     // to choose the offset to apply to this pair.
674     uint16_t lastPairOffsetX = getAmoebaSize () / 2;
675     if ( LayoutVertical == getLayout () )
676     {
677         if ( Grid::QueueSideLeft == getQueueSide () )
678         {
679             lastPairOffsetX = -lastPairOffsetX;
680         }
681         main->setX (getQueuePositionX () + lastPairOffsetX);
682         main->setY (getQueuePositionY () + getAmoebaSize () * 3);
683         satellite->setX (getQueuePositionX () + lastPairOffsetX);
684         satellite->setY (getQueuePositionY () + getAmoebaSize () * 2);
685     }
686     else
687     {
688         if (Grid::QueueSideLeft == getQueueSide () )
689         {
690             main->setX (getQueuePositionX () - getAmoebaSize () * 4);
691             main->setY (getQueuePositionY () + lastPairOffsetX);
692             satellite->setX (getQueuePositionX () - getAmoebaSize () * 3);
693             satellite->setY (getQueuePositionY () + lastPairOffsetX);
694         }
695         else
696         {
697             main->setX (getQueuePositionX () + getAmoebaSize () * 3);
698             main->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
699             satellite->setX (getQueuePositionX () + getAmoebaSize () * 2);
700             satellite->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
701         }
702     }
703     // And set up the falling pair structure for when they fall.
704     FallingPair fallingPair;
705     fallingPair.main.amoeba = main;
706     fallingPair.main.x = 3;
707     fallingPair.main.y = k_FirstVisibleHeight - 2;
708     fallingPair.satellite.amoeba = satellite;
709     fallingPair.satellite.x = 3;
710     fallingPair.satellite.y = k_FirstVisibleHeight - 3;
711     fallingPair.verticalOffset = 0;
712     fallingPair.fallingTime = k_DefaultFallingTime;
713     m_Queue.push_back (fallingPair);
714     // Request a new pair if we are out of inactive pairs.
715     if ( m_InactiveAmoebas.empty () )
716     {
717         m_Generator->generate ();
718     }
719 }
720 
721 ///
722 /// \brief Gets the side of the queue.
723 ///
724 /// \return The side of the grid where the queue is located at.
725 ///
726 Grid::QueueSide
getQueueSide(void) const727 Grid::getQueueSide (void) const
728 {
729     return m_QueueSide;
730 }
731 
732 ///
733 /// \brief Gets the queue's top-left corner X position.
734 ///
735 /// \return The X position of queue's top-left corner.
736 ///
737 uint16_t
getQueuePositionX(void) const738 Grid::getQueuePositionX (void) const
739 {
740     return m_QueuePositionX;
741 }
742 
743 ///
744 /// \brief Gets the queue's top-left corner Y position.
745 ///
746 /// \return The Y position of queue's top-left corner.
747 ///
748 uint16_t
getQueuePositionY(void) const749 Grid::getQueuePositionY (void) const
750 {
751     return m_QueuePositionY;
752 }
753 
754 ///
755 /// \brief Gets the current grid's state.
756 ///
757 /// \return The grid's state.
758 ///
759 GridStatus
getState(void) const760 Grid::getState (void) const
761 {
762     std::vector<Amoeba::Colour> state (k_GridWidth * k_GridHeight,
763                                        Amoeba::ColourNone);
764     assert (state.size () == m_Grid.size () &&
765             "The state's size and the grid's size doesn't match.");
766     for ( uint16_t currentAmoeba = 0 ; currentAmoeba < state.size () ;
767           ++currentAmoeba )
768     {
769         Amoeba *amoeba = m_Grid.at (currentAmoeba);
770         if ( 0 != amoeba )
771         {
772             state.at (currentAmoeba) = amoeba->getColour ();
773         }
774     }
775     return GridStatus (state);
776 }
777 
778 ///
779 /// \brief Gets the waiting ghost's top-left corner X position.
780 ///
781 /// \return The X position of the waitings ghost's top-left corner.
782 ///
783 uint16_t
getWaitingGhostPositionX(void) const784 Grid::getWaitingGhostPositionX (void) const
785 {
786     return m_WaitingGhostPositionX;
787 }
788 
789 ///
790 /// \brief Gets the waiting ghost's top-left corner Y position.
791 ///
792 /// \return The Y position of the waitings ghost's top-left corner.
793 ///
794 uint16_t
getWaitingGhostPositionY(void) const795 Grid::getWaitingGhostPositionY (void) const
796 {
797     return m_WaitingGhostPositionY;
798 }
799 
800 ///
801 /// \brief Checks if an amoeba at a give position is the same colour of another.
802 ///
803 /// \param x The grid's X position of the amoeba to check its colour.
804 /// \param y The grid's Y position of the amoeba to check its colour.
805 /// \param amoeba The amoeba to check if its colour is the same as the
806 ///               amoeba at \a x i \a y.
807 /// \return \a true if the amoeba at \a x and \a y if of the same colour as
808 ///         \a amoeba.
809 ///
810 bool
isOfSameColour(int16_t x,int16_t y,Amoeba * amoeba) const811 Grid::isOfSameColour (int16_t x, int16_t y, Amoeba *amoeba) const
812 {
813     assert ( 0 != amoeba && "Tried to check the colour of a null amoeba.");
814 
815     Amoeba *amoebaInGrid = getAmoebaAt (x, y);
816     if ( 0 != amoebaInGrid )
817     {
818         return amoebaInGrid->getColour () == amoeba->getColour ();
819     }
820     return false;
821 }
822 
823 ///
824 /// \brief Tells if the queue is moving and needs to be updated.
825 ///
826 /// \return \a true if the amoebas in the queue are moving and needs to
827 ///         be updated, \a false otherwise.
828 ///
829 inline bool
isQueueMoving(void) const830 Grid::isQueueMoving (void) const
831 {
832     return m_QueueIsMoving;
833 }
834 
835 ///
836 /// \brief Tells if the satellite amoeba is rotating.
837 ///
838 /// \return \a true if the satellite amoeba is rotating.
839 ///         \a false otherwise.
840 ///
841 inline bool
isSatelliteRotating(void) const842 Grid::isSatelliteRotating (void) const
843 {
844     return 0 != static_cast<int16_t> (m_DegreesUntilRotationDone);
845 }
846 
847 ///
848 /// \brief Increments the number of ghosts amoebas.
849 ///
850 /// \param amount The amount to increment the number of ghost amoebas
851 ///               that are waiting to fall into the grid.
852 ///
853 void
incrementNumberOfWaitingGhosts(uint8_t amount)854 Grid::incrementNumberOfWaitingGhosts (uint8_t amount)
855 {
856     setNumberOfWaitingGhosts (getNumberOfWaitingGhosts () + amount);
857     updateWaitingGhosts ();
858 }
859 
860 ///
861 /// \brief Sets the waiting ghost as falling amoebas.
862 ///
863 void
makeGhostsFall(void)864 Grid::makeGhostsFall (void)
865 {
866     std::vector<uint8_t> emptyPositions (k_GridWidth);
867     uint8_t remainingGhosts = getNumberOfWaitingGhosts ();
868     int8_t remainingPositions = 0;
869     int8_t y = -1;
870 
871     while ( remainingGhosts > 0 )
872     {
873         if ( 0 == remainingPositions )
874         {
875             for ( uint8_t currentPosition = 0 ; currentPosition < k_GridWidth ;
876                   ++currentPosition )
877             {
878                 emptyPositions.at (currentPosition) = currentPosition;
879             }
880             y -= 1;
881             remainingPositions = k_GridWidth;
882         }
883         uint8_t horizontalPositionIndex = rand () % remainingPositions;
884         int8_t x = emptyPositions.at (horizontalPositionIndex);
885         emptyPositions.at (horizontalPositionIndex) =
886         emptyPositions.at (remainingPositions - 1);
887         --remainingPositions;
888 
889         FallingAmoeba ghostAmoeba;
890         ghostAmoeba.amoeba = new Amoeba (Amoeba::ColourGhost);
891         ghostAmoeba.y = y;
892         ghostAmoeba.x = x;
893         m_ActiveAmoebas.push_back (ghostAmoeba.amoeba);
894         m_FloatingAmoebas.push_back (ghostAmoeba);
895 
896         --remainingGhosts;
897     }
898     setNumberOfWaitingGhosts (0);
899     updateWaitingGhosts ();
900 }
901 
902 ///
903 /// \brief Makes a group of equal colour amoebas recursively.
904 ///
905 /// \param group The group vector to save the members found to.
906 /// \param ghosts The ghosts amoebas that are in contact with the group.
907 /// \param x The X position to look for a posible group candidate.
908 /// \param y The Y position to look for a possible group candidate.
909 /// \param visitedPositions The set of the already visited positions by the
910 ///                         algorithm. Initially should be empty.
911 /// \param colour The colour the amoeba should be to be part of the group.
912 /// \param initialAmoeba Tells if the amoeba \a x and \a y should be the
913 ///        first amoeba of the group.
914 ///
915 void
makeGroup(std::vector<FallingAmoeba> & group,std::vector<FallingAmoeba> & ghosts,int16_t x,int16_t y,std::set<std::pair<int16_t,int16_t>> & visitedPositions,Amoeba::Colour colour,bool initialAmoeba)916 Grid::makeGroup (std::vector<FallingAmoeba> &group,
917                  std::vector<FallingAmoeba> &ghosts, int16_t x, int16_t y,
918                  std::set<std::pair<int16_t, int16_t> > &visitedPositions,
919                  Amoeba::Colour colour, bool initialAmoeba)
920 {
921     // Only check this possition if we didn't visited it yet.
922     if ( visitedPositions.end () ==
923             visitedPositions.find (std::make_pair (x, y)) )
924     {
925         // If there's any amoeba at the given position, check if is a member
926         // of the group.
927         Amoeba *amoeba = getAmoebaAt (x, y);
928         if ( 0 != amoeba && !amoeba->isDying () )
929         {
930             // The initial amoeba is always a member of the group, so
931             // we get its colour and make it the group's colour.
932             if ( initialAmoeba )
933             {
934                 colour = amoeba->getColour ();
935             }
936             // Can't make groups of ghost amoebas. Set it to the
937             // ghosts groups and don't look any further.
938             if ( amoeba->getColour () == Amoeba::ColourGhost )
939             {
940                 FallingAmoeba ghost;
941                 ghost.amoeba = amoeba;
942                 ghost.x = x;
943                 ghost.y = y;
944                 ghosts.push_back (ghost);
945 
946                 // Add the current position to the already visited positions.
947                 visitedPositions.insert (std::make_pair (x, y));
948             }
949             else if ( colour == amoeba->getColour () )
950             {
951                 // Once a member is identified, add it to the group.
952                 FallingAmoeba groupMember;
953                 groupMember.amoeba = amoeba;
954                 groupMember.x = x;
955                 groupMember.y = y;
956                 group.push_back (groupMember);
957 
958                 // Add the current position to the already visited positions.
959                 visitedPositions.insert (std::make_pair (x, y));
960 
961                 // Check neighbours.
962                 makeGroup (group, ghosts, x, y - 1, visitedPositions, colour, false);
963                 makeGroup (group, ghosts, x + 1, y, visitedPositions, colour, false);
964                 makeGroup (group, ghosts, x, y + 1, visitedPositions, colour, false);
965                 makeGroup (group, ghosts, x - 1, y, visitedPositions, colour, false);
966             }
967         }
968     }
969 }
970 
971 ///
972 /// \brief Checks all grid's position and tries to make chains.
973 ///
974 /// A chain is nothing more than 4 or more amoebas joined in a single
975 /// group.
976 ///
977 void
makeChain(void)978 Grid::makeChain (void)
979 {
980     // First delete all groups.
981     for ( int16_t row = 0 ; row < k_GridHeight ; ++row )
982     {
983         for ( int16_t column = 0 ; column < k_GridWidth ; ++column )
984         {
985             std::set<std::pair<int16_t, int16_t> > visitedPositions;
986             std::vector<FallingAmoeba> group;
987             std::vector<FallingAmoeba> ghosts;
988             makeGroup (group, ghosts, column, row, visitedPositions);
989             if ( 4 <= group.size () )
990             {
991                 int16_t meanXPosition = 0;
992                 int16_t meanYPosition = 0;
993 
994                 m_DyingTime = k_DefaultDyingTime;
995                 m_BlinkTime = k_DefaultBlinkTime;
996                 for (std::vector<FallingAmoeba>::iterator currentAmoeba =
997                         group.begin () ;
998                      currentAmoeba != group.end () ; ++currentAmoeba )
999                 {
1000                     meanXPosition += currentAmoeba->x;
1001                     meanYPosition += currentAmoeba->y;
1002                     currentAmoeba->amoeba->setDying (true);
1003                     m_DyingAmoebas.push_back (*currentAmoeba);
1004                 }
1005 
1006                 for (std::vector<FallingAmoeba>::iterator currentGhost =
1007                         ghosts.begin () ;
1008                      currentGhost != ghosts.end () ; ++currentGhost )
1009                 {
1010                     currentGhost->amoeba->setDying (true);
1011                     m_DyingAmoebas.push_back (*currentGhost);
1012                 }
1013 
1014                 meanXPosition /= group.size ();
1015                 meanYPosition /= group.size ();
1016                 addChainLabel (getCurrentStepChain () + 1,
1017                                getGridPositionX () +
1018                                meanXPosition * getAmoebaSize () +
1019                                getAmoebaSize () / 2,
1020                                getGridPositionY () +
1021                                (meanYPosition - k_FirstVisibleHeight) *
1022                                getAmoebaSize () + getAmoebaSize () / 2);
1023             }
1024         }
1025     }
1026 
1027     findFloatingAmoebas ();
1028     // If there's no floating amoebas, set up the falling pair.
1029     if ( m_FloatingAmoebas.empty () && m_DyingAmoebas.empty () )
1030     {
1031         if ( getNumberOfWaitingGhosts () == 0 )
1032         {
1033                 // If the positions above the grid of the 3rd column (from
1034                 // the right) is occuped by an amoeba, then the grid is
1035                 // considered filled.  If any amobea is sitting over the grid in
1036                 // any other column, this is not considered filled.
1037                 if ( 0 != getAmoebaAt (3, k_FirstVisibleHeight - 1) )
1038                 {
1039                     markAsFilled ();
1040                 }
1041                 else
1042                 {
1043                     scheduleFallingPair ();
1044                 }
1045         }
1046         else
1047         {
1048             makeGhostsFall ();
1049         }
1050     }
1051 }
1052 
1053 ///
1054 /// \brief Marks the grid as filled.
1055 ///
1056 /// The grid must be marked as filled when the falling amoebas
1057 /// can't enter the grid.
1058 ///
1059 /// \see isFilled().
1060 ///
1061 void
markAsFilled(void)1062 Grid::markAsFilled (void)
1063 {
1064     m_Filled = true;
1065 }
1066 
1067 ///
1068 /// \brief Moves the falling amoeba pair a position to the left.
1069 ///
1070 void
moveLeft(void)1071 Grid::moveLeft (void)
1072 {
1073     if ( 0 < std::min (m_FallingPair.main.x,
1074                        m_FallingPair.satellite.x) &&
1075          0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) &&
1076          0 == getAmoebaAt (m_FallingPair.satellite.x - 1,
1077                            m_FallingPair.satellite.y) )
1078     {
1079         --m_FallingPair.main.x;
1080         --m_FallingPair.satellite.x;
1081     }
1082 }
1083 
1084 ///
1085 /// \brief Moves the falling amoeba pair a position to the right.
1086 ///
1087 void
moveRight(void)1088 Grid::moveRight (void)
1089 {
1090     if ( k_GridWidth - 1  > std::max (m_FallingPair.main.x,
1091                                       m_FallingPair.satellite.x) &&
1092         0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) &&
1093         0 == getAmoebaAt (m_FallingPair.satellite.x + 1,
1094                           m_FallingPair.satellite.y) )
1095     {
1096         ++m_FallingPair.main.x;
1097         ++m_FallingPair.satellite.x;
1098     }
1099 }
1100 
1101 ///
1102 /// \brief Removes some waiting ghost amoebas.
1103 ///
1104 /// \param newGhostAmoebas The number of waiting ghost amoebas to remove.
1105 /// \return The number of ghost amoebas remaining after removing
1106 ///         \p newGhostAmoebas ghost amoebas.
1107 ///
1108 uint8_t
removeGhostAmoebas(uint8_t newGhostAmoebas)1109 Grid::removeGhostAmoebas (uint8_t newGhostAmoebas)
1110 {
1111     uint8_t remainingGhostAmoebas = 0;
1112     if ( getNumberOfWaitingGhosts () < newGhostAmoebas )
1113     {
1114         remainingGhostAmoebas = newGhostAmoebas - getNumberOfWaitingGhosts ();
1115         setNumberOfWaitingGhosts (0);
1116     }
1117     else
1118     {
1119         setNumberOfWaitingGhosts (getNumberOfWaitingGhosts () - newGhostAmoebas);
1120     }
1121     updateWaitingGhosts ();
1122     return remainingGhostAmoebas;
1123 }
1124 
1125 ///
1126 /// \brief Rotates the falling pair clockwise.
1127 ///
1128 void
rotateClockwise(void)1129 Grid::rotateClockwise (void)
1130 {
1131     if ( !isSatelliteRotating () )
1132     {
1133         m_DegreesUntilRotationDone = 90.0f;
1134         m_RotationDirection = -1.0f;
1135 
1136         if ( m_FallingPair.satellite.y < m_FallingPair.main.y )
1137         {
1138             if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1139                     0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1140             {
1141                 m_FallingPair.satellite.x++;
1142                 m_FallingPair.satellite.y++;
1143             }
1144             else if ( 1 < m_FallingPair.main.x &&
1145                     0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1146             {
1147                 m_FallingPair.satellite.y++;
1148                 m_FallingPair.main.x--;
1149             }
1150             else
1151             {
1152                 // Can't rotate
1153                 m_DegreesUntilRotationDone = 0.0f;
1154             }
1155         }
1156         else if ( m_FallingPair.satellite.y > m_FallingPair.main.y )
1157         {
1158             if ( 0 <  m_FallingPair.main.x &&
1159                     0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1160             {
1161                 m_FallingPair.satellite.x--;
1162                 m_FallingPair.satellite.y--;
1163             }
1164             else if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1165                     0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1166             {
1167                 m_FallingPair.satellite.y--;
1168                 m_FallingPair.main.x++;
1169             }
1170             else
1171             {
1172                 // Can't rotate
1173                 m_DegreesUntilRotationDone = 0.0f;
1174             }
1175         }
1176         else if ( m_FallingPair.satellite.x > m_FallingPair.main.x )
1177         {
1178             if ( 0 == getAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y + 1) )
1179             {
1180                 m_FallingPair.satellite.x--;
1181                 m_FallingPair.satellite.y++;
1182             }
1183             else
1184             {
1185                 // Can't rotate
1186                 m_DegreesUntilRotationDone = 0.0f;
1187             }
1188         }
1189         else
1190         {
1191             m_FallingPair.satellite.x++;
1192             m_FallingPair.satellite.y--;
1193         }
1194     }
1195 }
1196 
1197 ///
1198 /// \brief Rotates the falling pair counterclockwise.
1199 ///
1200 void
rotateCounterClockwise(void)1201 Grid::rotateCounterClockwise (void)
1202 {
1203     if ( !isSatelliteRotating () )
1204     {
1205         m_DegreesUntilRotationDone = 90.0f;
1206         m_RotationDirection = 1.0f;
1207         if ( m_FallingPair.satellite.y < m_FallingPair.main.y )
1208         {
1209             if ( 0 < m_FallingPair.main.x &&
1210                     0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1211             {
1212                 m_FallingPair.satellite.x--;
1213                 m_FallingPair.satellite.y++;
1214             }
1215             else if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1216                     0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1217             {
1218                 m_FallingPair.satellite.y++;
1219                 m_FallingPair.main.x++;
1220             }
1221             else
1222             {
1223                 // Can't rotate
1224                 m_DegreesUntilRotationDone = 0.0f;
1225             }
1226         }
1227         else if ( m_FallingPair.satellite.y > m_FallingPair.main.y )
1228         {
1229             if ( k_GridWidth - 1 >  m_FallingPair.main.x &&
1230                     0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1231             {
1232                 m_FallingPair.satellite.x++;
1233                 m_FallingPair.satellite.y--;
1234             }
1235             else if ( 1 < m_FallingPair.main.x &&
1236                     0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1237             {
1238                 m_FallingPair.satellite.y--;
1239                 m_FallingPair.main.x--;
1240             }
1241             else
1242             {
1243                 // Can't rotate
1244                 m_DegreesUntilRotationDone = 0.0f;
1245             }
1246         }
1247         else if ( m_FallingPair.satellite.x < m_FallingPair.main.x )
1248         {
1249             if ( 0 == getAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y + 1) )
1250             {
1251                 m_FallingPair.satellite.x++;
1252                 m_FallingPair.satellite.y++;
1253             }
1254             else
1255             {
1256                 // Can't rotate
1257                 m_DegreesUntilRotationDone = 0.0f;
1258             }
1259         }
1260         else
1261         {
1262             m_FallingPair.satellite.x--;
1263             m_FallingPair.satellite.y--;
1264         }
1265     }
1266 }
1267 
1268 ///
1269 /// \brief Rotates the satellite amoeba.
1270 ///
1271 /// \param degreesToRotate The degrees to make rotate the satellite amoeba.
1272 ///
1273 void
rotateSatelliteAmoeba(float degreesToRotate)1274 Grid::rotateSatelliteAmoeba (float degreesToRotate)
1275 {
1276     if ( degreesToRotate > m_DegreesUntilRotationDone )
1277     {
1278         degreesToRotate = m_DegreesUntilRotationDone;
1279     }
1280 
1281     m_DegreesUntilRotationDone -= degreesToRotate;
1282     degreesToRotate *= m_RotationDirection;
1283     m_CurrentRotationDegrees += degreesToRotate;
1284     if ( m_CurrentRotationDegrees > 360.0f )
1285     {
1286         m_CurrentRotationDegrees -= 360.0f;
1287     }
1288     else if ( m_CurrentRotationDegrees < 0 )
1289     {
1290         m_CurrentRotationDegrees += 360.0f;
1291     }
1292     setFallingAmoebaScreenPosition (m_FallingPair.satellite,
1293                                     m_FallingPair.verticalOffset,
1294                                     isSatelliteRotating ());
1295 }
1296 
1297 ///
1298 /// \brief Sets the amoeba at a given grid's position.
1299 ///
1300 /// \param x The X position to set the amoeba to.
1301 /// \param y The Y position to set the amoeba to.
1302 /// \param amoeba The amoeba to set at grid's position \p x and \p y.
1303 ///
1304 void
setAmoebaAt(int16_t x,int16_t y,Amoeba * amoeba)1305 Grid::setAmoebaAt (int16_t x, int16_t y, Amoeba *amoeba)
1306 {
1307     if ( 0 <= x && x < k_GridWidth &&
1308          0 <= y && y < k_GridHeight )
1309     {
1310         m_Grid[y * k_GridWidth + x] = amoeba;
1311     }
1312 }
1313 
1314 ///
1315 /// \brief Sets the status of an amoeba at a given position.
1316 ///
1317 /// This checks the neighbors of the amoeba at grid's position \a x and \a y
1318 /// to check how many amoebas of the same colour there are.
1319 ///
1320 /// \param x The grid's X position of the amoeba to set the state to.
1321 /// \param y The grid's Y position of the amoeba to set the state to.
1322 /// \param neighbors If set to \a true, this function sets the state of
1323 ///                  the neighbors amoebas.
1324 ///
1325 void
setAmoebaStateAt(int16_t x,int16_t y,bool neighbors)1326 Grid::setAmoebaStateAt (int16_t x, int16_t y, bool neighbors)
1327 {
1328     Amoeba *currentAmoeba = getAmoebaAt (x, y);
1329     if ( 0 != currentAmoeba &&
1330          Amoeba::ColourGhost != currentAmoeba->getColour () )
1331     {
1332         int state = Amoeba::StateNone;
1333         if ( isOfSameColour (x, y - 1, currentAmoeba) )
1334         {
1335             state |= Amoeba::StateTop;
1336         }
1337         if ( isOfSameColour (x + 1, y, currentAmoeba) )
1338         {
1339             state |= Amoeba::StateRight;
1340         }
1341         if ( isOfSameColour (x, y + 1, currentAmoeba) )
1342         {
1343             state |= Amoeba::StateBottom;
1344         }
1345         if ( isOfSameColour (x - 1, y, currentAmoeba) )
1346         {
1347             state |= Amoeba::StateLeft;
1348         }
1349         currentAmoeba->setState (static_cast<Amoeba::State>(state));
1350     }
1351 
1352     // If we must check neighbors, set the state of the direct
1353     // neighbors (i.e., no diagonals).
1354     if ( neighbors )
1355     {
1356         setAmoebaStateAt (x, y - 1, false);
1357         setAmoebaStateAt (x + 1, y, false);
1358         setAmoebaStateAt (x, y + 1, false);
1359         setAmoebaStateAt (x - 1, y, false);
1360     }
1361 }
1362 
1363 ///
1364 /// \brief Gets the next pair from the inactive list and sets it to the queue.
1365 ///
1366 /// The next pair will be the last element in the queue, so it should
1367 /// be called when the first element of the queue becomes the falling pair.
1368 ///
1369 /// If the inactive amoebas list becomes empty, this function request
1370 /// to the pair generator a new pair.
1371 ///
1372 /// This function is used when moving the queue step by step instead of
1373 /// just setting the queued amoeba behind the scene.
1374 ///
1375 void
scheduleFallingPair(void)1376 Grid::scheduleFallingPair (void)
1377 {
1378     assert ( 0 != m_Generator &&
1379             "Tried to get the next pair without a generator.");
1380     assert ( 2 <= m_InactiveAmoebas.size () &&
1381             "Tried to get the next pair, but the inactive list is empty.");
1382 
1383     // Gets the next pair from the inactive list.
1384     Amoeba *main = m_InactiveAmoebas.front ();
1385     m_InactiveAmoebas.pop_front ();
1386     Amoeba *satellite = m_InactiveAmoebas.front ();
1387     m_InactiveAmoebas.pop_front ();
1388     // And set them to queued.
1389     m_QueuedAmoebas.push_back (main);
1390     m_QueuedAmoebas.push_back (satellite);
1391     // Sets they position to be the last pair on the queue.
1392     // Take into account the side where the queue is located at
1393     // to choose the offset to apply to this pair.
1394     uint16_t lastPairOffsetX = getAmoebaSize () / 2;
1395     if ( LayoutVertical == getLayout () )
1396     {
1397         if ( Grid::QueueSideLeft == getQueueSide () )
1398         {
1399             lastPairOffsetX = -lastPairOffsetX;
1400         }
1401         main->setX (getQueuePositionX () + lastPairOffsetX);
1402         main->setY (getQueuePositionY () + getAmoebaSize () * 5);
1403         satellite->setX (getQueuePositionX () + lastPairOffsetX);
1404         satellite->setY (getQueuePositionY () + getAmoebaSize () * 4);
1405     }
1406     else
1407     {
1408         if (Grid::QueueSideLeft == getQueueSide () )
1409         {
1410             main->setX (getQueuePositionX () - getAmoebaSize () * 6);
1411             main->setY (getQueuePositionY () + lastPairOffsetX);
1412             satellite->setX (getQueuePositionX () - getAmoebaSize () * 5);
1413             satellite->setY (getQueuePositionY () + lastPairOffsetX);
1414         }
1415         else
1416         {
1417             main->setX (getQueuePositionX () + getAmoebaSize () * 5);
1418             main->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
1419             satellite->setX (getQueuePositionX () + getAmoebaSize () * 4);
1420             satellite->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
1421         }
1422     }
1423     // And set up the falling pair structure for when they fall.
1424     FallingPair fallingPair;
1425     fallingPair.main.amoeba = main;
1426     fallingPair.main.x = 3;
1427     fallingPair.main.y = k_FirstVisibleHeight - 2;
1428     fallingPair.satellite.amoeba = satellite;
1429     fallingPair.satellite.x = 3;
1430     fallingPair.satellite.y = k_FirstVisibleHeight - 3;
1431     fallingPair.verticalOffset = 0;
1432     fallingPair.fallingTime = k_DefaultFallingTime;
1433     m_Queue.push_back (fallingPair);
1434     // Request a new pair if we are out of inactive pairs.
1435     if ( m_InactiveAmoebas.empty () )
1436     {
1437         m_Generator->generate ();
1438     }
1439 
1440     // Tells that the queue is moving.
1441     m_RemainingQueueHorizontalOffset = getAmoebaSize () / 2;
1442     m_RemainingQueueVerticalOffset = 2 * getAmoebaSize ();
1443     m_QueueIsMoving = true;
1444 }
1445 
1446 ///
1447 /// \brief Sets the global screen position to a falling amoeba.
1448 ///
1449 /// \param fallingAmoeba The falling amoeba to set its screen position.
1450 /// \param verticalOffset The offset to add to the amoeba's Y coordinate.
1451 /// \param rotating Tells if the amoeba is rotating and has to take into
1452 ///                 account the current rotation degrees.  Used for the
1453 ///                 satellite.
1454 ///
1455 void
setFallingAmoebaScreenPosition(FallingAmoeba & fallingAmoeba,int16_t verticalOffset,bool rotating)1456 Grid::setFallingAmoebaScreenPosition (FallingAmoeba &fallingAmoeba,
1457                                       int16_t verticalOffset, bool rotating)
1458 {
1459     int16_t x = getGridPositionX ();
1460     int16_t y = getGridPositionY ();
1461     if (Grid::LayoutVertical == getLayout ())
1462     {
1463         y += verticalOffset  - getAmoebaSize () * k_FirstVisibleHeight;
1464         if ( rotating )
1465         {
1466             float rotationX = k_Cos[getCurrentRotationDegree ()];
1467             float rotationY = k_Sin[getCurrentRotationDegree ()];
1468             int16_t directionX = 0;
1469             int16_t directionY = 0;
1470             if ( 0 <= getCurrentRotationDegree () &&
1471                       getCurrentRotationDegree () < 90 )
1472             {
1473                 if ( m_RotationDirection < 0.0f )
1474                 {
1475                     directionX = -1;
1476                     directionY = 0;
1477                 }
1478                 else
1479                 {
1480                     directionX = 0;
1481                     directionY = 1;
1482                 }
1483             }
1484             else if ( 90 <= getCurrentRotationDegree () &&
1485                             getCurrentRotationDegree () < 180 )
1486             {
1487                 if ( m_RotationDirection < 0.0f )
1488                 {
1489                     directionX = 0;
1490                     directionY = 1;
1491                 }
1492                 else
1493                 {
1494                     directionX = 1;
1495                     directionY = 0;
1496                 }
1497             }
1498             else if ( 180 <= getCurrentRotationDegree () &&
1499                              getCurrentRotationDegree () < 270 )
1500             {
1501                 if ( m_RotationDirection < 0.0f )
1502                 {
1503                     directionX = 1;
1504                     directionY = 0;
1505                 }
1506                 else
1507                 {
1508                     directionX = 0;
1509                     directionY = -1;
1510                 }
1511             }
1512             else if ( getCurrentRotationDegree () >= 270 )
1513             {
1514                 if ( m_RotationDirection < 0.0f )
1515                 {
1516                     directionX = 0;
1517                     directionY = -1;
1518                 }
1519                 else
1520                 {
1521                     directionX = -1;
1522                     directionY = 0;
1523                 }
1524             }
1525 
1526             x += static_cast<uint16_t> ((fallingAmoeba.x + directionX) *
1527                     getAmoebaSize () +
1528                     getAmoebaSize () * rotationX);
1529             y += static_cast<uint16_t> ((fallingAmoeba.y + directionY) *
1530                     getAmoebaSize () -
1531                     getAmoebaSize () * rotationY);
1532         }
1533         else
1534         {
1535             x += fallingAmoeba.x * getAmoebaSize ();
1536             y += fallingAmoeba.y * getAmoebaSize ();
1537         }
1538     }
1539     else
1540     {
1541         float rotationY = k_Cos[getCurrentRotationDegree ()];
1542         float rotationX = k_Sin[getCurrentRotationDegree ()];
1543         int16_t directionY = 0;
1544         int16_t directionX = 0;
1545         if ( rotating )
1546         {
1547             if ( 0 <= getCurrentRotationDegree () &&
1548                     getCurrentRotationDegree () < 90 )
1549             {
1550                 if ( m_RotationDirection < 0.0f )
1551                 {
1552                     directionY = -1;
1553                     directionX = 0;
1554                 }
1555                 else
1556                 {
1557                     directionY = 0;
1558                     directionX = 1;
1559                 }
1560             }
1561             else if ( 90 <= getCurrentRotationDegree () &&
1562                     getCurrentRotationDegree () < 180 )
1563             {
1564                 if ( m_RotationDirection < 0.0f )
1565                 {
1566                     directionY = 0;
1567                     directionX = 1;
1568                 }
1569                 else
1570                 {
1571                     directionY = 1;
1572                     directionX = 0;
1573                 }
1574             }
1575             else if ( 180 <= getCurrentRotationDegree () &&
1576                     getCurrentRotationDegree () < 270 )
1577             {
1578                 if ( m_RotationDirection < 0.0f )
1579                 {
1580                     directionY = 1;
1581                     directionX = 0;
1582                 }
1583                 else
1584                 {
1585                     directionY = 0;
1586                     directionX = -1;
1587                 }
1588             }
1589             else if ( getCurrentRotationDegree () >= 270 )
1590             {
1591                 if ( m_RotationDirection < 0.0f )
1592                 {
1593                     directionY = 0;
1594                     directionX = -1;
1595                 }
1596                 else
1597                 {
1598                     directionY = -1;
1599                     directionX = 0;
1600                 }
1601             }
1602         }
1603 
1604 
1605         if (Grid::QueueSideLeft == getQueueSide ())
1606         {
1607             if (rotating)
1608             {
1609                 x -= (fallingAmoeba.y + 1 + directionX) * getAmoebaSize () -
1610                     static_cast<uint16_t> (getAmoebaSize () * rotationX);
1611                 y += (fallingAmoeba.x + directionY) * getAmoebaSize () +
1612                     static_cast<uint16_t> (getAmoebaSize () * rotationY);
1613             }
1614             else
1615             {
1616 
1617                 x -= (fallingAmoeba.y + 1) * getAmoebaSize ();
1618                 y += fallingAmoeba.x * getAmoebaSize ();
1619             }
1620             x -= verticalOffset - getAmoebaSize () * k_FirstVisibleHeight;
1621         }
1622         else
1623         {
1624             if ( rotating )
1625             {
1626                 x += (fallingAmoeba.y + directionX) * getAmoebaSize () -
1627                     static_cast<uint16_t> (getAmoebaSize () * rotationX);
1628                 y -= (fallingAmoeba.x + 1 + directionY) * getAmoebaSize () +
1629                     static_cast<uint16_t> (getAmoebaSize () * rotationY);
1630             }
1631             else
1632             {
1633                 x += fallingAmoeba.y * getAmoebaSize ();
1634                 y -= (fallingAmoeba.x + 1) * getAmoebaSize ();
1635             }
1636             x += verticalOffset - getAmoebaSize () * k_FirstVisibleHeight;
1637         }
1638     }
1639 
1640     fallingAmoeba.amoeba->setX (x);
1641     fallingAmoeba.amoeba->setY (y);
1642 }
1643 
1644 ///
1645 /// \brief Sets the generator.
1646 ///
1647 /// When the list of waiting amoebas reaches zero, this generator
1648 /// will be the used to generate a new pair.
1649 ///
1650 /// \param generator The generator that the class is linked to and
1651 ///                  will use to request more pairs.
1652 ///
1653 void
setGenerator(PairGenerator * generator)1654 Grid::setGenerator (PairGenerator *generator)
1655 {
1656     m_Generator = generator;
1657 }
1658 
1659 ///
1660 /// \brief Sets the falling speed to maximum.
1661 ///
1662 void
setMaxFallingSpeed(void)1663 Grid::setMaxFallingSpeed (void)
1664 {
1665     m_MaxFallingSpeed = true;
1666 }
1667 
1668 ///
1669 /// \brief Sets the status of the new falling pair.
1670 ///
1671 /// \param hasNewFallingPair Set it to \a true when a new falling pair
1672 ///                          starts to fall. Otherwise set it to \a false.
1673 /// \see hasNewFallingPair().
1674 ///
1675 inline void
setHasNewFallingPair(bool hasNewFallingPair)1676 Grid::setHasNewFallingPair (bool hasNewFallingPair)
1677 {
1678     m_HasNewFallingPair = hasNewFallingPair;
1679 }
1680 
1681 ///
1682 /// \brief Sets the next falling pair.
1683 ///
1684 /// Gets the next falling pair from the list of waiting pairs and
1685 /// moves the last element to be the next.
1686 ///
1687 void
setupFallingPair(void)1688 Grid::setupFallingPair (void)
1689 {
1690     // Get the two first queued amoebas and set it to be active.
1691     m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
1692     m_QueuedAmoebas.pop_front ();
1693     m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
1694     m_QueuedAmoebas.pop_front ();
1695 
1696     m_FallingPair = m_Queue.front ();
1697     m_Queue.pop_front ();
1698     // Set the screen position without any vertical offset. The grid
1699     // position is set at getNextPair().
1700     setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
1701     setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
1702 
1703     // Reset the current rotation.
1704     m_CurrentRotationDegrees = 90.0f;
1705     m_DegreesUntilRotationDone = 0.0f;
1706     m_RotationDirection = 1.0f;
1707 
1708     // Now set the remainig pair to be the next to fall and
1709     // add a new pair.
1710     FallingPair remainingPair (m_Queue.front ());
1711     if ( Grid::LayoutVertical == getLayout () )
1712     {
1713         remainingPair.main.amoeba->setX (getQueuePositionX ());
1714         remainingPair.main.amoeba->setY (getQueuePositionY () + getAmoebaSize ());
1715         remainingPair.satellite.amoeba->setX (getQueuePositionX ());
1716         remainingPair.satellite.amoeba->setY (getQueuePositionY ());
1717     }
1718     else
1719     {
1720         if (Grid::QueueSideLeft == getQueueSide () )
1721         {
1722             remainingPair.main.amoeba->setX (getQueuePositionX () - getAmoebaSize () * 2);
1723             remainingPair.main.amoeba->setY (getQueuePositionY ());
1724             remainingPair.satellite.amoeba->setX (getQueuePositionX () - getAmoebaSize ());
1725             remainingPair.satellite.amoeba->setY (getQueuePositionY ());
1726         }
1727         else
1728         {
1729             remainingPair.main.amoeba->setX (getQueuePositionX () + getAmoebaSize ());
1730             remainingPair.main.amoeba->setY (getQueuePositionY () - getAmoebaSize ());
1731             remainingPair.satellite.amoeba->setX (getQueuePositionX ());
1732             remainingPair.satellite.amoeba->setY (getQueuePositionY () - getAmoebaSize ());
1733         }
1734     }
1735     getNextPair ();
1736     setCurrentStepChain (0);
1737     setHasNewFallingPair (true);
1738 }
1739 
1740 ///
1741 /// \brief Sets the falling speed to normal.
1742 ///
1743 void
setNormalFallingSpeed(void)1744 Grid::setNormalFallingSpeed (void)
1745 {
1746     m_MaxFallingSpeed = false;
1747 }
1748 
1749 
1750 ///
1751 /// \brief Sets the current silhoutte frame.
1752 ///
1753 /// \param frame The silhouette frame to set as current.
1754 ///
1755 inline void
setSilhouetteFrame(int8_t frame)1756 Grid::setSilhouetteFrame (int8_t frame)
1757 {
1758     m_SilhouetteFrame = frame;
1759 }
1760 
1761 ///
1762 /// \brief Updates the grid.
1763 ///
1764 /// Moves the amoebax according to its state and the time elapsed from the last
1765 /// time this function was called, as specified in \a elapsedTime.
1766 ///
1767 /// \param elapsedTime The elapsed time between the previous and current call,
1768 ///                    in milliseconds.
1769 ///
1770 void
update(uint32_t elapsedTime)1771 Grid::update (uint32_t elapsedTime)
1772 {
1773     // Only update if we are no waiting the initial amoebas
1774     // and the grid is not filled.
1775     if ( !m_WaitingInitialAmoebas && !isFilled () )
1776     {
1777         // By default, we don't have a new falling pair unless
1778         // we must set up a new pair to fall or is the first one.
1779         setHasNewFallingPair (false || m_FirstFallingPair);
1780         // The first pair is done!
1781         m_FirstFallingPair = false;
1782         // No ghost amoebas for our opponent.
1783         setOpponentGhostAmoebas (0);
1784 
1785         // Remove old label chain labels.
1786         for ( std::list<ChainLabel *>::iterator currentLabel =
1787                 m_ChainLabels.begin () ;
1788               currentLabel != m_ChainLabels.end () ; )
1789         {
1790             if ( !(*currentLabel)->isAlive () )
1791             {
1792                 currentLabel = m_ChainLabels.erase (currentLabel);
1793             }
1794             else
1795             {
1796                 ++currentLabel;
1797             }
1798         }
1799         // And update the remaining.
1800         for ( std::list<ChainLabel *>::iterator currentLabel =
1801                 m_ChainLabels.begin () ;
1802               currentLabel != m_ChainLabels.end () ;
1803               ++currentLabel )
1804         {
1805             (*currentLabel)->update (elapsedTime);
1806         }
1807 
1808         if ( m_FloatingAmoebas.empty () && m_DyingAmoebas.empty () &&
1809              !isQueueMoving () )
1810         {
1811             m_SilhouetteTime -= elapsedTime;
1812             if ( 0 > m_SilhouetteTime )
1813             {
1814                 m_SilhouetteTime = k_SilhouetteTime;
1815                 setSilhouetteFrame (getSilhouetteFrame () +
1816                         m_SilhouetteFrameDirection);
1817                 if ( 0 > getSilhouetteFrame () ||
1818                         k_MaxSilhouetteFrames == getSilhouetteFrame () )
1819                 {
1820                     m_SilhouetteFrameDirection = -m_SilhouetteFrameDirection;
1821                     setSilhouetteFrame (getSilhouetteFrame () +
1822                             m_SilhouetteFrameDirection);
1823                 }
1824             }
1825 
1826             if ( isSatelliteRotating () )
1827             {
1828                 float degreesToRotate = k_RotationSpeed * elapsedTime;
1829                 rotateSatelliteAmoeba (degreesToRotate);
1830             }
1831 
1832             m_FallingPair.fallingTime -= elapsedTime;
1833             if ( 0 >= m_FallingPair.fallingTime ||
1834                  m_MaxFallingSpeed )
1835             {
1836                 m_FallingPair.fallingTime = k_DefaultFallingTime;
1837                 m_FallingPair.verticalOffset += getAmoebaSize () / 2;
1838                 if ( m_MaxFallingSpeed )
1839                 {
1840                     m_FallingPair.verticalOffset += getAmoebaSize () / 2;
1841                     incrementScore ();
1842                 }
1843             }
1844 
1845 
1846             if ( m_FallingPair.verticalOffset >= getAmoebaSize () )
1847             {
1848                 m_FallingPair.verticalOffset -= getAmoebaSize ();
1849                 ++m_FallingPair.main.y;
1850                 ++m_FallingPair.satellite.y;
1851             }
1852 
1853             if ( k_GridHeight - 1 == m_FallingPair.main.y ||
1854                  k_GridHeight - 1 == m_FallingPair.satellite.y ||
1855                  0 != getAmoebaAt (m_FallingPair.main.x,
1856                                    m_FallingPair.main.y + 1) ||
1857                  0 != getAmoebaAt (m_FallingPair.satellite.x,
1858                                    m_FallingPair.satellite.y + 1) )
1859             {
1860                 // Set to no silhouette frame.
1861                 setSilhouetteFrame (0);
1862                 m_SilhouetteFrameDirection = 1;
1863                 // Set the screen position without any vertical offset.
1864                 setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
1865                 setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
1866                 // Set the amoeba's position to be occuped.
1867                 setAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y,
1868                         m_FallingPair.main.amoeba);
1869                 setAmoebaAt (m_FallingPair.satellite.x,
1870                         m_FallingPair.satellite.y,
1871                         m_FallingPair.satellite.amoeba);
1872                 setAmoebaStateAt (m_FallingPair.main.x,
1873                         m_FallingPair.main.y);
1874                 setAmoebaStateAt (m_FallingPair.satellite.x,
1875                         m_FallingPair.satellite.y);
1876                 // If either the satelite or the main amoeba is floating
1877                 // make it to behaviour as it should.
1878                 findFloatingAmoebas ();
1879                 // Check if we have any chain that we can make if no
1880                 // floating amoeba is around.
1881                 if ( m_FloatingAmoebas.empty () )
1882                 {
1883                     makeChain ();
1884                 }
1885                 setNormalFallingSpeed ();
1886             }
1887             else
1888             {
1889                 setFallingAmoebaScreenPosition (m_FallingPair.main,
1890                                                 m_FallingPair.verticalOffset,
1891                                                 false);
1892                 setFallingAmoebaScreenPosition (m_FallingPair.satellite,
1893                                                 m_FallingPair.verticalOffset,
1894                                                 isSatelliteRotating ());
1895             }
1896         }
1897 
1898         else if ( !m_FloatingAmoebas.empty () )
1899         {
1900             m_FloatingAmoebasVerticalOffset += getAmoebaSize () / 2;
1901             std::list<FallingAmoeba>::iterator floatingAmoeba =
1902                 m_FloatingAmoebas.begin ();
1903             while ( floatingAmoeba != m_FloatingAmoebas.end () )
1904             {
1905                 if ( m_FloatingAmoebasVerticalOffset >= getAmoebaSize () )
1906                 {
1907                     ++floatingAmoeba->y;
1908                     if ( k_GridHeight - 1 == floatingAmoeba->y ||
1909                          0 != getAmoebaAt (floatingAmoeba->x,
1910                                            floatingAmoeba->y + 1) )
1911                     {
1912                         if ( floatingAmoeba->y >= 0 )
1913                         {
1914                             setFallingAmoebaScreenPosition (*floatingAmoeba, 0,
1915                                                             false);
1916                             setAmoebaAt (floatingAmoeba->x, floatingAmoeba->y,
1917                                          floatingAmoeba->amoeba);
1918                             setAmoebaStateAt (floatingAmoeba->x,
1919                                               floatingAmoeba->y);
1920                         }
1921                         else
1922                         {
1923                             m_ActiveAmoebas.remove (floatingAmoeba->amoeba);
1924                             delete floatingAmoeba->amoeba;
1925                         }
1926                         floatingAmoeba =
1927                             m_FloatingAmoebas.erase (floatingAmoeba);
1928                     }
1929                     else
1930                     {
1931                         ++floatingAmoeba;
1932                     }
1933                 }
1934                 else
1935                 {
1936                     setFallingAmoebaScreenPosition (*floatingAmoeba,
1937                             m_FloatingAmoebasVerticalOffset, false);
1938                     ++floatingAmoeba;
1939                 }
1940             }
1941 
1942             if ( m_FloatingAmoebasVerticalOffset >= getAmoebaSize () )
1943             {
1944                 m_FloatingAmoebasVerticalOffset -= getAmoebaSize ();
1945             }
1946 
1947             if ( m_FloatingAmoebas.empty () )
1948             {
1949                 makeChain ();
1950             }
1951         }
1952 
1953         else if ( !m_DyingAmoebas.empty () )
1954         {
1955             m_DyingTime -= elapsedTime;
1956             if ( 0 >= m_DyingTime )
1957             {
1958                 clearDyingAmoebas ();
1959             }
1960             else
1961             {
1962                 m_BlinkTime -= elapsedTime;
1963                 if ( 0 >= m_BlinkTime )
1964                 {
1965                     m_BlinkTime = k_DefaultBlinkTime;
1966                     for (std::vector<FallingAmoeba>::iterator currentAmoeba =
1967                             m_DyingAmoebas.begin () ;
1968                         currentAmoeba != m_DyingAmoebas.end () ; ++currentAmoeba )
1969                     {
1970                         currentAmoeba->amoeba->setVisible (
1971                                 !currentAmoeba->amoeba->isVisible ());
1972                     }
1973                 }
1974             }
1975         }
1976 
1977         else if ( isQueueMoving () )
1978         {
1979             updateQueue (elapsedTime);
1980         }
1981     }
1982 }
1983 
1984 ///
1985 /// \brief Updates the moving queue.
1986 ///
1987 /// \param elapsedTime The time elapsed from the last call.
1988 ///
1989 void
updateQueue(uint32_t elapsedTime)1990 Grid::updateQueue (uint32_t elapsedTime)
1991 {
1992     uint16_t queueHorizontalOffset =
1993         static_cast<uint16_t> (k_QueueVerticalSpeed * elapsedTime);
1994     if ( queueHorizontalOffset > m_RemainingQueueHorizontalOffset )
1995     {
1996         queueHorizontalOffset = m_RemainingQueueHorizontalOffset;
1997     }
1998     m_RemainingQueueHorizontalOffset -= queueHorizontalOffset;
1999 
2000     uint16_t queueVerticalOffset =
2001         static_cast<uint16_t> (k_QueueVerticalSpeed * elapsedTime);
2002     if ( queueVerticalOffset > m_RemainingQueueVerticalOffset )
2003     {
2004         queueVerticalOffset = m_RemainingQueueVerticalOffset;
2005     }
2006     m_RemainingQueueVerticalOffset -= queueVerticalOffset;
2007 
2008     if ( Grid::LayoutHorizontal == getLayout () )
2009     {
2010         if ( Grid::QueueSideLeft == getQueueSide () )
2011         {
2012             queueVerticalOffset = -queueVerticalOffset;
2013         }
2014         else
2015         {
2016             queueHorizontalOffset = -queueHorizontalOffset;
2017         }
2018     }
2019     else
2020     {
2021         if ( Grid::QueueSideLeft == getQueueSide () )
2022         {
2023             queueHorizontalOffset = -queueHorizontalOffset;
2024         }
2025     }
2026 
2027     std::list<Amoeba *>::iterator currentAmoeba = m_QueuedAmoebas.begin () ;
2028     for ( int amoebaNum = 0 ; currentAmoeba != m_QueuedAmoebas.end () ;
2029           ++currentAmoeba, ++amoebaNum )
2030     {
2031         if ( Grid::LayoutHorizontal == getLayout () )
2032         {
2033             (*currentAmoeba)->setX ((*currentAmoeba)->getX () -
2034                                     queueVerticalOffset);
2035             if ( 2 == amoebaNum || 3 == amoebaNum )
2036             {
2037                 (*currentAmoeba)->setY ((*currentAmoeba)->getY () -
2038                                         queueHorizontalOffset);
2039             }
2040         }
2041         else
2042         {
2043             (*currentAmoeba)->setY ((*currentAmoeba)->getY () -
2044                                     queueVerticalOffset);
2045 
2046             if ( 2 == amoebaNum || 3 == amoebaNum )
2047             {
2048                 (*currentAmoeba)->setX ((*currentAmoeba)->getX () -
2049                                         queueHorizontalOffset);
2050             }
2051         }
2052     }
2053 
2054 
2055     if ( m_RemainingQueueHorizontalOffset == 0 &&
2056          m_RemainingQueueVerticalOffset == 0 )
2057     {
2058         m_QueueIsMoving = false;
2059 
2060         // Get the two first queued amoebas and set it to be active.
2061         m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
2062         m_QueuedAmoebas.pop_front ();
2063         m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
2064         m_QueuedAmoebas.pop_front ();
2065 
2066         m_FallingPair = m_Queue.front ();
2067         m_Queue.pop_front ();
2068         // Set the screen position without any vertical offset. The grid
2069         // position is set at getNextPair().
2070         setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
2071         setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
2072 
2073         // Reset the current rotation.
2074         m_CurrentRotationDegrees = 90.0f;
2075         m_DegreesUntilRotationDone = 0.0f;
2076         m_RotationDirection = 1.0f;
2077 
2078         setCurrentStepChain (0);
2079         setHasNewFallingPair (true);
2080     }
2081 }
2082 
2083 ///
2084 /// \brief Updates the state of the waiting ghosts amoebas.
2085 ///
2086 /// Updates the state of the amoebas that will be drawn as the
2087 /// waiting status. The "neighbour" state of those amoebas will
2088 /// decide the "weight" that each ghost amoeba has.
2089 ///
2090 void
updateWaitingGhosts(void)2091 Grid::updateWaitingGhosts (void)
2092 {
2093     int8_t remainingGhost = getNumberOfWaitingGhosts ();
2094     for ( uint8_t currentAmoeba = 0 ; currentAmoeba < k_GridWidth ; ++currentAmoeba )
2095     {
2096         uint8_t weight = 0;
2097         if ( remainingGhost > 0 )
2098         {
2099             if ( remainingGhost <= (k_GridWidth - currentAmoeba) )
2100             {
2101                 weight = 1;
2102             }
2103             else
2104             {
2105                 weight = static_cast<uint8_t>(std::ceil (remainingGhost /
2106                                                         (static_cast<double>(k_GridWidth) - currentAmoeba)));
2107             }
2108             remainingGhost -= weight;
2109         }
2110         m_WaitingGhostAmoebas[currentAmoeba]->setState (
2111                 static_cast<Amoeba::State> (Amoeba::StateTopRightBottomLeft - weight));
2112     }
2113 }
2114