summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2019-04-10 15:14:00 -0400
committerAndrew Opalach <andrew@akon.city> 2019-04-10 15:14:00 -0400
commit60699af63f92f43cc4b4b9e3050fcdd2a8468281 (patch)
tree20a18af2774daa43ae7f4352dd032a24f7e06075 /core/src
parent4527bc20241068731c62101d0467d416119ec0c4 (diff)
downloadcetris-60699af63f92f43cc4b4b9e3050fcdd2a8468281.tar.gz
cetris-60699af63f92f43cc4b4b9e3050fcdd2a8468281.tar.bz2
cetris-60699af63f92f43cc4b4b9e3050fcdd2a8468281.zip
add opengl fronend, remove sdl frontend, refactor build system
Diffstat (limited to 'core/src')
-rw-r--r--core/src/cetris.c262
-rw-r--r--core/src/input.c51
-rw-r--r--core/src/matrix.c269
-rw-r--r--core/src/test.c72
4 files changed, 654 insertions, 0 deletions
diff --git a/core/src/cetris.c b/core/src/cetris.c
new file mode 100644
index 0000000..e6bbc8d
--- /dev/null
+++ b/core/src/cetris.c
@@ -0,0 +1,262 @@
+#include <stdio.h>
+#include <locale.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "cetris.h"
+#include "types.h"
+#include "matrix.h"
+#include "input.h"
+
+#ifdef BUILD_TESTS
+#include "test.h"
+#endif
+
+/* FUNCTION PROTOTYPES */
+
+static void init_piece_queue(struct cetris_game* g);
+static void shuffle_queue(struct cetris_game* g);
+static void set_constants(struct cetris_game* g);
+
+/* LEVEL DROP SPEED VALUES */
+
+static const uint8_t level_drop_delay[20] = {
+ 48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3
+};
+
+/* GAME FUNCTIONS */
+
+void init_game(struct cetris_game* g) {
+ srand(time(NULL));
+
+ memset(g->board, 0, sizeof(slot) * CETRIS_BOARD_X * CETRIS_BOARD_Y);
+
+#ifdef BUILD_TESTS
+ apply_test_board(g, TSPIN_NO_LINES);
+#endif
+
+ g->tick = 0;
+ g->next_drop_tick = level_drop_delay[0];
+
+ g->current_index = 0;
+
+ memset(g->held_moves, 0, sizeof(uint8_t) * 7);
+ g->das_repeat = 0;
+ g->prev_das_move = 0;
+ g->das_move_tick = 0;
+ g->down_move_tick = 0;
+
+ g->lines = 0;
+ g->level = 1;
+ g->game_over = 0;
+
+ g->tspin = 0;
+ g->mini_tspin = 0;
+
+ g->score = 0;
+
+ init_piece_queue(g);
+ shuffle_queue(g);
+
+ next_piece(g);
+}
+
+void init_piece_queue(struct cetris_game* g) {
+ for (uint8_t i = 0; i < 7; i++) {
+ switch (i) {
+ case 0:
+ g->piece_queue[i].t = O;
+ g->piece_queue[i].c = COLOR_O;
+ break;
+ case 1:
+ g->piece_queue[i].t = I;
+ g->piece_queue[i].c = COLOR_I;
+ break;
+ case 2:
+ g->piece_queue[i].t = S;
+ g->piece_queue[i].c = COLOR_S;
+ break;
+ case 3:
+ g->piece_queue[i].t = Z;
+ g->piece_queue[i].c = COLOR_Z;
+ break;
+ case 4:
+ g->piece_queue[i].t = L;
+ g->piece_queue[i].c = COLOR_L;
+ break;
+ case 5:
+ g->piece_queue[i].t = J;
+ g->piece_queue[i].c = COLOR_J;
+ break;
+ case 6:
+ g->piece_queue[i].t = T;
+ g->piece_queue[i].c = COLOR_T;
+ break;
+ }
+ memcpy(g->piece_queue[i].m, default_matrices[i], sizeof(piece_matrix));
+ g->piece_queue[i].r = INIT;
+ g->piece_queue[i].lock_tick = 0;
+ g->piece_queue[i].pos = (vec2){3, 20}; // y = 22 - 1 for matrix
+ }
+}
+
+void shuffle_queue(struct cetris_game* g) {
+ for (uint8_t i = 0; i < 7; i++) {
+ struct tetrimino t = g->piece_queue[i];
+ uint8_t rand_index = rand() % 7;
+ g->piece_queue[i] = g->piece_queue[rand_index];
+ g->piece_queue[rand_index] = t;
+ }
+}
+
+void update_game_tick(struct cetris_game* g) {
+ if (g->game_over) return;
+
+ uint8_t did_move = 0;
+ if (g->tick == g->next_drop_tick) {
+ move_current(g, basic_movements[DOWN]);
+ did_move = 1;
+ g->next_drop_tick = g->tick + level_drop_delay[g->level - 1];
+ }
+
+ /* lock piece if it was hovering for CETRIS_LOCK_DELAY */
+ if (g->current.lock_tick && g->current.lock_tick <= g->tick) {
+ g->current.pos.y++;
+ if (check_new_matrix(g, g->current.m) <= 0) {
+ next_piece(g);
+ }
+ g->current.pos.y--;
+ g->current.lock_tick = 0;
+ }
+
+ if (handle_inputs(g)) {
+ did_move = 1;
+ }
+
+ if (did_move) wipe_board(g);
+
+ g->tick++;
+}
+
+void next_piece(struct cetris_game* g) {
+ set_constants(g);
+
+ g->current = g->piece_queue[g->current_index];
+ if (check_new_matrix(g, g->current.m) <= 0) {
+ g->game_over = 1;
+ }
+ g->current_index++;
+
+ if (g->current_index >= 7) {
+ g->current_index = 0;
+ shuffle_queue(g);
+ }
+}
+
+void set_constants(struct cetris_game* g) {
+ for (uint8_t x = 0; x < CETRIS_BOARD_X; x++) {
+ for (uint8_t y = 0; y < CETRIS_BOARD_Y; y++) {
+ if (g->board[x][y].occupied) g->board[x][y].constant = 1;
+ }
+ }
+}
+
+void add_score(struct cetris_game* g, int lines) {
+ if (!g->tspin && !g->mini_tspin) {
+ switch (lines) {
+ case 1: g->score += 100 * g->level; break;
+ case 2: g->score += 300 * g->level; break;
+ case 3: g->score += 500 * g->level; break;
+ case 4: g->score += 800 * g->level; break;
+ }
+ } else if (g->tspin) {
+ switch (lines) {
+ case 0: g->score += 400 * g->level; break;
+ case 1: g->score += 800 * g->level; break;
+ case 2: g->score += 1200 * g->level; break;
+ case 3: g->score += 1600 * g->level; break;
+ }
+ g->tspin = 0;
+ } else if (g->mini_tspin) {
+ switch (lines) {
+ case 0: g->score += 100 * g->level; break;
+ case 1: g->score += 200 * g->level; break;
+ case 2: g->score += 400 * g->level; break;
+ }
+ g->mini_tspin = 0;
+ }
+}
+
+void wipe_board(struct cetris_game* g) {
+ uint8_t lines_cleared = 0;
+ for (uint8_t y = 0; y < CETRIS_BOARD_Y; y++) {
+ uint8_t clear_line = 1;
+ for (uint8_t x = 0; x < CETRIS_BOARD_X; x++) {
+ if (!g->board[x][y].constant) {
+ memset(&g->board[x][y], 0, sizeof(slot));
+ }
+
+ if (g->board[x][y].remove_tick && g->board[x][y].remove_tick <= g->tick) {
+ memset(&g->board[x][y], 0, sizeof(slot));
+ for (int8_t s = y - 1; s >= 0; s--) {
+ g->board[x][s + 1] = g->board[x][s];
+ }
+ }
+
+ if (!g->board[x][y].occupied || g->board[x][y].remove_tick > 0) {
+ clear_line = 0;
+ }
+ }
+ if (clear_line) {
+ lines_cleared++;
+ for (uint8_t x = 0; x < CETRIS_BOARD_X; x++) {
+ g->board[x][y].remove_tick = g->tick + CETRIS_LINE_CLEAR_DELAY;
+ }
+ }
+ }
+
+ overlay_current_matrix(g);
+
+ assert(lines_cleared <= 4);
+ if (lines_cleared > 0) {
+ add_score(g, lines_cleared);
+ if (lines_cleared > 0) {
+ g->lines += lines_cleared;
+ if (g->lines >= (g->level * 10) && g->level <= 20) g->level++;
+ }
+ }
+}
+
+/* MOVEMENT FUNCTIONS */
+
+void move_peice(struct cetris_game* g, input_t move) {
+ if (!g->held_moves[move]) {
+ switch (move) {
+ case LEFT:
+ case RIGHT:
+ case DOWN:
+ move_current(g, basic_movements[move]);
+ break;
+ case HARD_DROP:
+ hard_drop(g);
+ break;
+ case ROTATE_CW:
+ rotate_matrix(g, 1);
+ break;
+ case ROTATE_CCW:
+ rotate_matrix(g, 0);
+ break;
+ }
+ }
+ g->held_moves[move] = 1;
+}
+
+void stop_holding(struct cetris_game* g, input_t move) {
+ g->held_moves[move] = 0;
+ if (move == RIGHT || move == LEFT) {
+ g->das_move_tick = 0;
+ g->das_repeat = 0;
+ } else if (move == DOWN) g->down_move_tick = 0;
+}
diff --git a/core/src/input.c b/core/src/input.c
new file mode 100644
index 0000000..9fd9142
--- /dev/null
+++ b/core/src/input.c
@@ -0,0 +1,51 @@
+#include <stdint.h>
+
+#include "input.h"
+
+#include "types.h"
+#include "matrix.h"
+#include "cetris.h"
+
+uint8_t handle_inputs(struct cetris_game* g) {
+ if ((g->held_moves[RIGHT] || g->held_moves[LEFT]) && !g->das_move_tick) {
+ if (g->das_repeat == 0) {
+ g->das_move_tick = g->tick + CETRIS_DAS_DELAY;
+ } else if (g->das_repeat > 0) {
+ g->das_move_tick = g->tick + CETRIS_DAS_PERIOD;
+ }
+ }
+
+ if (g->held_moves[DOWN] && !g->down_move_tick) {
+ g->down_move_tick = g->tick + CETRIS_DROP_PERIOD;
+ }
+
+ uint8_t did_move = 0;
+ if (g->das_move_tick && g->tick >= g->das_move_tick) {
+ if (g->held_moves[RIGHT]) {
+ if (g->prev_das_move == RIGHT || g->das_repeat == 0) g->das_repeat++;
+ else g->das_repeat = 0;
+ move_current(g, basic_movements[RIGHT]);
+ g->prev_das_move = RIGHT;
+ }
+
+ if (g->held_moves[LEFT]) {
+ if (g->prev_das_move == LEFT || g->das_repeat == 0) g->das_repeat++;
+ else g->das_repeat = 0;
+ move_current(g, basic_movements[LEFT]);
+ g->prev_das_move = LEFT;
+ }
+
+ g->das_move_tick = 0;
+ did_move = 1;
+ }
+
+ if (g->down_move_tick && g->tick >= g->down_move_tick) {
+ if (g->held_moves[DOWN]) {
+ move_current(g, basic_movements[DOWN]);
+ }
+
+ g->down_move_tick = 0;
+ did_move = 1;
+ }
+ return did_move;
+}
diff --git a/core/src/matrix.c b/core/src/matrix.c
new file mode 100644
index 0000000..5ee971b
--- /dev/null
+++ b/core/src/matrix.c
@@ -0,0 +1,269 @@
+#include <string.h>
+
+#include "matrix.h"
+#include "types.h"
+#include "cetris.h"
+
+/* SRS WALL KICK VALUES */
+
+// https://tetris.wiki/SRS
+static const vec2 srs_wall_kicks[8][5] = {
+ { {0, 0}, {-1, 0}, {-1,1}, {0,-2}, {-1,-2} }, // 0->R
+ { {0, 0}, {1, 0}, {1,-1}, {0,2}, {1,2} }, // R->0
+ { {0, 0}, {1, 0}, {1,-1}, {0,2}, {1,2} }, // R->2
+ { {0, 0}, {-1, 0}, {-1,1}, {0,-2}, {-1,-2} }, // 2->R
+ { {0, 0}, {1, 0}, {1,1}, {0,-2}, {1,-2} }, // 2->L
+ { {0, 0}, {-1, 0}, {-1,-1}, {0,2}, {-1,2} }, // L->2
+ { {0, 0}, {-1, 0}, {-1,-1}, {0,2}, {-1,2} }, // L->0
+ { {0, 0}, {1, 0}, {1,1}, {0,-2}, {1,-2} } // 0->L
+};
+
+static const vec2 srs_wall_kicks_i[8][5] = {
+ { {0, 0}, {-2, 0}, {1, 0}, {-2,-1}, {1,2} }, // 0->R
+ { {0, 0}, {2, 0}, {-1, 0}, {2,1}, {-1,-2} }, // R->0
+ { {0, 0}, {-1, 0}, {2, 0}, {-1,2}, {2,-1} }, // R->2
+ { {0, 0}, {1, 0}, {-2, 0}, {1,-2}, {-2,1} }, // 2->R
+ { {0, 0}, {2, 0}, {-1, 0}, {2,1}, {-1,-2} }, // 2->L
+ { {0, 0}, {-2, 0}, {1, 0}, {-2,-1}, {1,2} }, // L->2
+ { {0, 0}, {1, 0}, {-2, 0}, {1,-2}, {-2,1} }, // L->0
+ { {0, 0}, {-1, 0}, {2, 0}, {-1,2}, {2,-1} } // 0->L
+};
+
+/* DEFAULT MATRIX FOR EACH POSSIBLE TETRIMINO */
+
+const piece_matrix default_matrices[7] = {
+ {
+ { 0, 0, 0, 0 },
+ { 0, 1, 1, 0 },
+ { 0, 1, 1, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 1, 1, 1, 1 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 0, 1, 1, 0 },
+ { 1, 1, 0, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 1, 1, 0, 0 },
+ { 0, 1, 1, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 1, 1, 1, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 1, 0, 0, 0 },
+ { 1, 1, 1, 0 },
+ { 0, 0, 0, 0 }
+ },
+
+ {
+ { 0, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 1, 1, 1, 0 },
+ { 0, 0, 0, 0 }
+ }
+};
+
+/* MATRIX MODIFICATION */
+
+const vec2 basic_movements[4] = {
+ {0, 0}, {0, 1}, {1, 0}, {-1, 0} // NONE, DOWN, RIGHT, LEFT
+};
+
+void move_current(struct cetris_game* g, vec2 offset) {
+ if (g->game_over) return;
+
+ g->current.pos.y += offset.y;
+ g->current.pos.x += offset.x;
+
+ int8_t check = check_new_matrix(g, g->current.m);
+ if (check <= 0) {
+ g->current.pos.y -= offset.y;
+ g->current.pos.x -= offset.x;
+
+ if (check == -1 && g->current.lock_tick == 0) {
+ g->current.lock_tick = g->tick + 30;
+ }
+ }
+
+ wipe_board(g);
+}
+
+void overlay_current_matrix(struct cetris_game* g) {
+ for (uint8_t y = 0; y < 4; y++) {
+ for (uint8_t x = 0; x < 4; x++) {
+ vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y};
+ if (g->current.m[y][x]) {
+ g->board[r.x][r.y].occupied = 1;
+ g->board[r.x][r.y].c = g->current.c;
+ }
+ }
+ }
+}
+
+void hard_drop(struct cetris_game* g) {
+ if (g->game_over) return;
+
+ uint8_t drop = 0;
+ uint8_t drop_count = 0;
+ while (!drop) {
+ g->current.pos.y++;
+ drop_count++;
+ int8_t check = check_new_matrix(g, g->current.m);
+ if (check <= 0) {
+ g->current.pos.y--;
+ drop_count--;
+ drop = 1;
+ }
+ }
+
+ g->score += 2 * drop_count; // 2 score for each harddrop cell
+
+ wipe_board(g);
+ next_piece(g);
+}
+
+void rotate_matrix(struct cetris_game* g, int clockwise) {
+ if (g->game_over) return;
+ if (g->current.t == O) return;
+
+ rstate next;
+ int8_t wall_kick;
+ switch (g->current.r) {
+ case INIT:
+ if (clockwise) {
+ next = ONCE_RIGHT;
+ wall_kick = 0;
+ } else {
+ next = ONCE_LEFT;
+ wall_kick = 7;
+ } break;
+ case ONCE_RIGHT:
+ if (clockwise) {
+ next = TWICE;
+ wall_kick = 2;
+ } else {
+ next = INIT;
+ wall_kick = 1;
+ } break;
+ case ONCE_LEFT:
+ if (clockwise) {
+ next = INIT;
+ wall_kick = 6;
+ } else {
+ next = TWICE;
+ wall_kick = 5;
+ } break;
+ case TWICE:
+ if (clockwise) {
+ next = ONCE_LEFT;
+ wall_kick = 4;
+ } else {
+ next = ONCE_RIGHT;
+ wall_kick = 3;
+ } break;
+ }
+
+ piece_matrix m;
+ memset(m, 0, sizeof(piece_matrix));
+
+ for (uint8_t x = 0; x < 4; x++) {
+ for (uint8_t y = 0; y < 4; y++) {
+ if (g->current.m[y][x]) {
+ uint8_t new_x = (clockwise) ? 1 - (y - 2) : 1 + (y - 2);
+ uint8_t new_y = (clockwise) ? 2 + (x - 1) : 2 - (x - 1);
+
+ if (g->current.t == I) {
+ if (clockwise) new_y--;
+ else new_x++;
+ }
+
+ m[new_y][new_x] = 1;
+ }
+ }
+ }
+
+ vec2 kick;
+ uint8_t set_current = 0;
+ uint8_t did_kick = 0;
+ for (uint8_t i = 0; i < 5; i++) {
+ if (g->current.t == I) {
+ kick = srs_wall_kicks_i[wall_kick][i];
+ } else {
+ kick = srs_wall_kicks[wall_kick][i];
+ }
+ g->current.pos.x += kick.x;
+ g->current.pos.y += kick.y;
+ if (check_new_matrix(g, m) > 0) {
+ set_current = 1;
+ if (i > 0) did_kick = 1;
+ break;
+ } else {
+ g->current.pos.x -= kick.x;
+ g->current.pos.y -= kick.y;
+ }
+ }
+
+ if (set_current) {
+ /* check for tspin */
+ if (g->current.t == T) {
+ uint8_t did_tspin = 1;
+ for (uint8_t i = 0; i < 4; i++) {
+ g->current.pos.x += basic_movements[i].x;
+ g->current.pos.y += basic_movements[i].y;
+
+ if (check_new_matrix(g, m) == 1)
+ did_tspin = 0;
+
+ g->current.pos.x -= basic_movements[i].x;
+ g->current.pos.y -= basic_movements[i].y;
+ }
+
+ if (did_tspin) {
+ if (did_kick) g->mini_tspin = 1;
+ else g->tspin = 1;
+ }
+ }
+
+ g->current.r = next;
+ memcpy(g->current.m, m, sizeof(piece_matrix));
+ wipe_board(g);
+ }
+}
+
+int8_t check_new_matrix(struct cetris_game* g, piece_matrix m) {
+ for (uint8_t x = 0; x < 4; x++) {
+ for (uint8_t y = 0; y < 4; y++) {
+ vec2 r = (vec2){g->current.pos.x + x, g->current.pos.y + y};
+ if (m[y][x]) {
+ if (r.x > CETRIS_BOARD_X - 1 || r.x < 0)
+ return 0;
+
+ if (r.y > CETRIS_BOARD_Y - 1 || r.y < 0)
+ return -1;
+
+ if (g->board[r.x][r.y].occupied && g->board[r.x][r.y].constant)
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
diff --git a/core/src/test.c b/core/src/test.c
new file mode 100644
index 0000000..33badbd
--- /dev/null
+++ b/core/src/test.c
@@ -0,0 +1,72 @@
+#include "test.h"
+#include "cetris.h"
+
+int tspin_board[20][10] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
+ { 1, 1, 1, 0, 0, 0, 1, 1, 1, 1 },
+ { 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }
+};
+
+int tspin_no_lines_board[20][10] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 1, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }
+};
+
+void apply_test_board(struct cetris_game* g, enum tests t) {
+ int (*board)[20][10];
+ switch (t) {
+ case TSPIN:
+ board = &tspin_board;
+ break;
+ case TSPIN_NO_LINES:
+ board = &tspin_no_lines_board;
+ break;
+ default:
+ return;
+ }
+ for (int y = CETRIS_BOARD_VISABLE; y < CETRIS_BOARD_Y; y++) {
+ for (int x = 0; x < 10; x++) {
+ if ((*board)[y - CETRIS_BOARD_VISABLE][x]) {
+ g->board[x][y].occupied = 1;
+ g->board[x][y].constant = 1;
+ g->board[x][y].c = COLOR_I;
+ }
+ }
+ }
+}
+