tetris: remove completed rows
Stefan Schuermans

Stefan Schuermans commited on 2019-07-14 18:59:44
Showing 2 changed files, with 89 additions and 8 deletions.

... ...
@@ -151,8 +151,8 @@ void Tetris::opConnRecvKey(OpConn *pConn, char key)
151 151
   }
152 152
 
153 153
   /** normal keys for controlling game,
154
-      deactivated if dropping stone */
155
-  if (m_dropping) {
154
+      deactivated if dropping stone or rows blinking */
155
+  if (m_dropping || m_blinking > 0) {
156 156
     return;
157 157
   }
158 158
 
... ...
@@ -252,11 +252,17 @@ void Tetris::reinitialize()
252 252
   color2data(m_fileStoneColor, m_stoneColor);
253 253
   // get values
254 254
   valueFromFile(m_fileDelay, c_delayDescr, m_delay);
255
+  valueFromFile(m_fileDropDelay, c_dropDelayDescr, m_dropDelay);
256
+  valueFromFile(m_fileBlinkDelay, c_blinkDelayDescr, m_blinkDelay);
255 257
 
256 258
   // initialize field: empty
257 259
   m_field.clear();
258 260
   m_field.resize(m_height * m_width, -1);
259 261
 
262
+  // initialize blinking rows: no row blinking
263
+  m_rowsBlink.clear();
264
+  m_rowsBlink.resize(m_height, false);
265
+
260 266
   // start with new stone
261 267
   newStone();
262 268
 
... ...
@@ -273,14 +279,16 @@ void Tetris::redraw()
273 279
   // draw background
274 280
   rectFill(0, m_height, 0, m_width, m_backgroundColor);
275 281
 
276
-  // draw fixed pixels
282
+  // draw fixed pixels, respect blinking rows
277 283
   for (int y = 0, i = 0; y < m_height; ++y) {
284
+    if (! (m_blinking & 1) || ! m_rowsBlink.at(y)) {
278 285
       for (int x = 0; x < m_width; ++x, ++i) {
279 286
         if (m_field.at(i) >= 0) {
280 287
           pixel(y, x, m_stoneColor);
281 288
         }
282 289
       }
283 290
     }
291
+  }
284 292
 
285 293
   // draw current stone
286 294
   drawStone(m_stone, m_rot, m_posY, m_posX);
... ...
@@ -292,6 +300,43 @@ void Tetris::redraw()
292 300
 /// process next time step of game
293 301
 void Tetris::timeStep()
294 302
 {
303
+  // blinking of completed rows
304
+  if (m_blinking > 0) {
305
+
306
+    // blink rows
307
+    ++m_blinking;
308
+
309
+    // end of blinking -> remove blinking rows, new stone
310
+    if (m_blinking >= 8) {
311
+      // remove blinking rows
312
+      for (int b = 0; b < m_height; ++b) {
313
+        if (m_rowsBlink.at(b)) {
314
+          // move rows 0..b-1 one row down, i.e., to rows 1..b
315
+          for (int y = b, i = b * m_width + m_width - 1; y > 0; --y) {
316
+            for (int x = m_width - 1; x >= 0; --x, --i) {
317
+              m_field.at(i) = m_field.at(i - m_width);
318
+            }
319
+          }
320
+          // clear first row
321
+          for (int x = 0; x < m_width; ++x) {
322
+            m_field.at(x) = -1;
323
+          }
324
+          // row not blinking any more
325
+          m_rowsBlink.at(b) = false;
326
+        }
327
+      }
328
+      // blinking done
329
+      m_blinking = 0;
330
+      // new stone
331
+      newStone();
332
+    }
333
+
334
+    // redraw image and send frame
335
+    redraw();
336
+
337
+  // falling stone
338
+  } else {
339
+
295 340
     // stone can move down by one pixel
296 341
     if (checkStoneFit(m_stone, m_rot, m_posY + 1, m_posX)) {
297 342
       // move stone down by one pixel
... ...
@@ -305,17 +350,50 @@ void Tetris::timeStep()
305 350
       // add stone permanently to field at current position
306 351
       freezeStone(m_stone, m_rot, m_posY, m_posX);
307 352
       drawStone(m_stone, m_rot, m_posY, m_posX); // TODO: frozen color
308
-    // prepare new stone
309
-    newStone();
353
+      // no current stone any more
354
+      m_stone = -1;
355
+      // check for completed rows, (afterwards: new stone)
356
+      checkComplete();
310 357
     }
311 358
 
312 359
     // send updated image buffer as frame
313 360
     sendFrame();
314 361
 
362
+  } // falling stone
363
+
315 364
   // request next time step
316 365
   planTimeStep();
317 366
 }
318 367
 
368
+/// check for completed rows to disappear (new stone afterwards)
369
+void Tetris::checkComplete()
370
+{
371
+  // collect y coordinated of completed rows -> m_rowsBlink
372
+  bool blink = false;
373
+  for (int y = 0, i = 0; y < m_height; ++y) {
374
+    bool complete = true;
375
+    for (int x = 0; x < m_width; ++x, ++i) {
376
+      if (m_field.at(i) < 0) {
377
+        complete = false;
378
+      }
379
+    }
380
+    m_rowsBlink.at(y) = complete;
381
+    if (complete) {
382
+      blink = true;
383
+    }
384
+  }
385
+
386
+  // no completed rows -> new stone
387
+  if (! blink) {
388
+    newStone();
389
+  }
390
+
391
+  // start blinking (start with rows visible, last bit == 0)
392
+  else {
393
+    m_blinking = 2;
394
+  }
395
+}
396
+
319 397
 /// set up a new stone
320 398
 void Tetris::newStone()
321 399
 {
... ...
@@ -121,6 +121,9 @@ protected:
121 121
   /// process next time step of game
122 122
   virtual void timeStep();
123 123
 
124
+  /// check for completed rows to disappear (new stone afterwards)
125
+  void checkComplete();
126
+
124 127
   /// set up a new stone
125 128
   void newStone();
126 129
 
... ...
@@ -181,12 +184,12 @@ protected:
181 184
   int   m_posX;     ///< x position of current stone
182 185
   int   m_posY;     ///< y position of current stone
183 186
   bool  m_dropping; ///< whether currently dropping a stone
184
-  int   m_blinking; ///< step of blinking: 0 not blinking
187
+  int   m_blinking; ///< step of blinking: 0 not blinking, lsb == 0 -> visible
185 188
 
186 189
   /// tetris field (y * m_width + x), -1 for free, >= 0 for pixel from stone
187 190
   std::vector<int> m_field;
188
-  /// sorted y indices of rows that are currently blinking
189
-  std::vector<int> m_rowsBlink;
191
+  /// rows that are currently blinking (bool for each row, true when blinking)
192
+  std::vector<bool> m_rowsBlink;
190 193
 }; // class Tetris
191 194
 
192 195
 } // namespace Blinker
193 196