From 7c2779d53a69b4fa0a81d149002994e8c5051120 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Fri, 27 Sep 2019 18:35:22 -0400 Subject: single header and clang-format --- .clang-format | 127 ++++++++ core/cetris.c | 456 ----------------------------- core/cetris.h | 109 ------- core/matrix.c | 110 ------- core/matrix.h | 9 - core/meson.build | 12 - core/test.c | 72 ----- core/test.h | 8 - core/types.h | 16 - frontends/curses/meson.build | 2 +- frontends/gl/meson.build | 2 +- lib/cetris.h | 683 +++++++++++++++++++++++++++++++++++++++++++ lib/test.h | 75 +++++ meson.build | 3 +- 14 files changed, 888 insertions(+), 796 deletions(-) create mode 100644 .clang-format delete mode 100644 core/cetris.c delete mode 100644 core/cetris.h delete mode 100644 core/matrix.c delete mode 100644 core/matrix.h delete mode 100644 core/meson.build delete mode 100644 core/test.c delete mode 100644 core/test.h delete mode 100644 core/types.h create mode 100644 lib/cetris.h create mode 100644 lib/test.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..838938d --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/core/cetris.c b/core/cetris.c deleted file mode 100644 index 0ae1517..0000000 --- a/core/cetris.c +++ /dev/null @@ -1,456 +0,0 @@ -#include -#include -#include -#include -#include - -#include "cetris.h" -#include "types.h" -#include "matrix.h" - -#ifdef BUILD_TESTS -#include "test.h" -#endif - -/* PROTOTYPES */ - -static void next_piece(cetris_game* g); -static void update_board(cetris_game* g); -static void lock_current(cetris_game* g); -static void move_current(cetris_game* g, input_t move); -static void hard_drop(cetris_game* g); -static void rotate_piece(cetris_game* g, bool clockwise); -static void init_piece_queue(cetris_game* g); -static void shuffle_queue(cetris_game* g); -static void make_ghosts(cetris_game* g); -static void add_score(cetris_game* g, u8 lines); -static void reset_tetrimino(tetrimino* t); - -/* 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 -}; - -static const vec2 basic_movements[5] = { - {0, 0}, {0, 1}, {0, 1}, {1, 0}, {-1, 0} // NONE, DOWN, USER_DOWN, RIGHT, LEFT -}; - -// https://tetris.fandom.com/wiki/Tetris_Worlds -// TODO: Make this more accurate -static const u32 level_drop_delay[20] = { - 60, 48, 37, 28, 21, 16, 11, 8, 6, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1 -}; - -/* GAME FUNCTIONS */ - -void init_game(cetris_game* g) { - - /* check for config errors */ - assert(CETRIS_NEXT_PIECE_DELAY >= CETRIS_LINE_CLEAR_DELAY); - - srand(time(NULL)); - -#ifdef BUILD_TESTS - apply_test_board(g, TSPIN_NO_LINES); -#endif - - memset(g, 0, sizeof(cetris_game)); - - g->level = CETRIS_STARTING_LEVEL; - - init_piece_queue(g); - shuffle_queue(g); - - next_piece(g); -} - -void init_piece_queue(cetris_game* g) { - for (u8 i = 0; i < 7; i++) { - g->piece_queue[i].t = i; - g->piece_queue[i].c = i + 1; - 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].locked = false; - g->piece_queue[i].ghost_y = 0; - - /* Pieces should spawn so that on the first down - * tick the bottom row will show. Values here are adjusted - * for the default 4x4 matricies for each piece */ - g->piece_queue[i].pos.x = 3; - g->piece_queue[i].pos.y = (i == I) ? 17 : 16; - } -} - -void shuffle_queue(cetris_game* g) { - for (u8 i = 0; i < 7; i++) { - tetrimino t = g->piece_queue[i]; - u8 rand_index = rand() % 7; - g->piece_queue[i] = g->piece_queue[rand_index]; - g->piece_queue[rand_index] = t; - } -} - -void update_game_tick(cetris_game* g) { - if (g->game_over) return; - - g->tick++; - - if (g->next_piece_tick && g->tick >= g->next_piece_tick) { - next_piece(g); - } - - if (g->next_piece_tick) return; - - bool did_move = false; - if (g->tick >= g->next_drop_tick || !g->next_drop_tick) { - if (g->next_drop_tick) { - move_current(g, DOWN); - did_move = true; - } - - if (g->level <= 20) { - g->next_drop_tick = g->tick + level_drop_delay[g->level - 1]; - } else { - g->next_drop_tick = g->tick + level_drop_delay[19]; - } - } - - /* lock piece if it was hovering for CETRIS_LOCK_DELAY */ - if (!g->next_piece_tick && g->current.lock_tick && g->current.lock_tick <= g->tick) { - g->current.pos.y++; - if (check_matrix(g, &g->current.m) <= 0) { - lock_current(g); - did_move = true; - } - g->current.pos.y--; - g->current.lock_tick = 0; - } - - if (did_move) update_board(g); -} - -void next_piece(cetris_game* g) { - g->next_drop_tick = 0; - g->next_piece_tick = 0; - - g->current = g->piece_queue[g->current_index]; - if (check_matrix(g, &g->current.m) <= 0) { - g->game_over = true; - } - g->current_index++; - - if (!g->game_over) { - move_current(g, DOWN); - } - - if (g->current_index >= 7) { - g->current_index = 0; - shuffle_queue(g); - } - - update_board(g); -} - -void lock_current(cetris_game* g) { - g->current.locked = true; - for (u8 x = 0; x < CETRIS_BOARD_X; x++) { - for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { - if (g->board[x][y].occupied) g->board[x][y].constant = 1; - } - } - update_board(g); -} - -void make_ghosts(cetris_game* g) { - u8 orig_y = g->current.pos.y; - while (true) { - g->current.pos.y++; - if (check_matrix(g, &g->current.m) <= 0) { - g->current.ghost_y = g->current.pos.y - 1; - g->current.pos.y = orig_y; - break; - } - } -} - -void update_board(cetris_game* g) { - if (g->game_over) return; - - u8 lines_cleared = 0; - for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { - bool clear_line = true; - for (u8 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].occupied || g->board[0][y].remove_tick > 0) { - clear_line = false; - } - } - // remove tick only tracked on first block of line - if (g->board[0][y].remove_tick && g->board[0][y].remove_tick <= g->tick) { - for (s8 s = y - 1; s >= 0; s--) { - for (u8 x = 0; x < CETRIS_BOARD_X; x++) { - g->board[x][s + 1] = g->board[x][s]; - } - } - } - if (clear_line) { - g->board[0][y].remove_tick = g->tick + CETRIS_LINE_CLEAR_DELAY; - lines_cleared++; - } - } - - make_ghosts(g); - set_matrix(g, &g->current.m); - - assert(lines_cleared <= 4); - - if (g->current.locked && !g->next_piece_tick) { - if (lines_cleared > 0) { - g->next_piece_tick = g->tick + CETRIS_NEXT_PIECE_DELAY; - } else { - next_piece(g); - } - } - - if (lines_cleared > 0 || g->tspin || g->mini_tspin) { - add_score(g, lines_cleared); - if (lines_cleared > 0) { - g->lines += lines_cleared; - if (g->lines >= (g->level * 10)) g->level++; - } - } -} - -/* SCORE FUNCTIONS */ - -void add_score(cetris_game* g, u8 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 = false; - } 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 = false; - } -} - -/* MOVEMENT FUNCTIONS */ - -void move_piece(cetris_game* g, input_t move) { - switch (move) { - case LEFT: - case RIGHT: - case DOWN: - case USER_DOWN: - move_current(g, move); - break; - case HARD_DROP: - hard_drop(g); - break; - case ROTATE_CW: - rotate_piece(g, 1); - break; - case ROTATE_CCW: - rotate_piece(g, 0); - break; - } -} - -void reset_tetrimino(tetrimino* t) { - t->r = INIT; - t->pos.x = 3; - t->pos.y = (t->t == I) ? 17 : 16; - t->ghost_y = 0; -} - -void hold_piece(cetris_game* g) { - if (g->piece_held) { - tetrimino tmp = g->current; - g->current = g->held; - g->held = tmp; - } else { - g->held = g->current; - reset_tetrimino(&g->held); - g->piece_held = true; - next_piece(g); - } - update_board(g); -} - -void move_current(cetris_game* g, input_t move) { - if (g->game_over || g->next_piece_tick) return; - - g->current.pos.y += basic_movements[move].y; - g->current.pos.x += basic_movements[move].x; - - s8 check = check_matrix(g, &g->current.m); - if (check <= 0) { - g->current.pos.y -= basic_movements[move].y; - g->current.pos.x -= basic_movements[move].x; - - if (move == DOWN && check == -1 && !g->current.lock_tick) { - g->current.lock_tick = g->tick + CETRIS_LOCK_DELAY; - } - } else { - if (move == USER_DOWN) g->score++; - if ((move == DOWN) | (move == USER_DOWN)) - g->current.lock_tick = 0; - } - - update_board(g); -} - -void hard_drop(cetris_game* g) { - if (g->game_over || g->next_piece_tick) return; - - u8 drop_count = 0; - while (true) { - g->current.pos.y++; - drop_count++; - if (check_matrix(g, &g->current.m) <= 0) { - g->current.pos.y--; - drop_count--; - break; - } - } - - g->score += 2 * drop_count; // 2 score for each hard-drop'd cell - - update_board(g); - lock_current(g); -} - -void rotate_piece(cetris_game* g, bool clockwise) { - if (g->game_over || g->next_piece_tick) return; - if (g->current.t == O) return; - - rstate next = 0; - u8 wall_kick = 0; - 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)); - - rotate_matrix(g, &m, clockwise); - - vec2 kick; - bool set_current = false; - bool did_kick = false; - for (u8 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_matrix(g, &m) > 0) { - set_current = true; - if (i > 0) did_kick = true; - 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) { - bool did_tspin = true; - for (u8 i = 1; i < 5; i++) { - g->current.pos.x += basic_movements[i].x; - g->current.pos.y += basic_movements[i].y; - - if (check_matrix(g, &m) == 1) did_tspin = false; - - 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 = true; - else g->tspin = true; - } - } - - g->current.r = next; - memcpy(g->current.m, m, sizeof(piece_matrix)); - update_board(g); - } -} diff --git a/core/cetris.h b/core/cetris.h deleted file mode 100644 index 5c5a391..0000000 --- a/core/cetris.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -#include - -#include "types.h" - -#define CETRIS_BOARD_X 10 -#define CETRIS_BOARD_Y 40 -#define CETRIS_BOARD_VISABLE 20 - -#define CETRIS_HZ 60 -#define CETRIS_DAS_DELAY 11 -#define CETRIS_DAS_PERIOD 3 -#define CETRIS_DROP_PERIOD 2 -#define CETRIS_NEXT_PIECE_DELAY 40 -#define CETRIS_LINE_CLEAR_DELAY 40 -#define CETRIS_LOCK_DELAY 30 -#define CETRIS_WAIT_ON_CLEAR 0 - -#define CETRIS_STARTING_LEVEL 11 - -typedef enum { - O, I, S, Z, L, J, T -} type; - -typedef enum { - COLOR_NONE, - COLOR_O, // yellow - COLOR_I, // cyan - COLOR_S, // green - COLOR_Z, // red - COLOR_L, // orange - COLOR_J, // blue - COLOR_T // purple -} color; - -typedef enum { - INIT, - ONCE_RIGHT, - ONCE_LEFT, - TWICE -} rstate; - -typedef struct { - type t; - rstate r; - color c; - piece_matrix m; - s8 ghost_y; - vec2 pos; - u32 lock_tick; - bool locked; -} tetrimino; - -typedef struct { - bool occupied; - bool ghost; - bool constant; - u32 remove_tick; - color c; -} slot; - -typedef struct { - /* playfield represented by a 2d array */ - slot board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; - - /* constant queue of all 7 possible tetrimino */ - tetrimino piece_queue[7]; - - /* current tetrimino */ - tetrimino current; - tetrimino held; - bool piece_held; - u8 current_index; - - /* internal game tick */ - u32 tick; - u32 next_drop_tick; - u32 next_piece_tick; - u32 down_move_tick; - - /* progress trackers */ - u32 lines; - u32 level; - bool game_over; - - /* scoring flags */ - bool tspin; - bool mini_tspin; - - /* score counter */ - u32 score; -} cetris_game; - -typedef enum { - DOWN = 1, - USER_DOWN = 2, - RIGHT = 3, - LEFT = 4, - ROTATE_CCW = 5, - ROTATE_CW = 6, - HARD_DROP = 7 -} input_t; - -/* API FUNCTIONS */ -void init_game(cetris_game* g); -void update_game_tick(cetris_game* g); -void move_piece(cetris_game* g, input_t move); -void hold_piece(cetris_game* g); diff --git a/core/matrix.c b/core/matrix.c deleted file mode 100644 index ba32bfb..0000000 --- a/core/matrix.c +++ /dev/null @@ -1,110 +0,0 @@ -#include - -#include "types.h" -#include "cetris.h" -#include "matrix.h" - -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 } - } -}; - -s8 check_matrix(cetris_game* g, piece_matrix* m) { - for (s8 y = 0; y < 4; y++) { - for (s8 x = 0; x < 4; x++) { - vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; - if (r.y < 0) continue; - if ((*m)[y][x]) { - if (r.x >= CETRIS_BOARD_X || r.x < 0) return 0; - if (r.y >= CETRIS_BOARD_Y) return -1; - if (g->board[r.x][r.y].occupied && - g->board[r.x][r.y].constant) return -1; - } - } - } - return 1; -} - -void set_matrix(cetris_game* g, piece_matrix* m) { - for (s8 y = 0; y < 4; y++) { - for (s8 x = 0; x < 4; x++) { - if ((*m)[y][x]) { - vec2 r = (vec2){x + g->current.pos.x, - y + g->current.pos.y}; - if (r.y >= 0) { - if (!g->board[r.x][r.y].occupied) { - g->board[r.x][r.y].occupied = true; - g->board[r.x][r.y].c = g->current.c; - } - } - if (g->current.ghost_y + y >= 0) - if (r.y != (g->current.ghost_y + y)) - g->board[r.x][g->current.ghost_y + y].ghost = true; - } - } - } -} - -void rotate_matrix(cetris_game* g, piece_matrix* m, bool clockwise) { - for (u8 x = 0; x < 4; x++) { - for (u8 y = 0; y < 4; y++) { - if (g->current.m[y][x]) { - u8 new_x = (clockwise) ? 1 - (y - 2) : 1 + (y - 2); - u8 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; - } - } - } -} diff --git a/core/matrix.h b/core/matrix.h deleted file mode 100644 index 6994b36..0000000 --- a/core/matrix.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "types.h" -#include "cetris.h" - -extern piece_matrix default_matrices[7]; -s8 check_matrix(cetris_game* g, piece_matrix* m); -void set_matrix(cetris_game* g, piece_matrix* m); -void rotate_matrix(cetris_game* g, piece_matrix* m, bool clockwise); diff --git a/core/meson.build b/core/meson.build deleted file mode 100644 index ea8d15a..0000000 --- a/core/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -src = ['cetris.c', 'matrix.c'] - -if get_option('debug') == true - src += 'test.c' -endif - -cetris_lib = static_library('cetris', src, - include_directories: cetris_inc, - install: false) - -cetris = declare_dependency(include_directories: cetris_inc, - link_with: cetris_lib) diff --git a/core/test.c b/core/test.c deleted file mode 100644 index bad50d8..0000000 --- a/core/test.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "test.h" -#include "cetris.h" - -u8 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 } -}; - -u8 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(cetris_game* g, test t) { - u8 (*board)[20][10]; - switch (t) { - case TSPIN: - board = &tspin_board; - break; - case TSPIN_NO_LINES: - board = &tspin_no_lines_board; - break; - default: - return; - } - for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { - for (u8 x = 0; x < 10; x++) { - if ((*board)[y][x]) { - g->board[x][y].occupied = 1; - g->board[x][y].constant = 1; - g->board[x][y].c = COLOR_I; - } - } - } -} - diff --git a/core/test.h b/core/test.h deleted file mode 100644 index 4b8622e..0000000 --- a/core/test.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "cetris.h" - -typedef enum { - TSPIN, - TSPIN_NO_LINES -} test; - -void apply_test_board( cetris_game* g, test t); diff --git a/core/types.h b/core/types.h deleted file mode 100644 index f123da3..0000000 --- a/core/types.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#define u8 uint8_t -#define u32 uint32_t -#define u64 uint64_t -#define s8 int8_t -#define s32 int32_t - -typedef struct { - s8 x; - s8 y; -} vec2; - -typedef u8 piece_matrix[4][4]; diff --git a/frontends/curses/meson.build b/frontends/curses/meson.build index b254239..8adbd40 100644 --- a/frontends/curses/meson.build +++ b/frontends/curses/meson.build @@ -1,7 +1,7 @@ src = ['curses_ui.c'] inc = [cetris_inc] -deps = [cetris] +deps = [] if host_machine.system() == 'windows' inc += include_directories('win') diff --git a/frontends/gl/meson.build b/frontends/gl/meson.build index 9975a1b..a1787b2 100644 --- a/frontends/gl/meson.build +++ b/frontends/gl/meson.build @@ -1,7 +1,7 @@ src = ['main.c', 'glad/src/gl.c'] inc = [cetris_inc, include_directories('glad/include')] -deps = [cetris] +deps = [] if host_machine.system() == 'windows' deps += compiler.find_library('glfw3', dirs: meson.current_source_dir() + '/win/glfw') diff --git a/lib/cetris.h b/lib/cetris.h new file mode 100644 index 0000000..dc92d83 --- /dev/null +++ b/lib/cetris.h @@ -0,0 +1,683 @@ +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_TESTS +#include "test.h" +#endif + +#define u8 uint8_t +#define u32 uint32_t +#define u64 uint64_t +#define s8 int8_t +#define s32 int32_t + +#define CETRIS_EXPORT + +#define CETRIS_BOARD_X 10 +#define CETRIS_BOARD_Y 40 +#define CETRIS_BOARD_VISABLE 20 + +#define CETRIS_HZ 60 +#define CETRIS_DAS_DELAY 11 +#define CETRIS_DAS_PERIOD 3 +#define CETRIS_DROP_PERIOD 2 +#define CETRIS_NEXT_PIECE_DELAY 40 +#define CETRIS_LINE_CLEAR_DELAY 40 +#define CETRIS_LOCK_DELAY 30 +#define CETRIS_WAIT_ON_CLEAR 0 + +#define CETRIS_STARTING_LEVEL 1 + +typedef struct { + s8 x; + s8 y; +} vec2; + +typedef u8 piece_matrix[4][4]; + +typedef enum { O, I, S, Z, L, J, T } type; + +typedef enum { + COLOR_NONE, + COLOR_O, // yellow + COLOR_I, // cyan + COLOR_S, // green + COLOR_Z, // red + COLOR_L, // orange + COLOR_J, // blue + COLOR_T // purple +} color; + +typedef enum { INIT, ONCE_RIGHT, ONCE_LEFT, TWICE } rstate; + +typedef struct { + type t; + rstate r; + color c; + piece_matrix m; + s8 ghost_y; + vec2 pos; + u32 lock_tick; + bool locked; +} tetrimino; + +typedef struct { + bool occupied; + bool ghost; + bool constant; + u32 remove_tick; + color c; +} slot; + +typedef enum { + DOWN = 1, + USER_DOWN = 2, + RIGHT = 3, + LEFT = 4, + ROTATE_CCW = 5, + ROTATE_CW = 6, + HARD_DROP = 7 +} input_t; + +typedef struct { + /* playfield represented by a 2d array */ + slot board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; + + /* constant queue of all 7 possible tetrimino */ + tetrimino piece_queue[7]; + + /* current tetrimino */ + tetrimino current; + tetrimino held; + bool piece_held; + u8 current_index; + + /* internal game tick */ + u32 tick; + u32 next_drop_tick; + u32 next_piece_tick; + u32 down_move_tick; + + /* progress trackers */ + u32 lines; + u32 level; + bool game_over; + + /* scoring flags */ + bool tspin; + bool mini_tspin; + + /* score counter */ + u32 score; +} cetris_game; + +/* PROTOTYPES */ + +static void next_piece(cetris_game *g); +static void update_board(cetris_game *g); +static void lock_current(cetris_game *g); +static void move_current(cetris_game *g, input_t move); +static void hard_drop(cetris_game *g); +static void rotate_piece(cetris_game *g, bool clockwise); +static void init_piece_queue(cetris_game *g); +static void shuffle_queue(cetris_game *g); +static void make_ghosts(cetris_game *g); +static void add_score(cetris_game *g, u8 lines); +static void reset_tetrimino(tetrimino *t); +static s8 check_matrix(cetris_game *g, piece_matrix *m); +static void set_matrix(cetris_game *g, piece_matrix *m); +static void rotate_matrix(cetris_game *g, piece_matrix *m, bool clockwise); + +static 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}}}; + +/* 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 +}; + +static const vec2 basic_movements[5] = { + {0, 0}, + {0, 1}, + {0, 1}, + {1, 0}, + {-1, 0} // NONE, DOWN, USER_DOWN, RIGHT, LEFT +}; + +// https://tetris.fandom.com/wiki/Tetris_Worlds +// TODO: Make this more accurate +static const u32 level_drop_delay[20] = {60, 48, 37, 28, 21, 16, 11, 8, 6, 4, + 3, 2, 1, 1, 1, 1, 1, 1, 1, 1}; + +/* GAME FUNCTIONS */ + +CETRIS_EXPORT void init_game(cetris_game *g) { + + /* check for config errors */ + assert(CETRIS_NEXT_PIECE_DELAY >= CETRIS_LINE_CLEAR_DELAY); + + srand(time(NULL)); + +#ifdef BUILD_TESTS + apply_test_board(g, TSPIN_NO_LINES); +#endif + + memset(g, 0, sizeof(cetris_game)); + + g->level = CETRIS_STARTING_LEVEL; + + init_piece_queue(g); + shuffle_queue(g); + + next_piece(g); +} + +void init_piece_queue(cetris_game *g) { + for (u8 i = 0; i < 7; i++) { + g->piece_queue[i].t = i; + g->piece_queue[i].c = i + 1; + 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].locked = false; + g->piece_queue[i].ghost_y = 0; + + /* Pieces should spawn so that on the first down + * tick the bottom row will show. Values here are adjusted + * for the default 4x4 matricies for each piece */ + g->piece_queue[i].pos.x = 3; + g->piece_queue[i].pos.y = (i == I) ? 17 : 16; + } +} + +void shuffle_queue(cetris_game *g) { + for (u8 i = 0; i < 7; i++) { + tetrimino t = g->piece_queue[i]; + u8 rand_index = rand() % 7; + g->piece_queue[i] = g->piece_queue[rand_index]; + g->piece_queue[rand_index] = t; + } +} + +CETRIS_EXPORT void update_game_tick(cetris_game *g) { + if (g->game_over) + return; + + g->tick++; + + if (g->next_piece_tick && g->tick >= g->next_piece_tick) { + next_piece(g); + } + + if (g->next_piece_tick) + return; + + bool did_move = false; + if (g->tick >= g->next_drop_tick || !g->next_drop_tick) { + if (g->next_drop_tick) { + move_current(g, DOWN); + did_move = true; + } + + if (g->level <= 20) { + g->next_drop_tick = g->tick + level_drop_delay[g->level - 1]; + } else { + g->next_drop_tick = g->tick + level_drop_delay[19]; + } + } + + /* lock piece if it was hovering for CETRIS_LOCK_DELAY */ + if (!g->next_piece_tick && g->current.lock_tick && + g->current.lock_tick <= g->tick) { + g->current.pos.y++; + if (check_matrix(g, &g->current.m) <= 0) { + lock_current(g); + did_move = true; + } + g->current.pos.y--; + g->current.lock_tick = 0; + } + + if (did_move) + update_board(g); +} + +void next_piece(cetris_game *g) { + g->next_drop_tick = 0; + g->next_piece_tick = 0; + + g->current = g->piece_queue[g->current_index]; + if (check_matrix(g, &g->current.m) <= 0) { + g->game_over = true; + } + g->current_index++; + + if (!g->game_over) { + move_current(g, DOWN); + } + + if (g->current_index >= 7) { + g->current_index = 0; + shuffle_queue(g); + } + + update_board(g); +} + +void lock_current(cetris_game *g) { + g->current.locked = true; + for (u8 x = 0; x < CETRIS_BOARD_X; x++) { + for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { + if (g->board[x][y].occupied) + g->board[x][y].constant = 1; + } + } + update_board(g); +} + +void make_ghosts(cetris_game *g) { + u8 orig_y = g->current.pos.y; + while (true) { + g->current.pos.y++; + if (check_matrix(g, &g->current.m) <= 0) { + g->current.ghost_y = g->current.pos.y - 1; + g->current.pos.y = orig_y; + break; + } + } +} + +void update_board(cetris_game *g) { + if (g->game_over) + return; + + u8 lines_cleared = 0; + for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { + bool clear_line = true; + for (u8 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].occupied || g->board[0][y].remove_tick > 0) { + clear_line = false; + } + } + // remove tick only tracked on first block of line + if (g->board[0][y].remove_tick && g->board[0][y].remove_tick <= g->tick) { + for (s8 s = y - 1; s >= 0; s--) { + for (u8 x = 0; x < CETRIS_BOARD_X; x++) { + g->board[x][s + 1] = g->board[x][s]; + } + } + } + if (clear_line) { + g->board[0][y].remove_tick = g->tick + CETRIS_LINE_CLEAR_DELAY; + lines_cleared++; + } + } + + make_ghosts(g); + set_matrix(g, &g->current.m); + + assert(lines_cleared <= 4); + + if (g->current.locked && !g->next_piece_tick) { + if (lines_cleared > 0) { + g->next_piece_tick = g->tick + CETRIS_NEXT_PIECE_DELAY; + } else { + next_piece(g); + } + } + + if (lines_cleared > 0 || g->tspin || g->mini_tspin) { + add_score(g, lines_cleared); + if (lines_cleared > 0) { + g->lines += lines_cleared; + if (g->lines >= (g->level * 10)) + g->level++; + } + } +} + +/* SCORE FUNCTIONS */ + +// TODO: hard score +void add_score(cetris_game *g, u8 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 = false; + } 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 = false; + } +} + +/* MOVEMENT FUNCTIONS */ + +CETRIS_EXPORT void move_piece(cetris_game *g, input_t move) { + switch (move) { + case LEFT: + case RIGHT: + case DOWN: + case USER_DOWN: + move_current(g, move); + break; + case HARD_DROP: + hard_drop(g); + break; + case ROTATE_CW: + rotate_piece(g, 1); + break; + case ROTATE_CCW: + rotate_piece(g, 0); + break; + } +} + +void reset_tetrimino(tetrimino *t) { + t->r = INIT; + t->pos.x = 3; + t->pos.y = (t->t == I) ? 17 : 16; + t->ghost_y = 0; +} + +CETRIS_EXPORT void hold_piece(cetris_game *g) { + if (g->piece_held) { + tetrimino tmp = g->current; + g->current = g->held; + g->held = tmp; + } else { + g->held = g->current; + reset_tetrimino(&g->held); + g->piece_held = true; + next_piece(g); + } + update_board(g); +} + +void move_current(cetris_game *g, input_t move) { + if (g->game_over || g->next_piece_tick) + return; + + g->current.pos.y += basic_movements[move].y; + g->current.pos.x += basic_movements[move].x; + + s8 check = check_matrix(g, &g->current.m); + if (check <= 0) { + g->current.pos.y -= basic_movements[move].y; + g->current.pos.x -= basic_movements[move].x; + + if (move == DOWN && check == -1 && !g->current.lock_tick) { + g->current.lock_tick = g->tick + CETRIS_LOCK_DELAY; + } + } else { + if (move == USER_DOWN) + g->score++; + if ((move == DOWN) | (move == USER_DOWN)) + g->current.lock_tick = 0; + } + + update_board(g); +} + +void hard_drop(cetris_game *g) { + if (g->game_over || g->next_piece_tick) + return; + + u8 drop_count = 0; + while (true) { + g->current.pos.y++; + drop_count++; + if (check_matrix(g, &g->current.m) <= 0) { + g->current.pos.y--; + drop_count--; + break; + } + } + + g->score += 2 * drop_count; // 2 score for each hard-drop'd cell + + update_board(g); + lock_current(g); +} + +void rotate_piece(cetris_game *g, bool clockwise) { + if (g->game_over || g->next_piece_tick) + return; + if (g->current.t == O) + return; + + rstate next = 0; + u8 wall_kick = 0; + 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)); + + rotate_matrix(g, &m, clockwise); + + vec2 kick; + bool set_current = false; + bool did_kick = false; + for (u8 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_matrix(g, &m) > 0) { + set_current = true; + if (i > 0) + did_kick = true; + 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) { + bool did_tspin = true; + for (u8 i = 1; i < 5; i++) { + g->current.pos.x += basic_movements[i].x; + g->current.pos.y += basic_movements[i].y; + + if (check_matrix(g, &m) == 1) + did_tspin = false; + + 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 = true; + else + g->tspin = true; + } + } + + g->current.r = next; + memcpy(g->current.m, m, sizeof(piece_matrix)); + update_board(g); + } +} + +/* FUNCTIONS FOR MATRIX INTERATIONS */ + +s8 check_matrix(cetris_game *g, piece_matrix *m) { + for (s8 y = 0; y < 4; y++) { + for (s8 x = 0; x < 4; x++) { + vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; + if (r.y < 0) + continue; + if ((*m)[y][x]) { + if (r.x >= CETRIS_BOARD_X || r.x < 0) + return 0; + if (r.y >= CETRIS_BOARD_Y) + return -1; + if (g->board[r.x][r.y].occupied && g->board[r.x][r.y].constant) + return -1; + } + } + } + return 1; +} + +void set_matrix(cetris_game *g, piece_matrix *m) { + for (s8 y = 0; y < 4; y++) { + for (s8 x = 0; x < 4; x++) { + if ((*m)[y][x]) { + vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; + if (r.y >= 0) { + if (!g->board[r.x][r.y].occupied) { + g->board[r.x][r.y].occupied = true; + g->board[r.x][r.y].c = g->current.c; + } + } + if (g->current.ghost_y + y >= 0) + if (r.y != (g->current.ghost_y + y)) + g->board[r.x][g->current.ghost_y + y].ghost = true; + } + } + } +} + +void rotate_matrix(cetris_game *g, piece_matrix *m, bool clockwise) { + for (u8 x = 0; x < 4; x++) { + for (u8 y = 0; y < 4; y++) { + if (g->current.m[y][x]) { + u8 new_x = (clockwise) ? 1 - (y - 2) : 1 + (y - 2); + u8 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; + } + } + } +} diff --git a/lib/test.h b/lib/test.h new file mode 100644 index 0000000..3e41971 --- /dev/null +++ b/lib/test.h @@ -0,0 +1,75 @@ +#include "cetris.h" + +typedef enum { + TSPIN, + TSPIN_NO_LINES +} test; + +u8 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 } +}; + +u8 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(cetris_game* g, test t) { + u8 (*board)[20][10]; + switch (t) { + case TSPIN: + board = &tspin_board; + break; + case TSPIN_NO_LINES: + board = &tspin_no_lines_board; + break; + default: + return; + } + for (u8 y = 0; y < CETRIS_BOARD_Y; y++) { + for (u8 x = 0; x < 10; x++) { + if ((*board)[y][x]) { + g->board[x][y].occupied = 1; + g->board[x][y].constant = 1; + g->board[x][y].c = COLOR_I; + } + } + } +} diff --git a/meson.build b/meson.build index 12d9788..1140d1f 100644 --- a/meson.build +++ b/meson.build @@ -12,8 +12,7 @@ if get_option('debug') == true endif endif -cetris_inc = include_directories('core') -subdir('core') +cetris_inc = include_directories('lib') if get_option('curses-frontend') == true subdir('frontends/curses') -- cgit v1.2.3-101-g0448