Assignment 1
Extra information
- game.c: It includes an additional state named GAME_OVER. This is where you should perform operations like reporting the score or clearning the screen. Additionally, the game() now take a highscore structure as input and returns an updated structure.
- main.c: Setup to accept a command line argument for the highscore file. This functionality is not part of milestone 3 and is commented out. However, it is available as an extra credit opportunity in milestone 4.
- tetris.h: Changed the default well width and height. Tetris is intended to be played on a well that is 6-7 spaces wide and 12-18 spaces deep. With the previous well size, it takes a very long time to complete a row.
- well.c[h]: New functionality has been added to remove completed rows from the well. This function is called prune_well. It returns the number of completed lines.
- score.c[h]: New functionality to convert the number of completed lines into a score. It also handles the case where multiple 4 line clears (called a tetris) occur.
- highscore.c[h]: New functionality used to read and write previous high scores to a file. When the previous high scores are read from the file, a linked list is generated to store the data. If the current players score qualifies, then it is inserted into the linked list and saved back into the highcscore file.
- Makefile: Added new files to the build and added an all target. This allows the project to be cleaned and built in one step.
- tetromino.c[h], key.c[h]: Unchanged
- after the current tetromino has hit the bottom
- before a new tetromino is created
- Remove completed lines from the well.
- Compute and display the running score.
- Display the timer for the current game and maintain a timed game.
- End the game when a tetromino will immediately collide if it is added to the well.
#include
#include
#include
#include “highscore.h”
#include “game.h”
#include “well.h”
#include “tetris.h”
#include “tetromino.h”
#include “key.h”
void init_game(void) {
int x,y;
}
highscore_t *game(highscore_t *highscores) {
static int state = INIT;
tetromino_t *next = NULL;
tetromino_t *current = NULL;
well_t *w;
int x,y;
int c;
int arrow;
struct timespec tim = {0,1000000};
struct timespec tim_ret;
int move_counter = 0;
int move_timeout = 500;
int status;
int counter = 0;
int lines_cleared = 0;
int score = 0;
char str[80];
while(1) {
switch(state) {
case INIT: // Initialize the game, only run one time
initscr();
nodelay(stdscr,TRUE); // Do not wait for characters using getch.
noecho(); // Do not echo input characters
getmaxyx(stdscr,y,x); // Get the screen dimensions
w = init_well(((x/2)-(WELL_WIDTH/2)),3,WELL_WIDTH,WELL_HEIGHT);
draw_well(w);
srand(time(NULL)); // Seed the random number generator with the time. Used in create tet.
display_score(score, w->upper_left_x-15,w->upper_left_y);
state = ADD_PIECE;
break;
case ADD_PIECE: // Add a new piece to the game
if (next) {
current = next;
next = create_tetromino ((w->upper_left_x+(w->width/2)), w->upper_left_y);
}
else {
current = create_tetromino ((w->upper_left_x+(w->width/2)), w->upper_left_y);
next = create_tetromino ((w->upper_left_x+(w->width/2)), w->upper_left_y);
}
display_tetromino(current);
state = MOVE_PIECE;
break;
case MOVE_PIECE: // Move the current piece
if ((arrow = read_escape(&c)) != NOCHAR) {
if (arrow == UP) {
undisplay_tetromino(current);
rotate_ccw(current);
display_tetromino(current);
}
else if (arrow == DOWN) {
undisplay_tetromino(current);
rotate_cw(current);
display_tetromino(current);
}
else if (arrow == LEFT) {
undisplay_tetromino(current);
move_tet(current,current->upper_left_x-1,current->upper_left_y);
display_tetromino(current);
}
else if (arrow == RIGHT) {
undisplay_tetromino(current);
move_tet(current,current->upper_left_x+1,current->upper_left_y);
display_tetromino(current);
}
else if (arrow == REGCHAR) {
if (c == ‘ ‘) {
move_timeout = DROP_RATE;
}
if (c == ‘q’) {
state = GAME_OVER;
}
}
}
if (move_counter++ >= move_timeout) {
counter++;
undisplay_tetromino(current);
status = move_tet(current,current->upper_left_x,current->upper_left_y+1);
display_tetromino(current);
if (status == MOVE_FAILED) {
state = ADD_PIECE;
move_timeout = BASE_FALL_RATE;
}
move_counter = 0;
}
break;
case GAME_OVER:
nodelay(stdscr,FALSE);
clear();
getmaxyx(stdscr,y,x);
mvprintw(1,x/2-5,” GAME_OVER “);
mvprintw(2,x/2-5,”#############”);
mvprintw(16,x/2-5,”Hit q to exit”);
getch(); // Wait for a key to be pressed.
state = EXIT;
break;
case EXIT:
return(highscores); // Return the highscore structure back to main to be stored on disk.
break;
}
refresh();
nanosleep(&tim,&tim_ret);
}
}
highscore.c
#include
#include
#include
#include
#include “highscore.h”
highscore_t *parse_line(char *line) {
highscore_t *sptr;
char *p;
char *p_end;
const char delim = ‘,’;
enum {NAME, SCORE, EXIT};
int state = NAME;
// Make sure that the line ptr is not NULL
if (!line) {
return (NULL);
}
// Make Space for a new structure
sptr = malloc(sizeof(highscore_t));
// Assumnig that the new space is available, break the line down into parts and load the struct.
if (sptr) {
p = strtok(line,&delim); // initialize strtok to break line into tokens delimited by ‘,’
while (p) {
switch (state) {
case NAME:
if (strlen(p)
strcpy(sptr->initials,p);
state = SCORE;
}
else {
return (NULL);
}
break;
case SCORE:
errno = 0;
sptr->score = strtol(p,&p_end,10);
if (errno) {
return (NULL);
}
return (sptr);
break;
default:
return(NULL);
}
p = strtok(NULL,&delim); // Read the next token
}
}
return (NULL);
}
highscore_t *load_scores(char *filename) {
char linebuf[MAX_LINE];
FILE *fp;
char *lp;
int status;
highscore_t *current = NULL;
highscore_t *next = NULL;
highscore_t *head = NULL;
// If file exists, open it for reading and writing.
errno = 0;
fp = fopen(filename,”r”);
if (errno) {
fp = fopen(filename,”w”);
sprintf(linebuf,”END,0″);
head = parse_line(linebuf);
close(fp);
return (head);
}
// Read the file, line-by-line
while (lp = fgets(linebuf,MAX_LINE-1,fp)) {
next = parse_line(linebuf);
if (next) {
if (current) {
current->next = next;
next->next = NULL;
}
else {
head = next;
next->next = NULL;
}
current = next;
}
}
close(fp);
return (head);
}
highscore_t *insert_score(highscore_t *list, char * initials, int score) {
highscore_t *current = NULL;
highscore_t *next = NULL;
highscore_t *new_item = NULL;
highscore_t *last = NULL;
new_item = malloc(sizeof(highscore_t));
if (new_item) {
strncpy(new_item->initials,initials,NAME_SIZE-1);
new_item->score = score;
}
if (!list) {
// the list does not exist, need to create it and add this as the first element
// Return the list.
new_item->next = NULL;
return (new_item);
}
else {
current = list;
while (current) {
if (score > current->score) {
if (last) {
last->next = new_item;
new_item->next=current;
return (list);
}
else {
// New first item in the list
new_item->next = current;
return (new_item);
}
}
last = current;
current = current->next;
}
// Lowest score in the list, add it to the end.
last->next = new_item;
new_item->next = NULL;
}
return(list);
}
int store_scores(char *filename, highscore_t *list) {
int counter = 0;
FILE *fp;
highscore_t *current = NULL;
// Open the high score file for writing.
fp = fopen(filename,”w”);
current = list;
while (current) {
fprintf(fp,”%s,%d\n”,current->initials,current->score);
current = current->next;
}
close(fp);
return (0);
}
int free_score_list (highscore_t *list) {
highscore_t *current = list;
highscore_t *next = NULL;
while (current) {
next = current->next;
free(current);
current = next;
}
return(0);
}
int print_score_list (highscore_t *list, int x_start, int y_start, int numscores) {
/* Prints the scores to the screen. If -1 or 0 is passed for numscores,
then the function prints all of the scores. Otherwise, it prints only numscores
intials/scores to the screen.
*/
int i = 0;
while ((list) && (i
if (strcmp(list->initials,”END”)) {
mvprintw(y_start+i,x_start,”%s %d”,list->initials, list->score);
if (numscores > 0) {
i++;
}
}
list = list->next;
}
return(0);
}
int compare_highscore(highscore_t *list, int score, int numscores) {
int i = 0;
// Compares the passed score with the scores in the list. If
// -1 or 0 is passed for numscores, then the function compares against all scores in the list.
while ((list) && (i
if (score >= list->score) {
return (1);
}
list = list->next;
if (numscores > 0) {
i++;
}
}
return(0);
}
key.c
#include
#include “key.h”
int read_escape(int *read_char) {
int c;
if ((c = getch()) == ERR) {
return (NOCHAR);
}
else if (c==0x1b) {
if ((c = getch()) == ‘[‘) {
c=getch();
switch (c) {
case ‘A’:
return (UP);
break;
case ‘B’:
return (DOWN);
break;
case ‘C’:
return (RIGHT);
break;
case ‘D’:
return (LEFT);
break;
default:
return (BADESC);
}
}
}
else {
*read_char = c;
return (REGCHAR);
}
}
main.c
#include
#include
#include “tetromino.h”
#include “highscore.h”
#include “game.h”
int main(int argc, char *argv[]) {
int status = 1;
highscore_t * highscores;
/* if (argc!=2) { */
/* printf(“Please specify a high score file\n”); */
/* return (-1); */
/* } */
highscores = game(highscores);
endwin();
return (0);
}
score.c
int compute_score(int previous_score, int lines_cleared) {
enum {NORMAL, TETRIS};
int new_score;
static int state = NORMAL;
switch (lines_cleared) {
case 0:
new_score = 0;
break;
case 1:
new_score = 100;
break;
case 2:
new_score = 250;
break;
case 3:
new_score = 500;
break;
case 4:
new_score = 800;
state = TETRIS;
break;
}
switch (state) {
case NORMAL:
return (previous_score + new_score);
break;
case TETRIS:
if (lines_cleared == 4) {
return (previous_score + 1200);
}
else {
state = NORMAL;
return (previous_score + new_score);
}
break;
default:
state = NORMAL;
return (previous_score + new_score);
break;
}
}
void display_score(int score, int x, int y) {
mvprintw(y,x,”*** SCORE ***”,score);
mvprintw(y+1,x,”%8d”,score);
}
tetromino.c
#include
#include
#include
#include
#include
#include “tetromino.h”
const tetromino_t tetromino_types[7] = {
{“block”,
{{0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“tee”,
{{0,0,0,0},
{1,1,1,0},
{0,1,0,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“zigzag_l”,
{{0,0,1,0},
{0,1,1,0},
{0,1,0,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“zigzag_r”,
{{0,1,0,0},
{0,1,1,0},
{0,0,1,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“lform_l”,
{{0,0,1,0},
{0,0,1,0},
{0,1,1,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“lform_r”,
{{0,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,0}},
0,
0,
‘%’,
{0,0,0}},
{“pipe”,
{{0,1,0,0},
{0,1,0,0},
{0,1,0,0},
{0,1,0,0}},
0,
0,
‘%’,
{0,0,0}}
};
int check_collision (tetromino_t *tet) {
int x,y;
chtype row_buf[5];
int num_chars;
int i;
for (y=0;y<+4;y++) {
num_chars = mvinchnstr(tet->upper_left_y+y, tet->upper_left_x, row_buf, 4);
for (x=0;x<4;x++) {
if (tet->piece[x][y] && row_buf[x]!=’ ‘) {
return COLLIDE;
}
}
}
return SAFE;
}
int move_tet (tetromino_t *tet, int new_x, int new_y) {
int old_x = tet->upper_left_x;
int old_y = tet->upper_left_y;
tet->upper_left_x = new_x;
tet->upper_left_y = new_y;
if (check_collision(tet) == COLLIDE) {
tet->upper_left_x = old_x;
tet->upper_left_y = old_y;
return MOVE_FAILED;
}
else {
return MOVE_OK;
}
}
int rotate_cw(tetromino_t *tet) {
char temp[4][4];
int x,y;
tetromino_t temp_tet;
memcpy(&temp_tet,tet,sizeof(tetromino_t));
for (x=0;x<4;x++) {
for (y=0;y<4;y++) {
temp[x][y] = tet->piece[y][3-x];
}
}
memcpy(tet->piece,&temp,sizeof(tet->piece));
if (check_collision(tet) == COLLIDE) {
memcpy(tet,&temp_tet,sizeof(tetromino_t));
return MOVE_FAILED;
}
else {
return MOVE_OK;
}
}
int rotate_ccw(tetromino_t *tet) {
char temp[4][4];
int x,y;
tetromino_t temp_tet;
memcpy(&temp_tet,tet,sizeof(tetromino_t));
for (x=0;x<4;x++) {
for (y=0;y<4;y++) {
temp[x][y] = tet->piece[3-y][x];
}
}
memcpy(tet->piece,&temp,sizeof(tet->piece));
if (check_collision(tet) == COLLIDE) {
memcpy(tet,&temp_tet,sizeof(tetromino_t));
return MOVE_FAILED;
}
else {
return MOVE_OK;
}
}
tetromino_t *create_tetromino (int initial_x, int initial_y) {
int type;
tetromino_t *tet = malloc(sizeof(tetromino_t));
type = rand()%7;
memcpy(tet, &tetromino_types[type], sizeof(tetromino_t));
tet->upper_left_x = initial_x;
tet->upper_left_y = initial_y;
srand(time(NULL));
int randColor = rand()%7 +1;
tet->color[0] = randColor;
return(tet);
}
display_tetromino(tetromino_t *tet) {
int x,y;
initscr();
start_color();
init_pair(tet->color[0],tet->color[0],tet->color[0]);
attron(COLOR_PAIR(tet->color[0]));
for (x=0;x<4;x++) {
for (y=0;y<+4;y++) {
if (tet->piece[x][y]) {
mvprintw(tet->upper_left_y+y,tet->upper_left_x+x,”%c”,tet->draw_char);
}
}
}
attroff(COLOR_PAIR(tet->color[0]));
endwin();
refresh();
}
undisplay_tetromino(tetromino_t *tet) {
int x,y;
for (x=0;x<4;x++) {
for (y=0;y<+4;y++) {
if (tet->piece[x][y]) {
mvprintw(tet->upper_left_y+y,tet->upper_left_x+x,” “,tet->draw_char);
}
}
}
}
int destroy_tetromino(tetromino_t *tet) {
free(tet);
}
well.c
#include
#include
#include “well.h”
well_t *init_well(int upper_left_x, int upper_left_y, int width, int height) {
well_t *w;
w = malloc(sizeof(well_t));
w->upper_left_x = upper_left_x;
w->upper_left_y = upper_left_y;
w->width = width;
w->height = height;
w->draw_char = ‘#’;
w->color[0] = 0;
w->color[1] = 0;
w->color[2] = 0;
return (w);
}
void draw_well(well_t *w) {
int row_counter, column_counter;
// Draw left side of well
for (column_counter=w->upper_left_y;column_counter<=(w->upper_left_y + w->height);column_counter++) {
mvprintw(column_counter,w->upper_left_x,”%c”,w->draw_char);
}
// Draw right side of well
for (column_counter=w->upper_left_y;column_counter<=(w->upper_left_y + w->height);column_counter++) {
mvprintw(column_counter,(w->upper_left_x + w->width),”%c”,w->draw_char);
}
// Draw Bottom of well
for (row_counter=w->upper_left_x;row_counter<=(w->upper_left_x + w->width);row_counter++) {
mvprintw(w->upper_left_y + w->height,row_counter,”%c”,w->draw_char);
}
}
int prune_well(well_t * well) {
// returns the number of lines cleared. Assumes that a new tet has not been started yet.
int row; // indicator of the row in the well. The row furthest from the top is row 0.
chtype *well_original; // pointer to an array of characters representing the state of the well.
chtype *well_modified;
chtype *col_ptr;
int current_column_position, current_row_position, output_row;
int num_chars;
int i, keep_row;
int cleared_rows = 0;
// Setup memory to receive the status of the well
current_column_position = well->upper_left_x + 1; // left most well x, not including the wall
current_row_position = well->upper_left_y + well->height – 1; // bottom of the well.
well_original = malloc(sizeof(chtype) * well->width * well->height);
well_modified = well_original;
// Read in the well
while (current_row_position > well->upper_left_y) {
num_chars = mvinchnstr(current_row_position, current_column_position, well_modified, well->width-1);
well_modified += well->width-1;
current_row_position–;
}
well_modified = well_original; // Reset to the beginning of the memory.
current_row_position = well->upper_left_y + well->height – 1; // bottom of the well.
output_row = current_row_position;
while (current_row_position > well->upper_left_y) {
col_ptr = well_modified; // Capture a pointer to the beginning of the row
keep_row = 0;
// Test the row to see if it is complete
for (i = 0; i < well->width-1; i++) {
if (*well_modified == ‘ ‘) {
keep_row = 1;
}
well_modified++;
}
// If it is not complete, write it back. Otherwise, skip this row.
if (keep_row) {
well_modified = col_ptr; // restore pointer back to the beginning of the row.
for (i = 0; i < well->width-1; i++) {
mvprintw(output_row,current_column_position+i,”%c”,*well_modified);
//mvprintw(output_row,i+2,”%c”,*well_modified);
well_modified++;
}
output_row–;
}
else {
cleared_rows++;
}
current_row_position–;
}
free(well_original);
return(cleared_rows);
}
Solution
game.c
#include
#include
#include
#include “highscore.h”
#include “game.h”
#include “well.h”
#include “tetris.h”
#include “tetromino.h”
#include “key.h”
void init_game(void) {
int x, y;
}
void display_timer(time_t timer, int x, int y) {
// Calculate difference time from start with now.
time_t now = time(NULL);
double second = difftime(now, timer);
mvprintw(y, x, “***TIME(s)***”, second);
mvprintw(y + 1, x, “%8.f (s)”, (long) second);
mvprintw(y + 2, x, “***CONF(s)***”);
mvprintw(y + 3, x, “%8ld (s)”, (long) MAX_TIMER);
}
highscore_t *game(highscore_t *highscores) {
static int state = INIT;
tetromino_t *next = NULL;
tetromino_t *current = NULL;
well_t *w;
int x, y;
int c;
int arrow;
struct timespec tim = { 0, 1000000 };
struct timespec tim_ret;
int move_counter = 0;
int move_timeout = 500;
int status;
int counter = 0;
int lines_cleared = 0;
int score = 0;
char str[80];
time_t starttime = 0;
while (1) {
switch (state) {
case INIT: // Initialize the game, only run one time
initscr();
nodelay(stdscr, TRUE); // Do not wait for characters using getch.
noecho(); // Do not echo input characters
getmaxyx(stdscr, y, x); // Get the screen dimensions
w = init_well(((x / 2) – (WELL_WIDTH / 2)), 3, WELL_WIDTH,
WELL_HEIGHT);
draw_well(w);
time(&starttime); // Save time start
srand(time(NULL)); // Seed the random number generator with the time. Used in create tet.
display_score(score, w->upper_left_x – 15, w->upper_left_y);
display_timer(starttime, w->upper_left_x – 15, w->upper_left_y + 2);
state = ADD_PIECE;
break;
case ADD_PIECE: // Add a new piece to the game
if (next) {
current = next;
next = create_tetromino((w->upper_left_x + (w->width / 2)),
w->upper_left_y);
} else {
current = create_tetromino((w->upper_left_x + (w->width / 2)),
w->upper_left_y);
next = create_tetromino((w->upper_left_x + (w->width / 2)),
w->upper_left_y);
}
if (check_collision(current) == COLLIDE) {
state = GAME_OVER;
} else {
display_tetromino(current);
display_next(next);
state = MOVE_PIECE;
}
break;
case MOVE_PIECE: // Move the current piece
if ((arrow = read_escape(&c)) != NOCHAR) {
if (arrow == UP) {
undisplay_tetromino(current);
rotate_ccw(current);
display_tetromino(current);
} else if (arrow == DOWN) {
undisplay_tetromino(current);
rotate_cw(current);
display_tetromino(current);
} else if (arrow == LEFT) {
undisplay_tetromino(current);
move_tet(current, current->upper_left_x – 1,
current->upper_left_y);
display_tetromino(current);
} else if (arrow == RIGHT) {
undisplay_tetromino(current);
move_tet(current, current->upper_left_x + 1,
current->upper_left_y);
display_tetromino(current);
} else if (arrow == REGCHAR) {
if (c == ‘ ‘) {
move_timeout = DROP_RATE;
}
if (c == ‘q’) {
state = GAME_OVER;
}
}
}
if (move_counter++ >= move_timeout) {
counter++;
undisplay_tetromino(current);
status = move_tet(current, current->upper_left_x,
current->upper_left_y + 1);
display_tetromino(current);
if (status == MOVE_FAILED) {
lines_cleared = prune_well(w);
score = compute_score(score, lines_cleared);
display_score(score, w->upper_left_x – 15, w->upper_left_y);
display_timer(starttime, w->upper_left_x – 15, w->upper_left_y + 2);
state = ADD_PIECE;
move_timeout = BASE_FALL_RATE;
}
move_counter = 0;
}
break;
case GAME_OVER:
nodelay(stdscr, FALSE);
clear();
getmaxyx(stdscr, y, x);
mvprintw(1, x / 2 – 5, ” GAME_OVER “);
mvprintw(2, x / 2 – 5, “#############”);
mvprintw(9, x / 2 – 5, “Score: %d”, score);
mvprintw(11, x / 2 – 5, “Time: %8.f’s”,
difftime(time(NULL), starttime));
mvprintw(13, x / 2 – 5, “Time configure: %d’s”, MAX_TIMER);
mvprintw(16, x / 2 – 5, “Hit q to exit”);
getch(); // Wait for a key to be pressed.
state = EXIT;
break;
case EXIT:
return (highscores); // Return the highscore structure back to main to be stored on disk.
break;
}
refresh();
nanosleep(&tim, &tim_ret);
display_timer(starttime, w->upper_left_x – 15, w->upper_left_y + 2);
if(MAX_TIMER <= difftime(time(NULL), starttime) && state != EXIT)
{
state = GAME_OVER;
}
}
}
game.h
/*
* game.h
*
* Created on: Oct 7, 2017
*/
#ifndef GAME_H_
#define GAME_H_
typedef struct terminal_dimensions {
int maxx;
int maxy;
} terminal_dimensions_t;
// Delay timers for the main game loop.
#ifndef DELAY_US
#define DELAY_US 100000
#endif
// Game States
enum {INIT, ADD_PIECE, MOVE_PIECE, ADJUST_WELL, GAME_OVER, EXIT};
void init_game(void);
highscore_t *game(highscore_t *);
#endif /* GAME_H_ */
highscore.c
#include
#include
#include
#include
#include “highscore.h”
highscore_t *parse_line(char *line) {
highscore_t *sptr;
char *p;
char *p_end;
const char delim = ‘,’;
enum {NAME, SCORE, EXIT};
int state = NAME;
// Make sure that the line ptr is not NULL
if (!line) {
return (NULL);
}
// Make Space for a new structure
sptr = malloc(sizeof(highscore_t));
// Assumnig that the new space is available, break the line down into parts and load the struct.
if (sptr) {
p = strtok(line,&delim); // initialize strtok to break line into tokens delimited by ‘,’
while (p) {
switch (state) {
case NAME:
if (strlen(p)
strcpy(sptr->initials,p);
state = SCORE;
}
else {
return (NULL);
}
break;
case SCORE:
errno = 0;
sptr->score = strtol(p,&p_end,10);
if (errno) {
return (NULL);
}
return (sptr);
break;
default:
return(NULL);
}
p = strtok(NULL,&delim); // Read the next token
}
}
return (NULL);
}
highscore_t *load_scores(char *filename) {
char linebuf[MAX_LINE];
FILE *fp;
char *lp;
int status;
highscore_t *current = NULL;
highscore_t *next = NULL;
highscore_t *head = NULL;
// If file exists, open it for reading and writing.
errno = 0;
fp = fopen(filename,”r”);
if (errno) {
fp = fopen(filename,”w”);
sprintf(linebuf,”END,0″);
head = parse_line(linebuf);
close(fp);
return (head);
}
// Read the file, line-by-line
while (lp = fgets(linebuf,MAX_LINE-1,fp)) {
next = parse_line(linebuf);
if (next) {
if (current) {
current->next = next;
next->next = NULL;
}
else {
head = next;
next->next = NULL;
}
current = next;
}
}
close(fp);
return (head);
}
highscore_t *insert_score(highscore_t *list, char * initials, int score) {
highscore_t *current = NULL;
highscore_t *next = NULL;
highscore_t *new_item = NULL;
highscore_t *last = NULL;
new_item = malloc(sizeof(highscore_t));
if (new_item) {
strncpy(new_item->initials,initials,NAME_SIZE-1);
new_item->score = score;
}
if (!list) {
// the list does not exist, need to create it and add this as the first element
// Return the list.
new_item->next = NULL;
return (new_item);
}
else {
current = list;
while (current) {
if (score > current->score) {
if (last) {
last->next = new_item;
new_item->next=current;
return (list);
}
else {
// New first item in the list
new_item->next = current;
return (new_item);
}
}
last = current;
current = current->next;
}
// Lowest score in the list, add it to the end.
last->next = new_item;
new_item->next = NULL;
}
return(list);
}
int store_scores(char *filename, highscore_t *list) {
int counter = 0;
FILE *fp;
highscore_t *current = NULL;
// Open the high score file for writing.
fp = fopen(filename,”w”);
current = list;
while (current) {
fprintf(fp,”%s,%d\n”,current->initials,current->score);
current = current->next;
}
close(fp);
return (0);
}
int free_score_list (highscore_t *list) {
highscore_t *current = list;
highscore_t *next = NULL;
while (current) {
next = current->next;
free(current);
current = next;
}
return(0);
}
int print_score_list (highscore_t *list, int x_start, int y_start, int numscores) {
/* Prints the scores to the screen. If -1 or 0 is passed for numscores,
then the function prints all of the scores. Otherwise, it prints only numscores
intials/scores to the screen.
*/
int i = 0;
while ((list) && (i
if (strcmp(list->initials,”END”)) {
mvprintw(y_start+i,x_start,”%s %d”,list->initials, list->score);
if (numscores > 0) {
i++;
}
}
list = list->next;
}
return(0);
}
int compare_highscore(highscore_t *list, int score, int numscores) {
int i = 0;
// Compares the passed score with the scores in the list. If
// -1 or 0 is passed for numscores, then the function compares against all scores in the list.
while ((list) && (i
if (score >= list->score) {
return (1);
}
list = list->next;
if (numscores > 0) {
i++;
}
}
return(0);
}
highscore.h
/*
* highscore.h
*
* Created on: Oct 7, 2017
*/
#ifndef HIGHSCORE_H_
#define HIGHSCORE_H_
#define MAX_LINE 100
#define NAME_SIZE 4
#define NUM_SCORES_STORED 10
typedef struct highscore {
char initials[NAME_SIZE+1];
int score;
struct highscore *next;
} highscore_t;
highscore_t *parse_line(char *);
int compare_highscore(highscore_t *, int, int);
highscore_t *load_scores(char *);
highscore_t *insert_score(highscore_t *, char *, int);
int store_scores(char *, highscore_t *);
int compare_highscore(highscore_t *, int, int);
int print_score_list (highscore_t *, int, int, int);
#endif /* HIGHSCORE_H_ */
key.c
#include
#include “key.h”
int read_escape(int *read_char) {
int c;
if ((c = getch()) == ERR) {
return (NOCHAR);
}
else if (c==0x1b) {
if ((c = getch()) == ‘[‘) {
c=getch();
switch (c) {
case ‘A’:
return (UP);
break;
case ‘B’:
return (DOWN);
break;
case ‘C’:
return (RIGHT);
break;
case ‘D’:
return (LEFT);
break;
default:
return (BADESC);
}
}
}
else {
*read_char = c;
return (REGCHAR);
}
}
key.h
/*
* key.h
*
* Created on: Oct 7, 2017
*/
#ifndef KEY_H_
#define KEY_H_
enum {NOCHAR, REGCHAR, UP, DOWN, LEFT, RIGHT, BADESC};
int read_escape(int *);
#endif /* KEY_H_ */
main.c
#include
#include
#include “tetromino.h”
#include “highscore.h”
#include “game.h”
int main(int argc, char *argv[]) {
int status = 1;
highscore_t * highscores;
/* if (argc!=2) { */
/* printf(“Please specify a high score file\n”); */
/* return (-1); */
/* } */
highscores = game(highscores);
endwin();
return (0);
}
score.c
int compute_score(int previous_score, int lines_cleared) {
enum {NORMAL, TETRIS};
int new_score;
static int state = NORMAL;
switch (lines_cleared) {
case 0:
new_score = 0;
break;
case 1:
new_score = 100;
break;
case 2:
new_score = 250;
break;
case 3:
new_score = 500;
break;
case 4:
new_score = 800;
state = TETRIS;
break;
}
switch (state) {
case NORMAL:
return (previous_score + new_score);
break;
case TETRIS:
if (lines_cleared == 4) {
return (previous_score + 1200);
}
else {
state = NORMAL;
return (previous_score + new_score);
}
break;
default:
state = NORMAL;
return (previous_score + new_score);
break;
}
}
void display_score(int score, int x, int y) {
mvprintw(y,x,”*** SCORE ***”,score);
mvprintw(y+1,x,”%8d”,score);
}
score.h
/*
* score.h
*
* Created on: Oct 7, 2017
*/
#ifndef SCORE_H_
#define SCORE_H_
int compute_score(int previous_score, int lines_cleared);
void display_score(int score, int x, int y);
#endif /* SCORE_H_ */
tetris.h
/*
* tetris.h
*
* Created on: Oct 7, 2017
* Author: thanh
*/
#ifndef TETRIS_H_
#define TETRIS_H_
#define WELL_WIDTH 8
#define WELL_HEIGHT 16
#define BASE_FALL_RATE 500
#define DROP_RATE 10
// 5 minute = 5 * 60 seconds
#define MAX_TIMER 300
#endif /* TETRIS_H_ */
tetromino.c
#include
#include
#include
#include
#include
#include “tetromino.h”
const tetromino_t tetromino_types[7] = { { “block”, { { 0, 0, 0, 0 }, { 0, 1, 1,
0 }, { 0, 1, 1, 0 }, { 0, 0, 0, 0 } }, 0, 0, ‘%’, { 0, 0, 0 } }, {
“tee”,
{ { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 } }, 0,
0, ‘%’, { 0, 0, 0 } }, { “zigzag_l”, { { 0, 0, 1, 0 }, { 0, 1, 1, 0 }, {
0, 1, 0, 0 }, { 0, 0, 0, 0 } }, 0, 0, ‘%’, { 0, 0, 0 } }, { “zigzag_r”,
{ { 0, 1, 0, 0 }, { 0, 1, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 } }, 0,
0, ‘%’, { 0, 0, 0 } }, { “lform_l”, { { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, {
0, 1, 1, 0 }, { 0, 0, 0, 0 } }, 0, 0, ‘%’, { 0, 0, 0 } }, { “lform_r”, {
{ 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 }, { 0, 0, 0, 0 } }, 0, 0,
‘%’, { 0, 0, 0 } }, { “pipe”, { { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1,
0, 0 }, { 0, 1, 0, 0 } }, 0, 0, ‘%’, { 0, 0, 0 } } };
int check_collision(tetromino_t *tet) {
int x, y;
chtype row_buf[5];
int num_chars;
int i;
for (y = 0; y < +4; y++) {
num_chars = mvinchnstr(tet->upper_left_y + y, tet->upper_left_x,
row_buf, 4);
for (x = 0; x < 4; x++) {
if (tet->piece[x][y] && row_buf[x] != ‘ ‘) {
return COLLIDE;
}
}
}
return SAFE;
}
int move_tet(tetromino_t *tet, int new_x, int new_y) {
int old_x = tet->upper_left_x;
int old_y = tet->upper_left_y;
tet->upper_left_x = new_x;
tet->upper_left_y = new_y;
if (check_collision(tet) == COLLIDE) {
tet->upper_left_x = old_x;
tet->upper_left_y = old_y;
return MOVE_FAILED;
} else {
return MOVE_OK;
}
}
int rotate_cw(tetromino_t *tet) {
char temp[4][4];
int x, y;
tetromino_t temp_tet;
memcpy(&temp_tet, tet, sizeof(tetromino_t));
for (x = 0; x < 4; x++) {
for (y = 0; y < 4; y++) {
temp[x][y] = tet->piece[y][3 – x];
}
}
memcpy(tet->piece, &temp, sizeof(tet->piece));
if (check_collision(tet) == COLLIDE) {
memcpy(tet, &temp_tet, sizeof(tetromino_t));
return MOVE_FAILED;
} else {
return MOVE_OK;
}
}
int rotate_ccw(tetromino_t *tet) {
char temp[4][4];
int x, y;
tetromino_t temp_tet;
memcpy(&temp_tet, tet, sizeof(tetromino_t));
for (x = 0; x < 4; x++) {
for (y = 0; y < 4; y++) {
temp[x][y] = tet->piece[3 – y][x];
}
}
memcpy(tet->piece, &temp, sizeof(tet->piece));
if (check_collision(tet) == COLLIDE) {
memcpy(tet, &temp_tet, sizeof(tetromino_t));
return MOVE_FAILED;
} else {
return MOVE_OK;
}
}
tetromino_t *create_tetromino(int initial_x, int initial_y) {
int type;
tetromino_t *tet = malloc(sizeof(tetromino_t));
type = rand() % 7;
memcpy(tet, &tetromino_types[type], sizeof(tetromino_t));
tet->upper_left_x = initial_x;
tet->upper_left_y = initial_y;
srand(time(NULL));
int randColor = rand() % 7 + 1;
tet->color[0] = randColor;
return (tet);
}
display_tetromino(tetromino_t *tet) {
int x, y;
initscr();
start_color();
init_pair(tet->color[0], tet->color[0], tet->color[0]);
attron(COLOR_PAIR(tet->color[0]));
for (x = 0; x < 4; x++) {
for (y = 0; y < +4; y++) {
if (tet->piece[x][y]) {
mvprintw(tet->upper_left_y + y, tet->upper_left_x + x, “%c”,
tet->draw_char);
}
}
}
attroff(COLOR_PAIR(tet->color[0]));
endwin();
refresh();
}
void display_next(tetromino_t *tet) {
int x, y;
for (x = 0; x < 4; x++) {
for (y = 0; y < +4; y++) {
if (tet->piece[x][y]) {
mvprintw(tet->upper_left_y + 10, tet->upper_left_x + 4, “%c”,
tet->draw_char);
}
}
}
}
undisplay_tetromino(tetromino_t *tet) {
int x, y;
for (x = 0; x < 4; x++) {
for (y = 0; y < +4; y++) {
if (tet->piece[x][y]) {
mvprintw(tet->upper_left_y + y, tet->upper_left_x + x, ” “,
tet->draw_char);
}
}
}
}
int destroy_tetromino(tetromino_t *tet) {
free(tet);
}
tetromino.h
/*
* tetromino.h
*
* Created on: Oct 7, 2017
*/
#ifndef TETROMINO_H_
#define TETROMINO_H_
typedef struct tetromino {
char type_str[20];
char piece[4][4]; // 4×4 grid of characters, piece[x][y]
int upper_left_x;
int upper_left_y;
char draw_char;
char color[3];
} tetromino_t;
extern const tetromino_t tetromino_types[7];
enum {SAFE, COLLIDE}; // Return status for check_collision
int check_collision (tetromino_t *);
enum {MOVE_OK, MOVE_FAILED}; // Return status for move
int move_tet (tetromino_t *, int, int);
int rotate_cw(tetromino_t *);
int rotate_ccw(tetromino_t *);
tetromino_t *create_tetromino (int, int);
int destroy_tetromino(tetromino_t *);
void print_tetromino(tetromino_t *tet);
void test_tetromino(void);
void display_next(tetromino_t *tet);
/* tetromino.h ends here */
#endif /* TETROMINO_H_ */
well.c
#include
#include
#include “well.h”
well_t *init_well(int upper_left_x, int upper_left_y, int width, int height) {
well_t *w;
w = malloc(sizeof(well_t));
w->upper_left_x = upper_left_x;
w->upper_left_y = upper_left_y;
w->width = width;
w->height = height;
w->draw_char = ‘#’;
w->color[0] = 0;
w->color[1] = 0;
w->color[2] = 0;
return (w);
}
void draw_well(well_t *w) {
int row_counter, column_counter;
// Draw left side of well
for (column_counter=w->upper_left_y;column_counter<=(w->upper_left_y + w->height);column_counter++) {
mvprintw(column_counter,w->upper_left_x,”%c”,w->draw_char);
}
// Draw right side of well
for (column_counter=w->upper_left_y;column_counter<=(w->upper_left_y + w->height);column_counter++) {
mvprintw(column_counter,(w->upper_left_x + w->width),”%c”,w->draw_char);
}
// Draw Bottom of well
for (row_counter=w->upper_left_x;row_counter<=(w->upper_left_x + w->width);row_counter++) {
mvprintw(w->upper_left_y + w->height,row_counter,”%c”,w->draw_char);
}
}
int prune_wll(well_t * well) {
// returns the number of lines cleared. Assumes that a new tet has not been started yet.
int row; // indicator of the row in the well. The row furthest from the top is row 0.
chtype *well_original; // pointer to an array of characters representing the state of the well.
chtype *well_modified;
chtype *col_ptr;
int current_column_position, current_row_position, output_row;
int num_chars;
int i, keep_row;
int cleared_rows = 0;
// Setup memory to receive the status of the well
current_column_position = well->upper_left_x + 1; // left most well x, not including the wall
current_row_position = well->upper_left_y + well->height – 1; // bottom of the well.
well_original = malloc(sizeof(chtype) * well->width * well->height);
well_modified = well_original;
// Read in the well
while (current_row_position > well->upper_left_y) {
num_chars = mvinchnstr(current_row_position, current_column_position, well_modified, well->width-1);
well_modified += well->width-1;
current_row_position–;
}
well_modified = well_original; // Reset to the beginning of the memory.
current_row_position = well->upper_left_y + well->height – 1; // bottom of the well.
output_row = current_row_position;
while (current_row_position > well->upper_left_y) {
col_ptr = well_modified; // Capture a pointer to the beginning of the row
keep_row = 0;
// Test the row to see if it is complete
for (i = 0; i < well->width-1; i++) {
if (*well_modified == ‘ ‘) {
keep_row = 1;
}
well_modified++;
}
// If it is not complete, write it back. Otherwise, skip this row.
if (keep_row) {
well_modified = col_ptr; // restore pointer back to the beginning of the row.
for (i = 0; i < well->width-1; i++) {
mvprintw(output_row,current_column_position+i,”%c”,*well_modified);
//mvprintw(output_row,i+2,”%c”,*well_modified);
well_modified++;
}
output_row–;
}
else {
cleared_rows++;
}
current_row_position–;
}
free(well_original);
return(cleared_rows);
}
well.h
/*
* well.h
*
* Created on: Oct 7, 2017
* Author: thanh
*/
#ifndef WELL_H_
#define WELL_H_
typedef struct well {
int upper_left_x;
int upper_left_y;
int width;
int height;
char draw_char;
char color[3];
} well_t;
well_t *init_well(int, int, int, int);
void draw_well(well_t *);
int prune_well(well_t * well);
#endif /* WELL_H_ */