COMP 3413 Fall 2021 Assignment 3 Page 1 of 6 COMP 3413 Fall 2021 Assignment 3 Due: Monday, Nov 15th, 2021, 11:59 PM Notes and Style • must be written in C. No C++ • The prof has provided a working...

1 answer below »
view attached doc


COMP 3413 Fall 2021 Assignment 3 Page 1 of 6 COMP 3413 Fall 2021 Assignment 3 Due: Monday, Nov 15th, 2021, 11:59 PM Notes and Style • must be written in C. No C++ • The prof has provided a working demo in D2l (a3demo executable <- change="" permissions="" when="" copied="" to="" your="" fcs="" machine="" with="" chmod="" u+x="">) • your assignment code must be handed in electronically using the D2L drop box. Try early and re-submit later. • Include a Makefile. If “make” doesn’t compile and produce an executable, your assignment will not be marked • You make file should also include a “make run” and “make clean” command that runs your executable and removes all files resulting from compilation • Your program must run. Programs that do not run, or segfault before working at all, will lose 50% automatically. Save early and often, and use version control. Submit often, only submit working versions. Partial solutions that are commented out to prevent segfaults should be pointed out to the markers in the submission text box, so markers know to read those comments, otherwise they can skip commented code. • Your program must compile with –Wall, with no warnings showing. You will lose marks for warnings and lose marks for not compiling with -Wall in your makefile • Include a “listing” file, a single .txt file with all your source files concatenated into it. Good header text in EACH file is imperative here. E.g. cat *.h *.c | listing.txt • Use common-sense commenting and good style, practices. Provide function headers, comment regions, etc. No particular format or style is required but bad programming practices will result in marks docked (e.g., huge complicated functions, unreadable code, magic numbers, bad variable names, etc). • Error checking is extremely important in real world OS work, and you must do it rigorously. Error handing, however, is hard in C. Try to handle gracefully when you can, but for hard errors (out of memory, etc.), hard fail (exit) is OK. • Test on FCS computers with their version of gcc, make sure your Makefile uses the one you tested. • Your assignment will be tested on FCS computers. Attack of the Poisonous Fredericton Caterpillars You will make a text version of the classic video game “centipede.” However, when researching Fredericton, before I moved, I found a funny story about venomous caterpillars in the city, so we’ll do caterpillars instead. The rules for the game are simple: kill all the caterpillars before they kill you. The problem is, these are space caterpillars (and you are in a space ship) – when you shoot them, they split at the point they were hit, and now you have two smaller caterpillars! To save explanation, try running a sample of the program (how yours will act) on AN FCS computer using the precompiled executable on D2L. (a3demo). When you copy it over you will probably need to give it executable permissions (chmod u+x a3demo) wasd controls for up/left/down/right), space to fire, and q quits. Synopsis: COMP 3413 Fall 2021 Assignment 3 Page 2 of 6 - Have a player spaceship that the player can control using w, a, s, d, around a restricted space at the bottom of the screen. The player spaceship must animate. - Generate caterpillars starting at the top of the screen, at some random interval. They go left to right (or opposite), and when they hit an end, they go to the next row and change direction. Note in the sample how they wrap around (tricky logic). Caterpillars must animate graphics in addition to move. - If the player presses space, a bullet fires from the player toward the top of the screen. If it goes off the screen, it is destroyed. If it hits a caterpillar, the caterpillar splits into two at that spot, and you now have two caterpillars, with the new one going faster than the old one by some multiple. If one of the two caterpillars is below a minimum size (e.g., 5), it dies. The player has a reasonable maximum fire rate (e.g., once per n ticks). - The caterpillars randomly shoot from their head at the player. - When the player is hit, pause the game briefly and kill all the bullets (but leave the caterpillars) to avoid unfair re-spawn deaths. If the player is hit, they lose a life. When no lives are left, the game is over. - The player wins if there are no caterpillars left on screen. - The program must quit properly by joining all threads, until only the main one is left, and freeing memory, and quickly. The final “Done” text must appear on the console as in the provided main and sample. This is to highlight that you did not just use an exit() call. Every time you create a thread, you MUST think: who will join it and when? This is to make scalable, reusable programs. For the same reasons, you must free all memory properly. - There must be no evident race conditions or deadlock conditions. - Game mechanics will not be a major concern, e.g., left-over dead aliens, exact speeds, etc. In fact, I encourage you to have fun with this. Collision detection must be perfect to help test for race conditions. REQUIRED programming components: As this is a threading assignment, you need the following (to make it hard enough!)  You will create (at least) the following threads. You may find it useful to make additional ones to help you. Threads must be joinable (default), and cannot be detached (man pthread_create). ➢ a thread for the player. This handles the player animation and other things, e.g., what happens when the player is shot. ➢ a thread for the keyboard. This requires its own thread so it can block while the rest of the program runs. This is quite tricky because you will have to use the “select” command (man select): it blocks until there is data on a stream, or on a timeout. Here is the problem: if you use getchar, it blocks. While it is blocking, the game may end (you get shot). However, until getchar unblocks that thread cannot end. Using select, you can time out regularly to check if the game is over before blocking again. ➢ a thread to re-draw the screen / perform a refresh using the command in the provided console code. Do not do refreshes each time you draw, only here. COMP 3413 Fall 2021 Assignment 3 Page 3 of 6 ➢ An upkeep thread, that puts updated score and player lives to screen, checks if there are no enemies left, and does regular cleanup (such as delete memory left behind for dead bullets and caterpillars, maybe). ➢ A thread that spawns new caterpillars at a regular (but somewhat random) interval. ➢ a new thread for EACH caterpillar. The thread lives until the caterpillar is shot and killed. Using a single thread for all the caterpillars is not acceptable. When a caterpillar splits, a new thread is started for the new caterpillar. ➢ a new thread for each bullet (yours and caterpillars’!), which lives until the bullet gets consumed into an enemy or you, or until it leaves the screen. ➢ a main program (not in main.c!) that starts everything and then sleeps using a condition variable (pthread_cond_wait). When the game ends this thread is woken up (pthread_cond_signal) and cleans up. You must use a condition variable and signal here for the assignment.  Dynamically managed (malloc/free) linked lists: these are very thread un-friendly. At one list for the bullets, and one list for the caterpillars. No storing in arrays, etc. In a real game engine, you would likely have fewer threads, e.g., one thread for enemy AI, one thread for input, etc. However, games are highly multithreaded and have a lot of concurrency issues. Making a game engine is a lot like making a simple OS as well: there is a core set of functions that control access to game resources and keeps track of states of other parts. Then, the actual game components (player, enemies, etc) will access those through the engine. This assignment should require more thinking about the engineering and planning of your solution than normal, but it is perhaps algorithmically more straight forward than A2. Have fun! There’s a lot up to you (redesign the spaceship or enemy look, manage difficulty, etc). Your caterpillars may just be simple chains of ascii characters that animate a bit, or they may be more like as seen in the example, larger and stranger looking. Want to add other types? Go for it! There are no game aesthetic or marks for “fun” factor, but it can feel good to make something you’re proud of. Really cool submissions will be given a few extra marks (but not enough to make up for not threading correctly). Handout: You have been provided with a console.h/console.c library that provides text- mode functions for you and a sample program. Be sure to compile with the –lcurses flag to link to the curses (text graphics) library, and the –pthread flag to link to the posix-threads library. Console.h has a lot of comments to explain how to use each function. Definitely use these in your solution. They are provided so you don’t have to worry about “graphics” and drawing to screen here. You should not modify this code. Look at console.h comments to understand what each function does.  This library is simple to use (see the example). There is a screen buffer off screen. You draw to the buffer using the commands detailed in the library. To have the changes reflected to screen, use the refresh command. To move, e.g., the player, you first draw a blank
Answered 1 days AfterOct 26, 2021

Answer To: COMP 3413 Fall 2021 Assignment 3 Page 1 of 6 COMP 3413 Fall 2021 Assignment 3 Due: Monday, Nov 15th,...

Ayush answered on Oct 28 2021
107 Votes
Space-centipede-master/.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}
Space-centipede-master/bullet.c
#include
#include
#include
#include
#include "bullet.h"
#include "console.h"
#include "globals.h"
#include "player.h"
bool check_hit(int row, int col);
static Bullet *b;
Bullet *init_bullet(char *head, int row, int col, int direction)
{
Bullet *bullet = (Bullet *)malloc(sizeof(Bullet));
//initialize the bullet
bullet->head[0] = head;
bullet->col = col;
bullet->row = row;
bullet->hits = false;
bullet->speed = BULLET_SPEED;
bullet->direction = direction;
return bullet;
}
void update_bullet
(Bullet * bullet)
{
b = bullet;
}
Bullet *get_bullet()
{
return b;
}
void bulletRun(void *bullet_t)
{
Bullet *bullet = (Bullet *)bullet_t;
while (!killThreads && !bullet->hits)
{
if (bullet->row > TOP_WALL && bullet->row < BOTTOM)
{
pthread_mutex_lock(&lock_console);
consoleClearImage(bullet->row, bullet->col, BULLET_SIZE, BULLET_SIZE);
bullet->row += bullet->direction;
consoleDrawImage(bullet->row, bullet->col, bullet->head, BULLET_SIZE);
pthread_mutex_unlock(&lock_console);
if(bullet->direction == DOWN)
{
if( bullet->row == player->row && bullet->col >= player->col
&& bullet->col < player->col + PLAYER_WIDHT)
{
bullet->hits = true;
pthread_mutex_lock(&lock_player);
player->dies = true;
pthread_mutex_unlock(&lock_player);
}
}
else if (bullet->direction == UP && !bullet->hits)
{
update_bullet(bullet);
}
}
else
{
pthread_mutex_lock(&lock_console);
consoleClearImage(bullet->row, bullet->col, BULLET_SIZE, BULLET_SIZE);
pthread_mutex_unlock(&lock_console);
    bullet->hits = true;
}

sleepTicks(bullet->speed);
}

pthread_exit(NULL);
}
Space-centipede-master/bullet.h
#ifndef MY_BULLET_H
#define MY_BULLET_H
#include
#define BULLET_SPEED 4
#define BULLET_UP_LIMIT 2
#define BULLET_SIZE 1
typedef struct BULLET Bullet;
struct BULLET
{
    char *head[BULLET_SIZE];
    int col;
    int row;
    int direction;
    pthread_t thread;
    int speed;
int hits;
};
void bulletRun();
//creates a bullet give the starting position and the direction to go
Bullet *init_bullet(char *head, int row, int col, int direction);
Bullet *get_bullet();
#endif
Space-centipede-master/comp3430_A2.c
#include
#include "game.h"
#include "console.h"
int main(int argc, char**argv) {
    gameRun();
    printf("done!\n");
    
    return 0;
}
Space-centipede-master/console.c
#include "console.h"
#include
#include
#include /*for nano sleep */
#include
static int CON_WIDTH, CON_HEIGHT;
static int consoleLock = false;
static int MAX_STR_LEN = 256; /* for strlen checking */
/* Local functions */
static bool checkConsoleSize(int reqHeight, int reqWidth)
{
    if ( (reqWidth > COLS) || (reqHeight > LINES) )
    {
        fprintf(stderr, "\n\n\rSorry, your window is only %ix%i. \n\r%ix%i is required. Sorry.\n\r", COLS, LINES, reqWidth, reqHeight);
        return (false);
    }
return(true);
}
bool consoleInit(int height, int width, char *image[]) /* assumes image height/width is same as height param */
{
    bool status;
    initscr();
    crmode();
    noecho();
    clear();
    CON_HEIGHT = height; CON_WIDTH = width;
    status = checkConsoleSize(CON_HEIGHT, CON_WIDTH);
    if (status)
    {
        consoleDrawImage(0, 0, image, CON_HEIGHT);
        consoleRefresh();
    }
    return(status);
}
void consoleDrawImage(int row, int col, char *image[], int height)
{
    int i, length;
    int newLeft, newRight, newOffset, newLength;
    if (consoleLock) return;
    newLeft = col < 0 ? 0 : col;
    newOffset = col < 0 ? -col : 0;
    for (i = 0; i < height; i++)
    {
        if (row+i < 0 || row+i >= CON_HEIGHT)
            continue;
        length = strnlen(image[i], MAX_STR_LEN);
        newRight = col+length >= CON_WIDTH ? CON_WIDTH-1 : col+length;
        newLength = newRight - newLeft + 1;
        if (newOffset >= length || newLength <= 0)
         continue;
        if (mvaddnstr(row+i, newLeft, image[i]+newOffset, newLength) == ERR)
            fprintf(stderr, "ERROR drawing to screen"); /* smarter handling is needed */
    }
}
void consoleClearImage(int row, int col, int height, int width)
{
    int i, j;
    if (consoleLock) return;
    if (col+width > CON_WIDTH)
        width = CON_WIDTH-col;
    if (col < 0)
    {
        width += col; /* -= -col */
        col = 0;
    }
    if (width < 1 || col >= CON_WIDTH) /* nothing to clear */
        return;
    for (i = 0; i < height; i++)
    {
        if (row+i < 0 || row+i >= CON_HEIGHT)
            continue;
        move(row+i, col);
        for (j = 0; j < width; j++)
             addch(' ');
    }
}
void consoleRefresh(void)
{
    if (!consoleLock)
    {
     move(LINES-1, COLS-1);
     refresh();
    }
}
void consoleFinish(void)
{
endwin();
}
void putBanner(const char *str)
{
if (consoleLock) return;
int len;
len = strnlen(str,MAX_STR_LEN);

move (CON_HEIGHT/2, (CON_WIDTH-len)/2);
addnstr(str, len);
consoleRefresh();
}
void putString(char *str, int row, int col, int maxlen)
{
if (consoleLock) return;
move(row, col);
addnstr(str, maxlen);
}
/* setup to work in USECS, reduces risk of overflow */
/* 10000 usec = 10 ms, or 100fps */
#define TIMESLICE_USEC 10000
#define TIME_USECS_SIZE 1000000
#define USEC_TO_NSEC 1000
struct timespec getTimeout(int ticks)
{
struct timespec rqtp;
/* work in usecs at first */
rqtp.tv_nsec = TIMESLICE_USEC * ticks;
/* handle usec overflow */
rqtp.tv_sec = rqtp.tv_nsec / TIME_USECS_SIZE;
rqtp.tv_nsec %= TIME_USECS_SIZE;
rqtp.tv_nsec *= USEC_TO_NSEC; /*convert to nsecs */
return rqtp;
}
void sleepTicks(int ticks)
{
if (ticks <= 0)
return;
struct timespec rqtp = getTimeout(ticks);
nanosleep(&rqtp, NULL);
}
#define FINAL_PAUSE 2
void finalKeypress()
{
    flushinp();
    sleepTicks(FINAL_PAUSE);
    move(LINES-1, COLS-1);
    getch(); /* wait for user to press a character, blocking. */
}
void disableConsole(int disabled)
{
    consoleLock = disabled;
}
Space-centipede-master/console.h
#ifndef CONSOLE_H
#define CONSOLE_H
#include
/**************** DRAWING **************************/
/* directions in terms of deltas in x / y dimension */
#define LEFT -1
#define RIGHT 1
#define UP -1
#define DOWN 1
#define SCR_LEFT 0
#define SCR_TOP 0
/* Initialize curses, draw initial gamescreen. Refreshes console to terminal.
Also stores the requested dimensions of the consoe and tests the terminal for the
given dimensions.*/
extern bool consoleInit(int reqHeight, int reqWidth, char *image[]);
/* Draws 2d `image' of `height' rows, at curses coordinates `(row, col)'.
Note: parts of the `image' falling on negative rows are not drawn; each
row drawn is clipped on the left and right side of the game console (note
that `col' may be negative, indicating `image' starts to the left of the
screen and will thus only be partially drawn. Useful for objects that are
half off the screen */
extern void consoleDrawImage(int row, int col, char *image[], int height);
/* Clears a 2d `width'x`height' rectangle with spaces. Upper left hand
corner is curses coordinate `(row,col)'. */
extern void consoleClearImage(int row, int col, int width, int height);
/* Moves cursor to bottom right corner and refreshes. If this is not done,
the curses internal buffer (that you have been drawing to) is not dumped
to screen. */
extern void consoleRefresh(void);
/* turns off all updates. Can be used to prevent the screen refresh from working, e.g., at game end while threads are all catching up.*/
extern void disableConsole(int disabled);
/* Terminates curses cleanly. */
extern void consoleFinish(void);
/* Puts the given banner in the center of the screen */
void putBanner(const char *);
/* Draws the given string at the given location */
void putString(char *, int row, int col, int maxlen);
/* Sleeps the given number of 20ms ticks */
void sleepTicks(int ticks);
/* clears the input buffer and then waits for one more key */
void finalKeypress();
/* gets a timespec that represents the time of one tick */
struct timespec getTimeout(int ticks);
#endif /* CONSOLE_H */
Space-centipede-master/enemy.c
#include
#include
#include
#include
#include
#include
#include "enemy.h"
#include "console.h"
#include "bullet.h"
#include "globals.h"
char* ENEMY_HEAD[ENEMY_ANIM_TILES][ENEMY_HEIGHT] =
{
{"@@", "><"},
{"QQ", "=="},
};
char* ENEMY_BODY[ENEMY_ANIM_TILES][ENEMY_HEIGHT] =
{
{"Oo", "/\\"},
{"oO", "()"},
};
void enemy_shoot();
void move_body(Body_part *body);
void move_head(Enemy *enemy);
Enemy *init_head(int row, int col, int direction);
void break_apart(List *body_list, int direction, int row, int col, int at);
//static Enemy *enemy;
void enemyRun(void *enemy_t)
{
Enemy *enemy = (Enemy *)enemy_t;
char** head;
char** tail;
int i = 0;
int shoot = 0;
int prevcol = enemy->row;
int prevrow = enemy->col;
Body_part *body;
int new_enemy = 0;

srand(time(NULL));
//printf("f2 %d %d\n", enemy->col, enemy->row);
i = 0;
while (!killThreads && !enemy->dead)
{
new_enemy++;
freeze(); //if frozen wait
shoot = rand() % RAND;
//shoot at some random interval(when shoot is btw Rand and shoot_now)
if(shoot < SHOOT_NOW || (shoot > SHOOT_PLAYER &&
enemy->col == player->col))
{
enemy_shoot(enemy->row + BULLET_OFFSET, enemy->col);
}
//head = enemy->HEAD[i % ENEMY_ANIM_TILES];
head = ENEMY_HEAD[i % ENEMY_ANIM_TILES];

pthread_mutex_lock(&lock_console);
consoleClearImage(prevrow, prevcol, strlen(head[0]), ENEMY_HEIGHT);
consoleDrawImage(enemy->row, enemy->col, head, ENEMY_HEIGHT);
pthread_mutex_unlock(&lock_console);
//tail = enemy->TAIL[i % ENEMY_ANIM_TILES];
body = (Body_part*)list_firstItem(enemy->BODY);
tail = ENEMY_BODY[i % ENEMY_ANIM_TILES];
//bool broken = false;
int count = 0;
if(new_enemy > GAME_COLS && shoot < SHOOT_NOW)
{
pthread_cond_signal(&cond_enemy);
new_enemy =...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here