From 27a9537652d975477f664c266f8c3b7a56a25529 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Wed, 13 Mar 2019 18:39:49 -0400 Subject: more restruture --- cetris.c | 576 --------------------------------- cetris.h | 118 ------- frontend/ncurses/ncurses_ui.c | 20 +- frontend/sdl/.border.jpeg-autosave.kra | Bin 71027 -> 0 bytes frontend/sdl/sdl_ui.c | 10 +- meson.build | 16 +- src/cetris.c | 275 ++++++++++++++++ src/cetris.h | 102 ++++++ src/input.c | 44 +++ src/input.h | 25 ++ src/matrix.c | 282 ++++++++++++++++ src/matrix.h | 16 + src/test.c | 71 ++++ src/test.h | 8 + test.c | 71 ---- test.h | 8 - 16 files changed, 848 insertions(+), 794 deletions(-) delete mode 100644 cetris.c delete mode 100644 cetris.h delete mode 100644 frontend/sdl/.border.jpeg-autosave.kra create mode 100644 src/cetris.c create mode 100644 src/cetris.h create mode 100644 src/input.c create mode 100644 src/input.h create mode 100644 src/matrix.c create mode 100644 src/matrix.h create mode 100644 src/test.c create mode 100644 src/test.h delete mode 100644 test.c delete mode 100644 test.h diff --git a/cetris.c b/cetris.c deleted file mode 100644 index 26da86a..0000000 --- a/cetris.c +++ /dev/null @@ -1,576 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "cetris.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 next_piece(struct cetris_game* g); -static void move_current(struct cetris_game* g, vec2 offset); -static int8_t check_new_matrix(struct cetris_game* g, piece_matrix m); -static void wipe_board(struct cetris_game* g); -static void set_constants(struct cetris_game* g); -static void rotate_matrix(struct cetris_game* g, int clockwise); -static void overlay_current_matrix(struct cetris_game* g); -static uint8_t handle_inputs(struct cetris_game* g); - -/* DEFAULT MATRIX FOR EACH POSSIBLE TETRIMINO */ - -static 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 } - } -}; - -/* 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 -}; - -/* 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 -}; - -/* MATRIX MODIFICATION */ - -static 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) { - g->current.pos.y += offset.y; - g->current.pos.x += offset.x; - - int8_t check = check_new_matrix(g, g->current.mat); - 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 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.mat); - 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 move_hard_drop(struct cetris_game* g) { - if (g->input.held_move != HARD_DROP) { - hard_drop(g); - } - g->input.held_move = HARD_DROP; -} - -void move_down(struct cetris_game* g) { - if (g->input.held_move != DOWN) { - move_current(g, basic_movements[DOWN]); - } - g->input.held_move = DOWN; -} - -void move_right(struct cetris_game* g) { - if (g->input.held_move != RIGHT) { - move_current(g, basic_movements[RIGHT]); - } - g->input.held_move = RIGHT; -} - -void move_left(struct cetris_game* g) { - if (g->input.held_move != LEFT) { - move_current(g, basic_movements[LEFT]); - } - g->input.held_move = LEFT; -} - -void rotate_matrix(struct cetris_game* g, int clockwise) { - if (g->current.t == O) return; - - rstate next; - switch (g->current.r) { - case INIT: - next = (clockwise) ? RRIGHT : RLEFT; - break; - case RRIGHT: - next = (clockwise) ? TWO : INIT; - break; - case RLEFT: - next = (clockwise) ? INIT : TWO; - break; - case TWO: - next = (clockwise) ? RLEFT : RRIGHT; - 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.mat[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; - } - } - } - - uint8_t wall_kick; - switch (g->current.r) { - case INIT: - wall_kick = (next == RRIGHT) ? 0 : 7; - break; - case RRIGHT: - wall_kick = (next == INIT) ? 1 : 2; - break; - case RLEFT: - wall_kick = (next == INIT) ? 6 : 5; - break; - case TWO: - wall_kick = (next == RRIGHT) ? 3 : 4; - break; - default: // check for invalid rotations - assert(0); - } - - 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.mat, m, sizeof(piece_matrix)); - wipe_board(g); - } -} - -void rotate_clockwise(struct cetris_game* g) { - if (g->input.held_move != ROTATE_CW) { - rotate_matrix(g, 1); - } - g->input.held_move = ROTATE_CW; -} - -void rotate_counterclockwise(struct cetris_game* g) { - if (g->input.held_move != ROTATE_CCW) { - rotate_matrix(g, 0); - } - g->input.held_move = ROTATE_CCW; -} - -int8_t check_new_matrix(struct cetris_game* g, piece_matrix m) { - vec2 r; - for (uint8_t x = 0; x < 4; x++) { - for (uint8_t y = 0; y < 4; y++) { - r = (vec2){g->current.pos.x + x, g->current.pos.y + y}; - if (m[y][x]) { - if (r.x > BOARD_X - 1 || r.x < 0) return 0; - - if (r.y > 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; -} - -/* GAME FUNCTIONS */ - -void init_game(struct cetris_game* g) { - srand(time(NULL)); - - memset(g->board, 0, sizeof(slot) * BOARD_X * 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; - - g->input.held_move = 0; - g->input.prev_move = 0; - g->input.next_move_tick = 0; - g->input.can_rotate = 0; - g->input.can_hard_drop = 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].mat, 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; - } -} - -uint8_t handle_inputs(struct cetris_game* g) { - uint8_t did_move = 0; - if (g->input.held_move && !g->input.next_move_tick) { - if (g->input.held_move == RIGHT || g->input.held_move == LEFT) { - if (g->input.prev_move == g->input.held_move) { - g->input.next_move_tick = g->tick + CETRIS_DAS_PERIOD; - } else { - g->input.next_move_tick = g->tick + CETRIS_DAS_DELAY; - } - } else { - g->input.next_move_tick = g->tick + CETRIS_DROP_PERIOD; - } - } - - if (g->input.next_move_tick && g->tick >= g->input.next_move_tick) { - switch (g->input.held_move) { - case DOWN: - g->score++; - move_current(g, basic_movements[DOWN]); - break; - case LEFT: - move_current(g, basic_movements[LEFT]); - break; - case RIGHT: - move_current(g, basic_movements[RIGHT]); - break; - } - did_move = 1; - } - - if (did_move) { - g->input.next_move_tick = 0; - g->input.prev_move = g->input.held_move; - } - return did_move; -} - -void clear_held_key(struct input_manager* input) { - input->prev_move = 0; - input->held_move = 0; - input->next_move_tick = 0; -} - -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.mat) <= 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.mat) <= 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 < BOARD_X; x++) { - for (uint8_t y = 0; y < 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 overlay_current_matrix(struct cetris_game* g) { - vec2 r; - for (uint8_t y = 0; y < 4; y++) { - for (uint8_t x = 0; x < 4; x++) { - r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; - if (g->current.mat[y][x]) { - g->board[r.x][r.y].occupied = 1; - g->board[r.x][r.y].c = g->current.c; - } - } - } -} - -void wipe_board(struct cetris_game* g) { - uint8_t lines_cleared = 0; - for (uint8_t y = 0; y < BOARD_Y; y++) { - uint8_t clear_line = 1; - for (uint8_t x = 0; x < 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 < 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++; - } - } -} diff --git a/cetris.h b/cetris.h deleted file mode 100644 index 8cab167..0000000 --- a/cetris.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include - -#define BOARD_X 10 -#define BOARD_Y 43 -#define BOARD_VISABLE 23 - -#define CETRIS_HZ 60 -#define CETRIS_DAS_DELAY 11 -#define CETRIS_DAS_PERIOD 5 -#define CETRIS_DROP_PERIOD 2 -#define CETRIS_LINE_CLEAR_DELAY 40 -#define CETRIS_WAIT_ON_CLEAR 0 - -typedef struct { - int8_t x; - int8_t y; -} vec2; - -typedef enum { - O, I, S, Z, L, J, T -} type; - -typedef enum { - COLOR_NONE, - COLOR_O, - COLOR_I, - COLOR_S, - COLOR_Z, - COLOR_L, - COLOR_J, - COLOR_T -} color; - -typedef enum { - INIT, - RRIGHT, - RLEFT, - TWO -} rstate; - -typedef uint8_t piece_matrix[4][4]; - -struct tetrimino { - type t; - rstate r; - color c; - piece_matrix mat; - vec2 pos; - int lock_tick; -}; - -typedef struct { - uint8_t occupied; - uint8_t constant; - int remove_tick; - color c; -} slot; - -typedef enum { - DOWN = 1, - RIGHT = 2, - LEFT = 3, - ROTATE_CCW = 4, - ROTATE_CW = 5, - HARD_DROP = 6 -} move; - -struct input_manager { - move held_move; - move prev_move; - int next_move_tick; - uint8_t can_rotate; - uint8_t can_hard_drop; -}; - -struct cetris_game { - /* playfield represented by a 2d array */ - slot board[BOARD_X][BOARD_Y]; - - /* constant queue of all 7 possible tetrimino */ - struct tetrimino piece_queue[7]; - - /* current tetrimino */ - struct tetrimino current; - uint8_t current_index; - - struct input_manager input; - - /* internal game tick */ - int tick; - int next_drop_tick; - - /* progress trackers */ - int lines; - uint8_t level; - uint8_t game_over; - - /* scoring flags */ - uint8_t tspin; - uint8_t mini_tspin; - - /* long int just incase */ - long int score; -}; - -/* API PROTOTYPES FUNCTIONS */ - -void init_game(struct cetris_game* g); -void update_game_tick(struct cetris_game* g); -void move_down(struct cetris_game* g); -void move_left(struct cetris_game* g); -void move_right(struct cetris_game* g); -void move_hard_drop(struct cetris_game* g); -void rotate_clockwise(struct cetris_game* g); -void rotate_counterclockwise(struct cetris_game* g); -void clear_held_key(struct input_manager* input); diff --git a/frontend/ncurses/ncurses_ui.c b/frontend/ncurses/ncurses_ui.c index 7a92c0f..98f2b4b 100644 --- a/frontend/ncurses/ncurses_ui.c +++ b/frontend/ncurses/ncurses_ui.c @@ -35,17 +35,17 @@ " \\--------------------/" #else #define BLOCK "[]" -#define PLAY_FIELD_STR " ┏━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ \n"\ +#define PLAY_FIELD_STR " ┏━━━━━━━━━━━┓ ┏━━━━━━━━━\n"\ " ┃ ┃ ┃ ┃ \n"\ - " ┃ ┃ ┗━━━━━━━━━━━━━━━┛ \n"\ + " ┃ ┃ ━━━━━━━━━┛ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ - " ┃ ┃ ┏━━━━━━━━━━━┓ \n"\ + " ┃ ┃ ┏━━━━━━┓ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ " ┃ ┃ ┃ ┃ \n"\ - " ┃ ┃ ┗━━━━━━━━━━━┛ \n"\ + " ┃ ┃ ┗━━━━━━┛ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ @@ -56,7 +56,7 @@ " ┃ ┃ \n"\ " ┃ ┃ \n"\ " ┃ ┃ \n"\ - " ┗━━━━━━━━━━━━━━━━━━━━┛ " + " ┗━━━━━━━━━━━┛ " #endif #define X_OFFSET 8 @@ -86,16 +86,16 @@ void curses_init() { void draw_board() { mvaddstr(0, 0, PLAY_FIELD_STR); - for (int x = 0; x < BOARD_X; x++) { - for (int y = BOARD_VISABLE; y < BOARD_Y; y++) { + for (int x = 0; x < CETRIS_BOARD_X; x++) { + for (int y = CETRIS_BOARD_VISABLE; y < CETRIS_BOARD_Y; y++) { if (game.board[x][y].occupied) { attron(COLOR_PAIR(game.board[x][y].c)); if (game.board[x][y].remove_tick > 0) { if (game.tick % 2 == 0) { - mvaddstr((y - BOARD_VISABLE) + 1, x * 2 + X_OFFSET, BLOCK); + mvaddstr((y - CETRIS_BOARD_VISABLE) + 1, x * 2 + X_OFFSET, BLOCK); } } else { - mvaddstr((y - BOARD_VISABLE) + 1, x * 2 + X_OFFSET, BLOCK); + mvaddstr((y - CETRIS_BOARD_VISABLE) + 1, x * 2 + X_OFFSET, BLOCK); } attroff(COLOR_PAIR(game.board[x][y].c)); } @@ -161,6 +161,8 @@ int main(void) { init_game(&game); } break; + default: + clear_held_key(&game.input); } update_game_tick(&game); erase(); diff --git a/frontend/sdl/.border.jpeg-autosave.kra b/frontend/sdl/.border.jpeg-autosave.kra deleted file mode 100644 index 69f596a..0000000 Binary files a/frontend/sdl/.border.jpeg-autosave.kra and /dev/null differ diff --git a/frontend/sdl/sdl_ui.c b/frontend/sdl/sdl_ui.c index 8eaecab..c5c9917 100644 --- a/frontend/sdl/sdl_ui.c +++ b/frontend/sdl/sdl_ui.c @@ -141,11 +141,11 @@ void draw_stuff() { free(lines); SDL_SetRenderDrawColor(render, 255, 255, 255, 124); - for (int x = 0; x < BOARD_X + 1; x++) { + for (int x = 0; x < CETRIS_BOARD_X + 1; x++) { int rx = BOARD_OFFSET_X + 1 + (x * 25); SDL_RenderDrawLine(render, rx, BOARD_OFFSET_Y + 1, rx, BOARD_OFFSET_Y + 500); } - for (int y = 0; y < BOARD_Y - BOARD_VISABLE + 1; y++) { + for (int y = 0; y < CETRIS_BOARD_Y - CETRIS_BOARD_VISABLE + 1; y++) { int ry = BOARD_OFFSET_Y + (y * 25); SDL_RenderDrawLine(render, BOARD_OFFSET_X + 1, ry, BOARD_OFFSET_X + 250, ry); } @@ -153,13 +153,13 @@ void draw_stuff() { sdl_color c; SDL_Rect r = {0, 0, 25, 25}; - for (int x = 0; x < BOARD_X; x++) { - for (int y = BOARD_VISABLE; y < BOARD_Y; y++) { + for (int x = 0; x < CETRIS_BOARD_X; x++) { + for (int y = CETRIS_BOARD_VISABLE; y < CETRIS_BOARD_Y; y++) { if (g.board[x][y].occupied) { c = colors[g.board[x][y].c]; SDL_SetTextureColorMod(block, c.r, c.g, c.b); r.x = BOARD_OFFSET_X + x * 25; - r.y = BOARD_OFFSET_Y + (y - BOARD_VISABLE) * 25; + r.y = BOARD_OFFSET_Y + (y - CETRIS_BOARD_VISABLE) * 25; SDL_RenderCopy(render, block, NULL, &r); } } diff --git a/meson.build b/meson.build index 5d5d1af..9fe6a8c 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,8 @@ project('cetris', 'c', compiler = meson.get_compiler('c') -src = ['cetris.c'] +src = ['src/input.c', 'src/matrix.c', 'src/cetris.c'] +inc = include_directories('src') if host_machine.system() != 'windows' if get_option('debug') == true @@ -12,7 +13,7 @@ if host_machine.system() != 'windows' endif endif -src += ['test.c'] +src += ['src/test.c'] sdlinc = [] sdlttfinc = [] @@ -28,11 +29,12 @@ if get_option('build_frontends') == true sdl2 = dependency('sdl2') sdl2_ttf = meson.get_compiler('c').find_library('SDL2_ttf') endif -# executable('cetris_curses', [src, 'frontend/ncurses/ncurses_ui.c'], -# dependencies: curses, -# install : false) + executable('cetris_curses', [src, 'frontend/ncurses/ncurses_ui.c'], + dependencies: curses, + include_directories: inc, + install : false) executable('cetris_sdl', [src, 'frontend/sdl/sdl_ui.c'], - include_directories: [sdlttfinc, sdlinc], + include_directories: [inc, sdlttfinc, sdlinc], dependencies: [sdl2, sdl2_ttf], - install : false) + install : false) endif diff --git a/src/cetris.c b/src/cetris.c new file mode 100644 index 0000000..7adf5b1 --- /dev/null +++ b/src/cetris.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include +#include + +#include "cetris.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 next_piece(struct cetris_game* g); +static void wipe_board(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; + + g->input.held_move = 0; + g->input.prev_move = 0; + g->input.next_move_tick = 0; + g->input.can_rotate = 0; + g->input.can_hard_drop = 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_down(struct cetris_game* g) { + if (g->input.held_move != DOWN) { + move_current(g, basic_movements[DOWN]); + } + g->input.held_move = DOWN; +} + +void move_right(struct cetris_game* g) { + if (g->input.held_move != RIGHT) { + move_current(g, basic_movements[RIGHT]); + } + g->input.held_move = RIGHT; +} + +void move_left(struct cetris_game* g) { + if (g->input.held_move != LEFT) { + move_current(g, basic_movements[LEFT]); + } + g->input.held_move = LEFT; +} + +void move_hard_drop(struct cetris_game* g) { + if (g->input.held_move != HARD_DROP) { + hard_drop(g); + } + g->input.held_move = HARD_DROP; +} + +void rotate_clockwise(struct cetris_game* g) { + if (g->input.held_move != ROTATE_CW) { + rotate_matrix(g, 1); + } + g->input.held_move = ROTATE_CW; +} + +void rotate_counterclockwise(struct cetris_game* g) { + if (g->input.held_move != ROTATE_CCW) { + rotate_matrix(g, 0); + } + g->input.held_move = ROTATE_CCW; +} diff --git a/src/cetris.h b/src/cetris.h new file mode 100644 index 0000000..1afc7ef --- /dev/null +++ b/src/cetris.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include "input.h" +#include "matrix.h" + +#define CETRIS_BOARD_X 10 +#define CETRIS_BOARD_Y 43 +#define CETRIS_BOARD_VISABLE 23 + +#define CETRIS_HZ 60 +#define CETRIS_DAS_DELAY 11 +#define CETRIS_DAS_PERIOD 3 +#define CETRIS_DROP_PERIOD 2 +#define CETRIS_LINE_CLEAR_DELAY 40 +#define CETRIS_WAIT_ON_CLEAR 0 + +typedef struct { + int8_t x; + int8_t y; +} vec2; + + +typedef enum { + O, I, S, Z, L, J, T +} type; + +typedef enum { + COLOR_NONE, + COLOR_O, + COLOR_I, + COLOR_S, + COLOR_Z, + COLOR_L, + COLOR_J, + COLOR_T +} color; + +typedef enum { + INIT, + ONCE_RIGHT, + ONCE_LEFT, + TWICE +} rstate; + +struct tetrimino { + type t; + rstate r; + color c; + piece_matrix m; + vec2 pos; + int lock_tick; +}; + +typedef struct { + uint8_t occupied; + uint8_t constant; + int remove_tick; + color c; +} slot; + +struct cetris_game { + /* playfield represented by a 2d array */ + slot board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; + + /* constant queue of all 7 possible tetrimino */ + struct tetrimino piece_queue[7]; + + /* current tetrimino */ + struct tetrimino current; + uint8_t current_index; + + struct input_manager input; + + /* internal game tick */ + int tick; + int next_drop_tick; + + /* progress trackers */ + int lines; + uint8_t level; + uint8_t game_over; + + /* scoring flags */ + uint8_t tspin; + uint8_t mini_tspin; + + /* long int just incase */ + long int score; +}; + +/* API PROTOTYPES FUNCTIONS */ + +void init_game(struct cetris_game* g); +void update_game_tick(struct cetris_game* g); +void move_down(struct cetris_game* g); +void move_left(struct cetris_game* g); +void move_right(struct cetris_game* g); +void move_hard_drop(struct cetris_game* g); +void rotate_clockwise(struct cetris_game* g); +void rotate_counterclockwise(struct cetris_game* g); diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..a77f4df --- /dev/null +++ b/src/input.c @@ -0,0 +1,44 @@ +#include "input.h" + +uint8_t handle_inputs(struct cetris_game* g) { + uint8_t did_move = 0; + if (g->input.held_move && !g->input.next_move_tick) { + if (g->input.held_move == RIGHT || g->input.held_move == LEFT) { + if (g->input.prev_move == g->input.held_move) { + g->input.next_move_tick = g->tick + CETRIS_DAS_PERIOD; + } else { + g->input.next_move_tick = g->tick + CETRIS_DAS_DELAY; + } + } else { + g->input.next_move_tick = g->tick + CETRIS_DROP_PERIOD; + } + } + + if (g->input.next_move_tick && g->tick >= g->input.next_move_tick) { + switch (g->input.held_move) { + case DOWN: + g->score++; + move_current(g, basic_movements[DOWN]); + break; + case LEFT: + move_current(g, basic_movements[LEFT]); + break; + case RIGHT: + move_current(g, basic_movements[RIGHT]); + break; + } + did_move = 1; + } + + if (did_move) { + g->input.next_move_tick = 0; + g->input.prev_move = g->input.held_move; + } + return did_move; +} + +void clear_held_key(struct input_manager* input) { + input->prev_move = 0; + input->held_move = 0; + input->next_move_tick = 0; +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..188ca71 --- /dev/null +++ b/src/input.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "cetris.h" + +typedef enum { + DOWN = 1, + RIGHT = 2, + LEFT = 3, + ROTATE_CCW = 4, + ROTATE_CW = 5, + HARD_DROP = 6 +} input_t; + +struct input_manager { + input_t held_move; + input_t prev_move; + int next_move_tick; + uint8_t can_rotate; + uint8_t can_hard_drop; +}; + +uint8_t handle_inputs(struct cetris_game* g); +void clear_held_key(struct input_manager* input); diff --git a/src/matrix.c b/src/matrix.c new file mode 100644 index 0000000..796b4a8 --- /dev/null +++ b/src/matrix.c @@ -0,0 +1,282 @@ +#include + +#include "matrix.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) { + g->current.pos.y += offset.y; + g->current.pos.x += offset.x; + + int8_t check = check_new_matrix(g, g->current.mat); + 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.mat[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.mat); + 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->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.mat[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; + } + } + } + + /* + uint8_t wall_kick; + switch (g->current.r) { + case INIT: + wall_kick = (next == RRIGHT) ? 0 : 7; + break; + case ONCE_RIGHT: + wall_kick = (next == INIT) ? 1 : 2; + break; + case ONCE_LEFT: + wall_kick = (next == INIT) ? 6 : 5; + break; + case TWICE: + wall_kick = (next == RRIGHT) ? 3 : 4; + break; + default: // check for invalid rotations + assert(0); + } + */ + + 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.mat, m, sizeof(piece_matrix)); + wipe_board(g); + } +} + +int8_t check_new_matrix(struct cetris_game* g, piece_matrix m) { + vec2 r; + for (uint8_t x = 0; x < 4; x++) { + for (uint8_t y = 0; y < 4; y++) { + 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/src/matrix.h b/src/matrix.h new file mode 100644 index 0000000..57166cc --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "cetris.h" + +typedef uint8_t piece_matrix[4][4]; + +const piece_matrix default_matrices[7]; +const vec2 basic_movements[4]; + +void move_current(struct cetris_game* g, vec2 offset); +void overlay_current_matrix(struct cetris_game* g); +void hard_drop(struct cetris_game* g); +void rotate_matrix(struct cetris_game* g, int clockwise); +int8_t check_new_matrix(struct cetris_game* g, piece_matrix m); diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..fa7aaa8 --- /dev/null +++ b/src/test.c @@ -0,0 +1,71 @@ +#include "test.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 = BOARD_VISABLE; y < BOARD_Y; y++) { + for (int x = 0; x < 10; x++) { + if ((*board)[y - BOARD_VISABLE][x]) { + g->board[x][y].occupied = 1; + g->board[x][y].constant = 1; + g->board[x][y].c = COLOR_I; + } + } + } +} + diff --git a/src/test.h b/src/test.h new file mode 100644 index 0000000..7860bc3 --- /dev/null +++ b/src/test.h @@ -0,0 +1,8 @@ +#include "cetris.h" + +enum tests { + TSPIN, + TSPIN_NO_LINES +}; + +void apply_test_board(struct cetris_game* g, enum tests t); diff --git a/test.c b/test.c deleted file mode 100644 index fa7aaa8..0000000 --- a/test.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "test.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 = BOARD_VISABLE; y < BOARD_Y; y++) { - for (int x = 0; x < 10; x++) { - if ((*board)[y - BOARD_VISABLE][x]) { - g->board[x][y].occupied = 1; - g->board[x][y].constant = 1; - g->board[x][y].c = COLOR_I; - } - } - } -} - diff --git a/test.h b/test.h deleted file mode 100644 index 7860bc3..0000000 --- a/test.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "cetris.h" - -enum tests { - TSPIN, - TSPIN_NO_LINES -}; - -void apply_test_board(struct cetris_game* g, enum tests t); -- cgit v1.2.3-101-g0448