From 7c737f4d1d21a8c666bc8b868f4f29cfe9a3cbab Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 4 Mar 2019 17:28:05 -0500 Subject: restructure project and add tspin checking --- cetris.c | 132 ++++++++++++++++++++++-------------------- cetris.h | 13 +++++ frontend/ncurses/ncurses_ui.c | 101 ++++++++++++++++++++++++++++++++ meson.build | 20 +++++-- meson_options.txt | 1 + ncurses_ui.c | 101 -------------------------------- test.c | 44 ++++++++++++++ test.h | 7 +++ 8 files changed, 250 insertions(+), 169 deletions(-) create mode 100644 frontend/ncurses/ncurses_ui.c create mode 100644 meson_options.txt delete mode 100644 ncurses_ui.c create mode 100644 test.c create mode 100644 test.h diff --git a/cetris.c b/cetris.c index fb3379d..b36efb0 100644 --- a/cetris.c +++ b/cetris.c @@ -7,6 +7,10 @@ #include "cetris.h" +#ifdef BUILD_TESTS +#include "test.h" +#endif + /* FUNCTION PROTOTYPES */ static void init_piece_queue(struct cetris_game* g); @@ -99,6 +103,10 @@ static const vec2 srs_wall_kicks_i[8][5] = { /* MATRIX MODIFICATION */ +static const vec2 cardinal_movements[4] = { + {0, 1}, {0, -1}, {1, 0}, {-1, 0} // DOWN, UP, RIGHT, LEFT +}; + void move_current(struct cetris_game* g, vec2 offset) { g->current.pos.y += offset.y; g->current.pos.x += offset.x; @@ -206,8 +214,9 @@ void rotate_matrix(struct cetris_game* g, int clockwise) { assert(0); } - int set_current = 0; vec2 kick; + int set_current = 0; + int did_kick = 0; for (int i = 0; i < 5; i++) { if (g->current.t == I) { kick = srs_wall_kicks_i[wall_kick][i]; @@ -218,6 +227,7 @@ void rotate_matrix(struct cetris_game* g, int clockwise) { 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; @@ -226,6 +236,25 @@ void rotate_matrix(struct cetris_game* g, int clockwise) { } if (set_current) { + /* check for tspin */ + if (g->current.t == T) { + int did_tspin = 1; + for (int i = 0; i < 4; i++) { + g->current.pos.x += cardinal_movements[i].x; + g->current.pos.y += cardinal_movements[i].y; + if (check_new_matrix(g, m) == 1) { + did_tspin = 0; + break; + } + g->current.pos.x -= cardinal_movements[i].x; + g->current.pos.y -= cardinal_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)); } @@ -264,39 +293,8 @@ void init_game(struct cetris_game* g) { memset(g->board, 0, sizeof(slot) * BOARD_X * BOARD_Y); -#if 1 - int tspinboard[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 } - }; - - for (int y = 0; y < 20; y++) { - for (int x = 0; x < 10; x++) { - if (tspinboard[y][x]) { - g->board[x][y].occupied = 1; - g->board[x][y].constant = 1; - } - } - } - +#ifdef BUILD_TESTS + apply_test_board(g, TSPIN); #endif g->tick = 0; @@ -307,6 +305,9 @@ void init_game(struct cetris_game* g) { g->lines = 0; g->level = 0; + g->tspin = 0; + g->mini_tspin = 0; + init_piece_queue(g); shuffle_queue(g); @@ -323,33 +324,33 @@ void init_piece_queue(struct cetris_game* g) { for (int i = 0; i < 7; i++) { switch (i) { case 0: - g->piece_queue[i].t = O; - g->piece_queue[i].c = COLOR_O; - break; + 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; + 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; + 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; + 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; + 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; + 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; + 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; @@ -367,8 +368,6 @@ void shuffle_queue(struct cetris_game* g) { } void update_game_tick(struct cetris_game* g) { - wipe_board(g); - if ((g->tick % CETRIS_HZ) == 0) move_down(g); enum movements current_move = g->move_queue[g->move_queue_pos]; @@ -388,16 +387,17 @@ void update_game_tick(struct cetris_game* g) { } if (!delay || g->tick % delay == 0) { + wipe_board(g); if (current_move > 0) { switch (current_move) { case DOWN: - move_current(g, (vec2){0, 1}); + move_current(g, cardinal_movements[0]); break; case LEFT: - move_current(g, (vec2){-1, 0}); + move_current(g, cardinal_movements[3]); break; case RIGHT: - move_current(g, (vec2){1, 0}); + move_current(g, cardinal_movements[2]); break; } if (g->move_queue_count != 0) g->move_queue_pos++; @@ -450,7 +450,7 @@ void wipe_board(struct cetris_game* g) { if (clear_line) { lines_cleared++; - + for (int x = 0; x < BOARD_X; x++) { g->board[x][y].remove_tick = g->tick + CETRIS_LINE_CLEAR_DELAY; } @@ -469,6 +469,12 @@ void wipe_board(struct cetris_game* g) { } assert(lines_cleared <= 4); - g->lines += lines_cleared; - if (g->lines >= (g->level * 10) && g->level <= 20) g->level++; + if (lines_cleared > 0) { + if (g->tspin || g->mini_tspin) { + g->tspin = 0; + g->mini_tspin = 0; + } + g->lines += lines_cleared; + if (g->lines >= (g->level * 10) && g->level <= 20) g->level++; + } } diff --git a/cetris.h b/cetris.h index a7dea7d..3ca728d 100644 --- a/cetris.h +++ b/cetris.h @@ -1,3 +1,5 @@ +#pragma once + #define BOARD_X 10 #define BOARD_Y 20 @@ -57,22 +59,33 @@ enum movements { }; 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; int current_index; + /* 20 action movment queue so das + * can be input independedent */ enum movements move_queue[20]; int move_queue_count; int move_queue_pos; + /* internal game tick */ int tick; + /* progress trackers */ int lines; int level; + /* scoring flags */ + int tspin; + int mini_tspin; + /* long int just incase */ long int score; }; diff --git a/frontend/ncurses/ncurses_ui.c b/frontend/ncurses/ncurses_ui.c new file mode 100644 index 0000000..1ae07b2 --- /dev/null +++ b/frontend/ncurses/ncurses_ui.c @@ -0,0 +1,101 @@ +#include +#include +#include + +#include "cetris.h" + +#define BLOCK "[]" +#define PLAY_FIELD_STR " /--------------------\\ /--------\\\n"\ + " | | | |\n"\ + " | | \\--------/\n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " | | \n"\ + " \\--------------------/" + +#define X_OFFSET 8 +#define Y_OFFSET 0 + +void curses_init() { + setlocale(LC_CTYPE, ""); + initscr(); + noecho(); + keypad(stdscr, TRUE); + curs_set(0); + timeout(1000 / CETRIS_HZ); + + start_color(); + init_pair(COLOR_NONE, COLOR_BLACK, COLOR_BLACK); + init_pair(COLOR_O, COLOR_MAGENTA, COLOR_BLACK); + init_pair(COLOR_Z, COLOR_RED, COLOR_BLACK); + init_pair(COLOR_S, COLOR_CYAN, COLOR_BLACK); + init_pair(COLOR_T, COLOR_WHITE, COLOR_BLACK); + init_pair(COLOR_L, COLOR_GREEN, COLOR_BLACK); + init_pair(COLOR_I, COLOR_BLUE, COLOR_BLACK); + init_pair(COLOR_J, COLOR_YELLOW, COLOR_BLACK); + clear(); +} + +void draw_board(struct cetris_game* g) { + mvaddstr(0, 0, PLAY_FIELD_STR); + for (int x = 0; x < BOARD_X; x++) { + for (int y = 0; y < BOARD_Y; y++) { + if (g->board[x][y].occupied) { + attron(COLOR_PAIR(g->board[x][y].c)); + if (g->board[x][y].remove_tick > 0) { + if (g->tick % 2 == 0) { + mvaddstr(y + 1, x * 2 + X_OFFSET, BLOCK); + } + } else { + mvaddstr(y + 1, x * 2 + X_OFFSET, BLOCK); + } + attroff(COLOR_PAIR(g->board[x][y].c)); + } + } + } +} + +int main(void) { + curses_init(); + + struct cetris_game game; + init_game(&game); + + int c; + while(1) { + c = getch(); + switch (c) { + case 'q': endwin(); exit(1); + case KEY_LEFT: + move_left(&game); break; + case KEY_RIGHT: + move_right(&game); break; + case KEY_DOWN: + move_down(&game); break; + case KEY_UP: + rotate_clockwise(&game); break; + case ' ': + move_hard_drop(&game); + } + update_game_tick(&game); + draw_board(&game); + refresh(); + } + return 0; +} + diff --git a/meson.build b/meson.build index c472081..18eab8e 100644 --- a/meson.build +++ b/meson.build @@ -4,12 +4,22 @@ project('cetris', 'c', compiler = meson.get_compiler('c') -if host_machine.system() == 'windows' - curses = compiler.find_library('pdcurses/pdcurses.a', dirs: meson.source_root()) -else - curses = dependency('ncursesw') +src = ['cetris.c'] + +if get_option('build_frontends') == true + if host_machine.system() == 'windows' + curses = compiler.find_library('pdcurses/pdcurses.a', dirs: meson.source_root()) + else + curses = dependency('ncursesw') + endif + src += ['frontend/ncurses/ncurses_ui.c'] +endif + +if get_option('debug') == true + src += ['test.c'] + add_global_arguments('-DBUILD_TESTS', language : 'c') endif -executable('cetris', ['cetris.c', 'ncurses_ui.c'], +executable('cetris', src, dependencies: curses, install : true) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..8636f78 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('build_frontends', type : 'boolean', value : true) diff --git a/ncurses_ui.c b/ncurses_ui.c deleted file mode 100644 index 1ae07b2..0000000 --- a/ncurses_ui.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include - -#include "cetris.h" - -#define BLOCK "[]" -#define PLAY_FIELD_STR " /--------------------\\ /--------\\\n"\ - " | | | |\n"\ - " | | \\--------/\n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " | | \n"\ - " \\--------------------/" - -#define X_OFFSET 8 -#define Y_OFFSET 0 - -void curses_init() { - setlocale(LC_CTYPE, ""); - initscr(); - noecho(); - keypad(stdscr, TRUE); - curs_set(0); - timeout(1000 / CETRIS_HZ); - - start_color(); - init_pair(COLOR_NONE, COLOR_BLACK, COLOR_BLACK); - init_pair(COLOR_O, COLOR_MAGENTA, COLOR_BLACK); - init_pair(COLOR_Z, COLOR_RED, COLOR_BLACK); - init_pair(COLOR_S, COLOR_CYAN, COLOR_BLACK); - init_pair(COLOR_T, COLOR_WHITE, COLOR_BLACK); - init_pair(COLOR_L, COLOR_GREEN, COLOR_BLACK); - init_pair(COLOR_I, COLOR_BLUE, COLOR_BLACK); - init_pair(COLOR_J, COLOR_YELLOW, COLOR_BLACK); - clear(); -} - -void draw_board(struct cetris_game* g) { - mvaddstr(0, 0, PLAY_FIELD_STR); - for (int x = 0; x < BOARD_X; x++) { - for (int y = 0; y < BOARD_Y; y++) { - if (g->board[x][y].occupied) { - attron(COLOR_PAIR(g->board[x][y].c)); - if (g->board[x][y].remove_tick > 0) { - if (g->tick % 2 == 0) { - mvaddstr(y + 1, x * 2 + X_OFFSET, BLOCK); - } - } else { - mvaddstr(y + 1, x * 2 + X_OFFSET, BLOCK); - } - attroff(COLOR_PAIR(g->board[x][y].c)); - } - } - } -} - -int main(void) { - curses_init(); - - struct cetris_game game; - init_game(&game); - - int c; - while(1) { - c = getch(); - switch (c) { - case 'q': endwin(); exit(1); - case KEY_LEFT: - move_left(&game); break; - case KEY_RIGHT: - move_right(&game); break; - case KEY_DOWN: - move_down(&game); break; - case KEY_UP: - rotate_clockwise(&game); break; - case ' ': - move_hard_drop(&game); - } - update_game_tick(&game); - draw_board(&game); - refresh(); - } - return 0; -} - diff --git a/test.c b/test.c new file mode 100644 index 0000000..c5465c6 --- /dev/null +++ b/test.c @@ -0,0 +1,44 @@ +#include "test.h" + +int tspin_test_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 } +}; + +void apply_test_board(struct cetris_game* g, enum tests t) { + int (*board)[20][10]; + switch (t) { + case TSPIN: + board = &tspin_test_board; + break; + default: + return; + } + for (int y = 0; y < 20; y++) { + for (int x = 0; x < 10; x++) { + if ((*board)[y][x]) { + g->board[x][y].occupied = 1; + g->board[x][y].constant = 1; + } + } + } +} + diff --git a/test.h b/test.h new file mode 100644 index 0000000..c4ed20e --- /dev/null +++ b/test.h @@ -0,0 +1,7 @@ +#include "cetris.h" + +enum tests { + TSPIN +}; + +void apply_test_board(struct cetris_game* g, enum tests t); -- cgit v1.2.3-101-g0448