jl777
5 years ago
6 changed files with 2046 additions and 4385 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,758 @@ |
|||
|
|||
#include "tetris.h" |
|||
#include "dapps/dappstd.c" |
|||
|
|||
|
|||
/***************************************************************************/ |
|||
/** https://github.com/brenns10/tetris
|
|||
@file main.c |
|||
@author Stephen Brennan |
|||
@date Created Wednesday, 10 June 2015 |
|||
@brief Main program for tetris. |
|||
@copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised |
|||
BSD License. See LICENSE.txt for details. |
|||
*******************************************************************************/ |
|||
|
|||
|
|||
#include <stdio.h> // for FILE |
|||
#include <stdbool.h> // for bool |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <stdbool.h> |
|||
#include <string.h> |
|||
#include <time.h> |
|||
#include <string.h> |
|||
|
|||
#ifdef BUILD_GAMESCC |
|||
#include "rogue/cursesd.h" |
|||
#else |
|||
#include <curses.h> |
|||
#endif |
|||
|
|||
|
|||
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) |
|||
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) |
|||
|
|||
/*******************************************************************************
|
|||
Array Definitions |
|||
*******************************************************************************/ |
|||
|
|||
const 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}}}, |
|||
}; |
|||
|
|||
const 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 |
|||
*******************************************************************************/ |
|||
|
|||
void sleep_milli(int milliseconds) |
|||
{ |
|||
struct timespec ts; |
|||
ts.tv_sec = 0; |
|||
ts.tv_nsec = milliseconds * 1000 * 1000; |
|||
nanosleep(&ts, NULL); |
|||
} |
|||
|
|||
/*
|
|||
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; |
|||
} |
|||
|
|||
/*
|
|||
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 = (char *)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 = (tetris_game *)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 = (tetris_game *)malloc(sizeof(tetris_game)); |
|||
if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) |
|||
{ |
|||
fprintf(stderr,"read game error\n"); |
|||
free(obj); |
|||
obj = 0; |
|||
} |
|||
else |
|||
{ |
|||
obj->board = (char *)malloc(obj->rows * obj->cols); |
|||
if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) |
|||
{ |
|||
fprintf(stderr,"fread error\n"); |
|||
free(obj->board); |
|||
free(obj); |
|||
obj = 0; |
|||
} |
|||
} |
|||
return obj; |
|||
} |
|||
|
|||
/*
|
|||
Save a game to a file. |
|||
*/ |
|||
void tg_save(tetris_game *obj, FILE *f) |
|||
{ |
|||
if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) |
|||
fprintf(stderr,"error writing tetrisgame\n"); |
|||
else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) |
|||
fprintf(stderr,"error writing board\n"); |
|||
} |
|||
|
|||
/*
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
/*
|
|||
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, (char *)"Score\n%d\n", tg->points); |
|||
wprintw(w, (char *)"Level\n%d\n", tg->level); |
|||
wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); |
|||
wnoutrefresh(w); |
|||
} |
|||
|
|||
/*
|
|||
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, (char *)"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(); |
|||
fprintf(stderr,"Game saved to \"tetris.save\".\n"); |
|||
fprintf(stderr,"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! |
|||
*/ |
|||
#ifdef STANDALONE |
|||
char *clonestr(char *str) |
|||
{ |
|||
char *clone; int32_t len; |
|||
if ( str == 0 || str[0] == 0 ) |
|||
{ |
|||
printf("warning cloning nullstr.%p\n",str); |
|||
#ifdef __APPLE__ |
|||
while ( 1 ) sleep(1); |
|||
#endif |
|||
str = (char *)"<nullstr>"; |
|||
} |
|||
len = strlen(str); |
|||
clone = (char *)calloc(1,len+16); |
|||
strcpy(clone,str); |
|||
return(clone); |
|||
} |
|||
|
|||
int tetris(int argc, char **argv) |
|||
{ |
|||
tetris_game *tg; |
|||
tetris_move move = TM_NONE; |
|||
bool running = true; |
|||
WINDOW *board, *next, *hold, *score; |
|||
int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; |
|||
memset(&gametxid,0,sizeof(gametxid)); |
|||
// 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); |
|||
} |
|||
// 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); |
|||
int32_t counter = 0; |
|||
// 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); |
|||
if ( (counter++ % 5) == 0 ) |
|||
doupdate(); |
|||
sleep_milli(10); |
|||
c = getch(); |
|||
if ( c != -1 || skipcount == 0x3fff ) |
|||
{ |
|||
if ( skipcount > 0 ) |
|||
issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); |
|||
if ( c != -1 ) |
|||
issue_games_events(gametxid,eventid,c); |
|||
skipcount = 0; |
|||
} else skipcount++; |
|||
eventid++; |
|||
switch ( c ) |
|||
{ |
|||
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 's': |
|||
save(tg, board); |
|||
move = TM_NONE; |
|||
break; |
|||
case ' ': |
|||
move = TM_HOLD; |
|||
break; |
|||
default: |
|||
move = TM_NONE; |
|||
} |
|||
} |
|||
|
|||
// Deinitialize NCurses
|
|||
wclear(stdscr); |
|||
endwin(); |
|||
// 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; |
|||
} |
|||
|
|||
int32_t games_replay(uint64_t seed,int32_t sleeptime) |
|||
{ |
|||
return(-1); |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,197 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef H_TETRIS_H |
|||
#define H_TETRIS_H |
|||
|
|||
#define GAMENAME "tetris" |
|||
#define GAMEMAIN tetris |
|||
#define CHAINNAME "GTEST" |
|||
|
|||
#define MAXPACK 23 |
|||
struct games_packitem |
|||
{ |
|||
int32_t type,launch,count,which,hplus,dplus,arm,flags,group; |
|||
char damage[8],hurldmg[8]; |
|||
}; |
|||
|
|||
struct games_player |
|||
{ |
|||
int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; |
|||
struct games_packitem gamespack[MAXPACK]; |
|||
}; |
|||
|
|||
struct games_state |
|||
{ |
|||
uint64_t seed; |
|||
char *keystrokes,*keystrokeshex; |
|||
uint32_t needflush,replaydone; |
|||
int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; |
|||
FILE *logfp; |
|||
struct games_player P; |
|||
char buffered[10000]; |
|||
uint8_t playerdata[10000]; |
|||
}; |
|||
|
|||
|
|||
/***************************************************************************/ |
|||
/** https://github.com/brenns10/tetris
|
|||
@file main.c |
|||
@author Stephen Brennan |
|||
@date Created Wednesday, 10 June 2015 |
|||
@brief Main program for tetris. |
|||
@copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised |
|||
BSD License. See LICENSE.txt for details. |
|||
*******************************************************************************/ |
|||
|
|||
/*
|
|||
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 1x1 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 |
|||
|
Loading…
Reference in new issue