Tetris Game in C Programming Language

Rate this post

Tetris Game in C Programming Language

A tetris game project in C language using NCURSES. It’s pretty feature complete.

tetris game
tetris game

Instructions

The controls are typical of Tetris:

  • left and right: Move the tetromino,
  • up: Rotate (clockwise?) the tetromino,
  • down: Immediately drop the tetromino (not a fast drop, an immediate drop),
  • q: Exit the game prematurely,
  • p: Pause the game (any key to resume),
  • b: “Boss mode” – show a mock terminal screen to fool nosy onlookers. Hit F1 to resume the game afterwards.
  • s: Save game and exit (just assumes filename tetris.save). To resume the game, run bin/release/main tetris.save (or whatever you may have renamed the game save to).

Dependencies 

  • libsdl and libsdl_mixer 1.2 for sound.
  • ncurses for terminal manipulation.

Installation of Tetris game

To install them on Arch Linux:

sudo pacman -S sdl_mixer ncurses

To install them on Ubuntu:

sudo apt-get install libsdl-mixer1.2-dev libncurses5-dev

To compile:

make

To run:

bin/release/main

 

Code for the Tertis game

For this project, we have 5 files.

  1. main.c :  Main project file for all the action in this game.
  2. tetris.c : Game logic written in this file.
  3. tetris.h : Variable declaration for the game
  4. util.c : Utilities for Tetris game
  5. util.h : Utility declarations for Tetris

 

Source code for main.c


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <ncurses.h>
#include <string.h>

#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>

#include “tetris.h”
#include “util.h”

/*
2 columns per cell makes the game much nicer.
*/
#define COLS_PER_CELL 2
/*
Macro to print a cell of a specific type to a window.
*/
#define ADD_BLOCK(w,x) waddch((w),’ ‘|A_REVERSE|COLOR_PAIR(x)); \
waddch((w),’ ‘|A_REVERSE|COLOR_PAIR(x))
#define ADD_EMPTY(w) waddch((w), ‘ ‘); waddch((w), ‘ ‘)

/*
Print the tetris board onto the ncurses window.
*/
void display_board(WINDOW *w, tetris_game *obj)
{
int i, j;
box(w, 0, 0);
for (i = 0; i < obj->rows; i++) {
wmove(w, 1 + i, 1);
for (j = 0; j < obj->cols; j++) {
if (TC_IS_FILLED(tg_get(obj, i, j))) {
ADD_BLOCK(w,tg_get(obj, i, j));
} else {
ADD_EMPTY(w);
}
}
}
wnoutrefresh(w);
}

/*
Display a tetris piece in a dedicated window.
*/
void display_piece(WINDOW *w, tetris_block block)
{
int b;
tetris_location c;
wclear(w);
box(w, 0, 0);
if (block.typ == -1) {
wnoutrefresh(w);
return;
}
for (b = 0; b < TETRIS; b++) {
c = TETROMINOS[block.typ][block.ori][b];
wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1);
ADD_BLOCK(w, TYPE_TO_CELL(block.typ));
}
wnoutrefresh(w);
}

/*
Display score information in a dedicated window.
*/
void display_score(WINDOW *w, tetris_game *tg)
{
wclear(w);
box(w, 0, 0);
wprintw(w, “Score\n%d\n”, tg->points);
wprintw(w, “Level\n%d\n”, tg->level);
wprintw(w, “Lines\n%d\n”, tg->lines_remaining);
wnoutrefresh(w);
}

/*
Boss mode! Make it look like you’re doing work.
*/
void boss_mode(void)
{
clear();
Mix_PauseMusic();
printw(“user@workstation-312:~/Documents/presentation $ ls -l\n”
“total 528\n”
“drwxr-xr-x 2 user users 4096 Jun 9 17:05 .\n”
“drwxr-xr-x 4 user users 4096 Jun 10 09:52 ..\n”
“-rw-r–r– 1 user users 88583 Jun 9 14:13 figure1.png\n”
“-rw-r–r– 1 user users 65357 Jun 9 15:40 figure2.png\n”
“-rw-r–r– 1 user users 4469 Jun 9 16:17 presentation.aux\n”
“-rw-r–r– 1 user users 42858 Jun 9 16:17 presentation.log\n”
“-rw-r–r– 1 user users 2516 Jun 9 16:17 presentation.nav\n”
“-rw-r–r– 1 user users 183 Jun 9 16:17 presentation.out\n”
“-rw-r–r– 1 user users 349607 Jun 9 16:17 presentation.pdf\n”
“-rw-r–r– 1 user users 0 Jun 9 16:17 presentation.snm\n”
“-rw-r–r– 1 user users 9284 Jun 9 17:05 presentation.tex\n”
“-rw-r–r– 1 user users 229 Jun 9 16:17 presentation.toc\n”
“\n”
“user@workstation-312:~/Documents/presentation $ “);
echo();
timeout(-1);
while (getch() != KEY_F(1));
timeout(0);
noecho();
clear();
Mix_ResumeMusic();
}

/*
Save and exit the game.
*/
void save(tetris_game *game, WINDOW *w)
{
FILE *f;

wclear(w);
box(w, 0, 0); // return the border
wmove(w, 1, 1);
wprintw(w, “Save and exit? [Y/n] “);
wrefresh(w);
timeout(-1);
if (getch() == ‘n’) {
timeout(0);
return;
}
f = fopen(“tetris.save”, “w”);
tg_save(game, f);
fclose(f);
tg_delete(game);
endwin();
printf(“Game saved to \”tetris.save\”.\n”);
printf(“Resume by passing the filename as an argument to this program.\n”);
exit(EXIT_SUCCESS);
}

/*
Do the NCURSES initialization steps for color blocks.
*/
void init_colors(void)
{
start_color();
//init_color(COLOR_ORANGE, 1000, 647, 0);
init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK);
init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK);
init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK);
init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK);
init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK);
init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK);
init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK);
}

/*
Main tetris game!
*/
int main(int argc, char **argv)
{
tetris_game *tg;
tetris_move move = TM_NONE;
bool running = true;
WINDOW *board, *next, *hold, *score;
Mix_Music *music;

// Load file if given a filename.
if (argc >= 2) {
FILE *f = fopen(argv[1], “r”);
if (f == NULL) {
perror(“tetris”);
exit(EXIT_FAILURE);
}
tg = tg_load(f);
fclose(f);
} else {
// Otherwise create new game.
tg = tg_create(22, 10);
}

// Initialize music.
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
fprintf(stderr, “unable to initialize SDL\n”);
exit(EXIT_FAILURE);
}
if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) {
fprintf(stderr, “unable to initialize SDL_mixer\n”);
exit(EXIT_FAILURE);
}
if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) != 0) {
fprintf(stderr, “unable to initialize audio\n”);
exit(EXIT_FAILURE);
}
Mix_AllocateChannels(1); // only need background music
music = Mix_LoadMUS(“tetris.mp3”);
if (music) {
Mix_PlayMusic(music, -1);
}

// NCURSES initialization:
initscr(); // initialize curses
cbreak(); // pass key presses to program, but not signals
noecho(); // don’t echo key presses to screen
keypad(stdscr, TRUE); // allow arrow keys
timeout(0); // no blocking on getch()
curs_set(0); // set the cursor to invisible
init_colors(); // setup tetris colors

// Create windows for each section of the interface.
board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0);
next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1);
hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1);
score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1);

// Game loop
while (running) {
running = tg_tick(tg, move);
display_board(board, tg);
display_piece(next, tg->next);
display_piece(hold, tg->stored);
display_score(score, tg);
doupdate();
sleep_milli(10);

switch (getch()) {
case KEY_LEFT:
move = TM_LEFT;
break;
case KEY_RIGHT:
move = TM_RIGHT;
break;
case KEY_UP:
move = TM_CLOCK;
break;
case KEY_DOWN:
move = TM_DROP;
break;
case ‘q’:
running = false;
move = TM_NONE;
break;
case ‘p’:
wclear(board);
box(board, 0, 0);
wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2);
wprintw(board, “PAUSED”);
wrefresh(board);
timeout(-1);
getch();
timeout(0);
move = TM_NONE;
break;
case ‘b’:
boss_mode();
move = TM_NONE;
break;
case ‘s’:
save(tg, board);
move = TM_NONE;
break;
case ‘ ‘:
move = TM_HOLD;
break;
default:
move = TM_NONE;
}
}

// Deinitialize NCurses
wclear(stdscr);
endwin();

// Deinitialize Sound
Mix_HaltMusic();
Mix_FreeMusic(music);
Mix_CloseAudio();
Mix_Quit();

// Output ending message.
printf(“Game over!\n”);
printf(“You finished with %d points on level %d.\n”, tg->points, tg->level);

// Deinitialize Tetris
tg_delete(tg);
return 0;
}


Source code for tetris.c

 

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>

#include “tetris.h”

#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))

/*******************************************************************************
Array Definitions
*******************************************************************************/

tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = {
// I
{{{1, 0}, {1, 1}, {1, 2}, {1, 3}},
{{0, 2}, {1, 2}, {2, 2}, {3, 2}},
{{3, 0}, {3, 1}, {3, 2}, {3, 3}},
{{0, 1}, {1, 1}, {2, 1}, {3, 1}}},
// J
{{{0, 0}, {1, 0}, {1, 1}, {1, 2}},
{{0, 1}, {0, 2}, {1, 1}, {2, 1}},
{{1, 0}, {1, 1}, {1, 2}, {2, 2}},
{{0, 1}, {1, 1}, {2, 0}, {2, 1}}},
// L
{{{0, 2}, {1, 0}, {1, 1}, {1, 2}},
{{0, 1}, {1, 1}, {2, 1}, {2, 2}},
{{1, 0}, {1, 1}, {1, 2}, {2, 0}},
{{0, 0}, {0, 1}, {1, 1}, {2, 1}}},
// O
{{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
{{0, 1}, {0, 2}, {1, 1}, {1, 2}}},
// S
{{{0, 1}, {0, 2}, {1, 0}, {1, 1}},
{{0, 1}, {1, 1}, {1, 2}, {2, 2}},
{{1, 1}, {1, 2}, {2, 0}, {2, 1}},
{{0, 0}, {1, 0}, {1, 1}, {2, 1}}},
// T
{{{0, 1}, {1, 0}, {1, 1}, {1, 2}},
{{0, 1}, {1, 1}, {1, 2}, {2, 1}},
{{1, 0}, {1, 1}, {1, 2}, {2, 1}},
{{0, 1}, {1, 0}, {1, 1}, {2, 1}}},
// Z
{{{0, 0}, {0, 1}, {1, 1}, {1, 2}},
{{0, 2}, {1, 1}, {1, 2}, {2, 1}},
{{1, 0}, {1, 1}, {2, 1}, {2, 2}},
{{0, 1}, {1, 0}, {1, 1}, {2, 0}}},
};

int GRAVITY_LEVEL[MAX_LEVEL+1] = {
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
50, 48, 46, 44, 42, 40, 38, 36, 34, 32,
//10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
30, 28, 26, 24, 22, 20, 16, 12, 8, 4
};

/*******************************************************************************
Helper Functions for Blocks
*******************************************************************************/

/*
Return the block at the given row and column.
*/
char tg_get(tetris_game *obj, int row, int column)
{
return obj->board[obj->cols * row + column];
}

/*
Set the block at the given row and column.
*/
static void tg_set(tetris_game *obj, int row, int column, char value)
{
obj->board[obj->cols * row + column] = value;
}

/*
Check whether a row and column are in bounds.
*/
bool tg_check(tetris_game *obj, int row, int col)
{
return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols;
}

/*
Place a block onto the board.
*/
static void tg_put(tetris_game *obj, tetris_block block)
{
int i;
for (i = 0; i < TETRIS; i++) {
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col,
TYPE_TO_CELL(block.typ));
}
}

/*
Clear a block out of the board.
*/
static void tg_remove(tetris_game *obj, tetris_block block)
{
int i;
for (i = 0; i < TETRIS; i++) {
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY);
}
}

/*
Check if a block can be placed on the board.
*/
static bool tg_fits(tetris_game *obj, tetris_block block)
{
int i, r, c;
for (i = 0; i < TETRIS; i++) {
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
r = block.loc.row + cell.row;
c = block.loc.col + cell.col;
if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) {
return false;
}
}
return true;
}

/*
Return a random tetromino type.
*/
static int random_tetromino(void) {
return rand() % NUM_TETROMINOS;
}

/*
Create a new falling block and populate the next falling block with a random
one.
*/
static void tg_new_falling(tetris_game *obj)
{
// Put in a new falling tetromino.
obj->falling = obj->next;
obj->next.typ = random_tetromino();
obj->next.ori = 0;
obj->next.loc.row = 0;
obj->next.loc.col = obj->cols/2 – 2;
}

/*******************************************************************************
Game Turn Helpers
*******************************************************************************/

/*
Tick gravity, and move the block down if gravity should act.
*/
static void tg_do_gravity_tick(tetris_game *obj)
{
obj->ticks_till_gravity–;
if (obj->ticks_till_gravity <= 0) {
tg_remove(obj, obj->falling);
obj->falling.loc.row++;
if (tg_fits(obj, obj->falling)) {
obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level];
} else {
obj->falling.loc.row–;
tg_put(obj, obj->falling);

tg_new_falling(obj);
}
tg_put(obj, obj->falling);
}
}

/*
Move the falling tetris block left (-1) or right (+1).
*/
static void tg_move(tetris_game *obj, int direction)
{
tg_remove(obj, obj->falling);
obj->falling.loc.col += direction;
if (!tg_fits(obj, obj->falling)) {
obj->falling.loc.col -= direction;
}
tg_put(obj, obj->falling);
}

/*
Send the falling tetris block to the bottom.
*/
static void tg_down(tetris_game *obj)
{
tg_remove(obj, obj->falling);
while (tg_fits(obj, obj->falling)) {
obj->falling.loc.row++;
}
obj->falling.loc.row–;
tg_put(obj, obj->falling);
tg_new_falling(obj);
}

/*
Rotate the falling block in either direction (+/-1).
*/
static void tg_rotate(tetris_game *obj, int direction)
{
tg_remove(obj, obj->falling);

while (true) {
obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS;

// If the new orientation fits, we’re done.
if (tg_fits(obj, obj->falling))
break;

// Otherwise, try moving left to make it fit.
obj->falling.loc.col–;
if (tg_fits(obj, obj->falling))
break;

// Finally, try moving right to make it fit.
obj->falling.loc.col += 2;
if (tg_fits(obj, obj->falling))
break;

// Put it back in its original location and try the next orientation.
obj->falling.loc.col–;
// Worst case, we come back to the original orientation and it fits, so this
// loop will terminate.
}

tg_put(obj, obj->falling);
}

/*
Swap the falling block with the block in the hold buffer.
*/
static void tg_hold(tetris_game *obj)
{
tg_remove(obj, obj->falling);
if (obj->stored.typ == -1) {
obj->stored = obj->falling;
tg_new_falling(obj);
} else {
int typ = obj->falling.typ, ori = obj->falling.ori;
obj->falling.typ = obj->stored.typ;
obj->falling.ori = obj->stored.ori;
obj->stored.typ = typ;
obj->stored.ori = ori;
while (!tg_fits(obj, obj->falling)) {
obj->falling.loc.row–;
}
}
tg_put(obj, obj->falling);
}

/*
Perform the action specified by the move.
*/
static void tg_handle_move(tetris_game *obj, tetris_move move)
{
switch (move) {
case TM_LEFT:
tg_move(obj, -1);
break;
case TM_RIGHT:
tg_move(obj, 1);
break;
case TM_DROP:
tg_down(obj);
break;
case TM_CLOCK:
tg_rotate(obj, 1);
break;
case TM_COUNTER:
tg_rotate(obj, -1);
break;
case TM_HOLD:
tg_hold(obj);
break;
default:
// pass
break;
}
}

/*
Return true if line i is full.
*/
static bool tg_line_full(tetris_game *obj, int i)
{
int j;
for (j = 0; j < obj->cols; j++) {
if (TC_IS_EMPTY(tg_get(obj, i, j)))
return false;
}
return true;
}

/*
Shift every row above r down one.
*/
static void tg_shift_lines(tetris_game *obj, int r)
{
int i, j;
for (i = r-1; i >= 0; i–) {
for (j = 0; j < obj->cols; j++) {
tg_set(obj, i+1, j, tg_get(obj, i, j));
tg_set(obj, i, j, TC_EMPTY);
}
}
}

/*
Find rows that are filled, remove them, shift, and return the number of
cleared rows.
*/
static int tg_check_lines(tetris_game *obj)
{
int i, nlines = 0;
tg_remove(obj, obj->falling); // don’t want to mess up falling block

for (i = obj->rows-1; i >= 0; i–) {
if (tg_line_full(obj, i)) {
tg_shift_lines(obj, i);
i++; // do this line over again since they’re shifted
nlines++;
}
}

tg_put(obj, obj->falling); // replace
return nlines;
}

/*
Adjust the score for the game, given how many lines were just cleared.
*/
static void tg_adjust_score(tetris_game *obj, int lines_cleared)
{
static int line_multiplier[] = {0, 40, 100, 300, 1200};
obj->points += line_multiplier[lines_cleared] * (obj->level + 1);
if (lines_cleared >= obj->lines_remaining) {
obj->level = MIN(MAX_LEVEL, obj->level + 1);
lines_cleared -= obj->lines_remaining;
obj->lines_remaining = LINES_PER_LEVEL – lines_cleared;
} else {
obj->lines_remaining -= lines_cleared;
}
}

/*
Return true if the game is over.
*/
static bool tg_game_over(tetris_game *obj)
{
int i, j;
bool over = false;
tg_remove(obj, obj->falling);
for (i = 0; i < 2; i++) {
for (j = 0; j < obj->cols; j++) {
if (TC_IS_FILLED(tg_get(obj, i, j))) {
over = true;
}
}
}
tg_put(obj, obj->falling);
return over;
}

/*******************************************************************************
Main Public Functions
*******************************************************************************/

/*
Do a single game tick: process gravity, user input, and score. Return true if
the game is still running, false if it is over.
*/
bool tg_tick(tetris_game *obj, tetris_move move)
{
int lines_cleared;
// Handle gravity.
tg_do_gravity_tick(obj);

// Handle input.
tg_handle_move(obj, move);

// Check for cleared lines
lines_cleared = tg_check_lines(obj);

tg_adjust_score(obj, lines_cleared);

// Return whether the game will continue (NOT whether it’s over)
return !tg_game_over(obj);
}

void tg_init(tetris_game *obj, int rows, int cols)
{
// Initialization logic
obj->rows = rows;
obj->cols = cols;
obj->board = malloc(rows * cols);
memset(obj->board, TC_EMPTY, rows * cols);
obj->points = 0;
obj->level = 0;
obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level];
obj->lines_remaining = LINES_PER_LEVEL;
srand(time(NULL));
tg_new_falling(obj);
tg_new_falling(obj);
obj->stored.typ = -1;
obj->stored.ori = 0;
obj->stored.loc.row = 0;
obj->next.loc.col = obj->cols/2 – 2;
printf(“%d”, obj->falling.loc.col);
}

tetris_game *tg_create(int rows, int cols)
{
tetris_game *obj = malloc(sizeof(tetris_game));
tg_init(obj, rows, cols);
return obj;
}

void tg_destroy(tetris_game *obj)
{
// Cleanup logic
free(obj->board);
}

void tg_delete(tetris_game *obj) {
tg_destroy(obj);
free(obj);
}

/*
Load a game from a file.
*/
tetris_game *tg_load(FILE *f)
{
tetris_game *obj = malloc(sizeof(tetris_game));
fread(obj, sizeof(tetris_game), 1, f);
obj->board = malloc(obj->rows * obj->cols);
fread(obj->board, sizeof(char), obj->rows * obj->cols, f);
return obj;
}

/*
Save a game to a file.
*/
void tg_save(tetris_game *obj, FILE *f)
{
fwrite(obj, sizeof(tetris_game), 1, f);
fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f);
}

/*
Print a game board to a file. Really just for early debugging.
*/
void tg_print(tetris_game *obj, FILE *f) {
int i, j;
for (i = 0; i < obj->rows; i++) {
for (j = 0; j < obj->cols; j++) {
if (TC_IS_EMPTY(tg_get(obj, i, j))) {
fputs(TC_EMPTY_STR, f);
} else {
fputs(TC_BLOCK_STR, f);
}
}
fputc(‘\n’, f);
}
}


Source code for tetris.h

#ifndef TETRIS_H
#define TETRIS_H

#include <stdio.h> // for FILE
#include <stdbool.h> // for bool

/*
Convert a tetromino type to its corresponding cell.
*/
#define TYPE_TO_CELL(x) ((x)+1)

/*
Strings for how you would print a tetris board.
*/
#define TC_EMPTY_STR ” ”
#define TC_BLOCK_STR “\u2588”

/*
Questions about a tetris cell.
*/
#define TC_IS_EMPTY(x) ((x) == TC_EMPTY)
#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x))

/*
How many cells in a tetromino?
*/
#define TETRIS 4
/*
How many tetrominos?
*/
#define NUM_TETROMINOS 7
/*
How many orientations of a tetromino?
*/
#define NUM_ORIENTATIONS 4

/*
Level constants.
*/
#define MAX_LEVEL 19
#define LINES_PER_LEVEL 10

/*
A “cell” is a 1×1 block within a tetris board.
*/
typedef enum {
TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ
} tetris_cell;

/*
A “type” is a type/shape of a tetromino. Not including orientation.
*/
typedef enum {
TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z
} tetris_type;

/*
A row,column pair. Negative numbers allowed, because we need them for
offsets.
*/
typedef struct {
int row;
int col;
} tetris_location;

/*
A “block” is a struct that contains information about a tetromino.
Specifically, what type it is, what orientation it has, and where it is.
*/
typedef struct {
int typ;
int ori;
tetris_location loc;
} tetris_block;

/*
All possible moves to give as input to the game.
*/
typedef enum {
TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE
} tetris_move;

/*
A game object!
*/
typedef struct {
/*
Game board stuff:
*/
int rows;
int cols;
char *board;
/*
Scoring information:
*/
int points;
int level;
/*
Falling block is the one currently going down. Next block is the one that
will be falling after this one. Stored is the block that you can swap out.
*/
tetris_block falling;
tetris_block next;
tetris_block stored;
/*
Number of game ticks until the block will move down.
*/
int ticks_till_gravity;
/*
Number of lines until you advance to the next level.
*/
int lines_remaining;
} tetris_game;

/*
This array stores all necessary information about the cells that are filled by
each tetromino. The first index is the type of the tetromino (i.e. shape,
e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final
array contains 4 tetris_location objects, each mapping to an offset from a
point on the upper left that is the tetromino “origin”.
*/
extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS];

/*
This array tells you how many ticks per gravity by level. Decreases as level
increases, to add difficulty.
*/
extern int GRAVITY_LEVEL[MAX_LEVEL+1];

// Data structure manipulation.
void tg_init(tetris_game *obj, int rows, int cols);
tetris_game *tg_create(int rows, int cols);
void tg_destroy(tetris_game *obj);
void tg_delete(tetris_game *obj);
tetris_game *tg_load(FILE *f);
void tg_save(tetris_game *obj, FILE *f);

// Public methods not related to memory:
char tg_get(tetris_game *obj, int row, int col);
bool tg_check(tetris_game *obj, int row, int col);
bool tg_tick(tetris_game *obj, tetris_move move);
void tg_print(tetris_game *obj, FILE *f);

#endif // TETRIS_H

 

Reference : https://github.com/brenns10/tetris/tree/master/src

For any query or issue, feel free to discuss on http://discuss.eduguru.in

Leave a Reply