Skip to content

Commit

Permalink
Merge pull request thecardkid#28 from thecardkid/hn/ghosting
Browse files Browse the repository at this point in the history
ADD: ghost piece that shows where tetromino will end up
  • Loading branch information
Hieu Nguyen authored Apr 14, 2017
2 parents 929e809 + 50937f8 commit 4cc487b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/block_factory.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void spawn(State* s) {
n = rand() % NUM_BLOCKS;
}
s->next = n;
project_ghost(s);
} else {
s->mode = SHUTDOWN;
}
Expand Down
30 changes: 14 additions & 16 deletions src/controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,15 @@ void run_mode(Movement* net_move, State* s, int* frame_counter) {
// Collect desired total movement in this frame
aggregate_movement(net_move, s, frame_counter);

// Clear interactables such as the block and ghost
clear_block(s);
clear_ghost(s);

// Perform movement
if (!move_block(s, net_move)) {
// Draw block before we reassign its pointer
draw_block(s);

// Block was unable to make any valid downwards move.

// Perform row clear if needed and update score
Expand All @@ -50,6 +57,10 @@ void run_mode(Movement* net_move, State* s, int* frame_counter) {
spawn(s);
}

// Draw block and ghost to grid
draw_ghost(s);
draw_block(s);

// Rendering loop
clear();
render(s);
Expand Down Expand Up @@ -212,14 +223,6 @@ void act_on_user_input(
}

int move_block(State* s, Movement* m) {
// Clear cells occupied by the block
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->y;

s->grid[x][y] = EMPTY;
}

// Flags
int applied_move = 1;
int can_move_vert = 1;
Expand All @@ -238,7 +241,7 @@ int move_block(State* s, Movement* m) {
int y = s->block->cells[i][1] + s->block->y;

// Conditions where move is invalid
if (!in_grid(x, y + m->y) || s->grid[x][y + m->y] != EMPTY) {
if (!in_grid(x, y + m->y) || ((s->grid[x][y + m->y] != EMPTY) && (s->grid[x][y + m->y] != GHOST))) {
can_move_vert = 0;
break;
}
Expand Down Expand Up @@ -304,13 +307,8 @@ int move_block(State* s, Movement* m) {
}
}

// Set cells occupied by block to correct color
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->y;

s->grid[x][y] = s->block->color;
}
// Recalculate ghost position
project_ghost(s);

return can_move_vert;
}
Expand Down
9 changes: 8 additions & 1 deletion src/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ void set_up_screen() {
init_pair(GREEN, COLOR_GREEN, COLOR_BLACK);
init_pair(PURPLE, COLOR_MAGENTA, COLOR_BLACK);
init_pair(RED, COLOR_RED, COLOR_BLACK);
init_pair(GHOST, COLOR_BLACK, COLOR_WHITE);
}

void display_grid(int grid[GRID_W][GRID_H]) {
Expand All @@ -26,7 +27,13 @@ void display_grid(int grid[GRID_W][GRID_H]) {
for (int x = 0; x < GRID_W; x++) {
if ((block = grid[x][y]) != EMPTY) {
attron(COLOR_PAIR(block));
printw("@");

if (block == GHOST) {
printw(" ");
} else {
printw("@");
}

attroff(COLOR_PAIR(block));
} else {
printw(" ");
Expand Down
16 changes: 0 additions & 16 deletions src/scorer.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,6 @@ int is_empty(int grid[GRID_W][GRID_H]) {
return 1;
}

void compute_heights(State* s) {
int h;

// recompute row heights
for (int c=0; c<GRID_W; c++) {
h = 0;
for (int r=GRID_H-1; r>=0; r--) {
if (s->grid[c][r] != EMPTY) {
h = GRID_H - r;
}
}
s->heights[c] = h;
}
}

void score_block(State* s) {
s->score += 10 * (s->level);
}
Expand Down Expand Up @@ -110,6 +95,5 @@ void clear_rows(State* s) {
void update_score(State* s) {
score_block(s);
clear_rows(s);
compute_heights(s);
}

64 changes: 64 additions & 0 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,70 @@ int in_grid(int x, int y) {
return 1;
}

void clear_block(State* s) {
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->y;

s->grid[x][y] = EMPTY;
}
}

void clear_ghost(State* s) {
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->ghosty;

s->grid[x][y] = EMPTY;
}
}

void draw_block(State* s) {
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->y;

s->grid[x][y] = s->block->color;
}
}

void draw_ghost(State* s) {
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + s->block->ghosty;

s->grid[x][y] = GHOST;
}
}

void project_ghost(State* s) {
int ghost_y = s->block->y;

if (ghost_y > GRID_H-OFFSET) ghost_y = GRID_H-OFFSET;

// It can be assumed that the ghost fits in the starting position since
// it starts in the same place as the block.
while (ghost_y < GRID_H) {
int can_move_down = 1;
for (int i = 0; i < 4; i++) {
int x = s->block->cells[i][0] + s->block->x;
int y = s->block->cells[i][1] + ghost_y;

if(!in_grid(x, y+1) || s->grid[x][y+1] != EMPTY) {
can_move_down = 0;
}
}

if (can_move_down) {
ghost_y++;
} else {
break;
}
}

s->block->ghosty = ghost_y;
}

void increment_with_max(int* num, int max) {
(*num)++;
if (*num > max) {
Expand Down
80 changes: 55 additions & 25 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,12 @@ extern const int L_Block[4][2];
extern const int rotation_matrix_R[2][2];
extern const int rotation_matrix_L[2][2];

/*
* Checks whether coordinate is inside playable area
*/
int in_grid(int x, int y);

/*
* Increment an integer by one without surpassing a specified maximum
*
* @param num: the integer to increment
* @param max: the maximum value that the integer can be incremented to
*/
void increment_with_max(int* num, int max);

/*
* Decrement an integer by one without surpassing a specified minimum
*
* @param num: the integer to deccrement
* @param max: the minimum value that the integer can be deccremented to
*/
void decrement_with_min(int* num, int min);

/*
* Taken from https://i.stack.imgur.com/JLRFu.png
*
* Not defined as enum class to allow implicit casting to int
*/
typedef enum {EMPTY, CYAN, BLUE, WHITE, YELLOW, GREEN, PURPLE, RED} BlockColor;
typedef enum {EMPTY, CYAN, BLUE, WHITE, YELLOW, GREEN, PURPLE, RED, GHOST} BlockColor;

/*
* Taken from http://tetris.wikia.com/wiki/Tetromino
Expand All @@ -84,15 +63,14 @@ typedef enum {RUNNING, PAUSED, CONFIRM_QUIT, SHUTDOWN, BOSS} Gamemode;
*/
typedef struct {
int cells[4][2];
int x;
int y;
int x, y;
int ghosty;
BlockColor color;
BlockType type;
} Block;

typedef struct {
int grid[GRID_W][GRID_H];
int heights[GRID_W];
int score;
int level;
Gamemode mode; // 1: Game in progress 0: Game over
Expand All @@ -110,4 +88,56 @@ typedef struct {
Rotation r;
} Movement;

/*
* Checks whether coordinate is inside playable area
*/
int in_grid(int x, int y);

/*
* Set all cells occupied by playable block to EMPTY
*/
void clear_block(State* s);

/*
* Set all cells occupied by playable block to the block's color
*/
void draw_block(State* s);

/*
* Set all cells occupied by playable block's ghost to EMPTY
* Note: this implementation allows for overlap between playable block and
* its ghost, so this method may clear some cells of the playable block.
*/
void clear_ghost(State* s);

/*
* Set all cells occupied by the playable block's ghost to the GHOST color.
* Note: since the ghost and playable block may overlap, the order in which
* their draw calls are made will determine which renders "ontop" of the other.
*/
void draw_ghost(State* s);

/*
* Calculate and assign correct position for the ghost of the playable block.
* The correct position of the ghost is always the furthest down the block may
* travel before hitting any other block.
*/
void project_ghost(State* s);

/*
* Increment an integer by one without surpassing a specified maximum
*
* @param num: the integer to increment
* @param max: the maximum value that the integer can be incremented to
*/
void increment_with_max(int* num, int max);

/*
* Decrement an integer by one without surpassing a specified minimum
*
* @param num: the integer to deccrement
* @param max: the minimum value that the integer can be deccremented to
*/
void decrement_with_min(int* num, int min);

#endif

0 comments on commit 4cc487b

Please sign in to comment.