General refactor and minor improvements
This commit is contained in:
4
Makefile
4
Makefile
@@ -1,5 +1,5 @@
|
||||
CC = gcc
|
||||
CFLAGS = -std=c11 -Wextra -Wpedantic -g3 -fsanitize=address
|
||||
CFLAGS = -std=c11 -Wextra -Wpedantic -pthread -g3 -fsanitize=address
|
||||
LDFLAGS = -lncurses -fsanitize=address
|
||||
SRC = $(filter-out voorbeeld.c,$(wildcard *.c))
|
||||
HEADERS = $(wildcard *.h)
|
||||
@@ -17,7 +17,7 @@ voorbeeld:
|
||||
tarball1: deel1.tar.gz
|
||||
tarball2: deel2.tar.gz
|
||||
|
||||
deel1.tar.gz: spel.c rooster.h rooster.c Makefile
|
||||
deel1.tar.gz: spel.c grid.h grid.c Makefile
|
||||
tar czf $@ $^
|
||||
|
||||
deel2.tar.gz: $(SRC) $(HEADERS) Makefile
|
||||
|
||||
@@ -2,17 +2,40 @@
|
||||
| |
|
||||
| !!! HOWTO MINIGAME !!! |
|
||||
| |
|
||||
| Make sure that your terminal |
|
||||
| has enough space to display |
|
||||
| all the output of the game. |
|
||||
| |
|
||||
| This can be achieved by |
|
||||
| going fullscreen and/or |
|
||||
| zooming out. |
|
||||
| |
|
||||
| *________________* |
|
||||
| |--- MANUAL ---| |
|
||||
| | GENERAL | |
|
||||
| *------------------------------------* |
|
||||
| | | |
|
||||
| | Exit: | |
|
||||
| | Exit current screen: | |
|
||||
| | Escape, backspace | |
|
||||
| | | |
|
||||
| | Reset zoom: | |
|
||||
| | "'ctrl' + '0'" | |
|
||||
| | | |
|
||||
| | Zoom out: | |
|
||||
| | "'ctrl' + '-'" | |
|
||||
| | | |
|
||||
| | Zoom in: | |
|
||||
| | "'ctrl' + '+'" | |
|
||||
| | "'ctrl' + 'shift' + '='" | |
|
||||
| | | |
|
||||
| *------------------------------------* |
|
||||
| |
|
||||
| "'ctrl' + '-'" |
|
||||
| |
|
||||
| You can reset the zoom with: |
|
||||
| "'ctrl' + '0'" |
|
||||
| |
|
||||
| *________________* |
|
||||
| |---- MENU ----| |
|
||||
| | MENU | |
|
||||
| *------------------------------------* |
|
||||
| | | |
|
||||
| | Move down: | |
|
||||
@@ -24,13 +47,10 @@
|
||||
| | Select: | |
|
||||
| | 'f', enter, space bar | |
|
||||
| | | |
|
||||
| | Exit: | |
|
||||
| | Escape, backspace | |
|
||||
| | | |
|
||||
| *------------------------------------* |
|
||||
| |
|
||||
| *-----------------* |
|
||||
| |- MAZE RUNNER -| |
|
||||
| | MAZE RUNNER | |
|
||||
| *------------------------------------* |
|
||||
| | | |
|
||||
| | Move up: | |
|
||||
@@ -45,13 +65,10 @@
|
||||
| | Move left: | |
|
||||
| | 'a', arrow_left | |
|
||||
| | | |
|
||||
| | Exit: | |
|
||||
| | Escape, backspace | |
|
||||
| | | |
|
||||
| *------------------------------------* |
|
||||
| |
|
||||
| *-----------------* |
|
||||
| |---- SNAKE ----| |
|
||||
| | SNAKE | |
|
||||
| *------------------------------------* |
|
||||
| | | |
|
||||
| | Turn up: | |
|
||||
@@ -66,10 +83,5 @@
|
||||
| | Turn left: | |
|
||||
| | 'a', arrow_left | |
|
||||
| | | |
|
||||
| | Exit: | |
|
||||
| | Escape, backspace | |
|
||||
| | | |
|
||||
| *------------------------------------* |
|
||||
*--------------------------------------------*
|
||||
1234567890123456789012345678901234567890123456
|
||||
10 20 30 40 |
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
#include "rooster.h"
|
||||
#include "grid.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* The rooster type this program is build around.
|
||||
* The grid type this program is build around.
|
||||
*/
|
||||
typedef struct rooster_data {
|
||||
typedef struct grid_data {
|
||||
char *rost;
|
||||
int height;
|
||||
int width;
|
||||
toestand state;
|
||||
} rooster;
|
||||
state state;
|
||||
} grid;
|
||||
|
||||
rooster *grid_from_string(const char* input) {
|
||||
static int internal_location(const grid *gp, const int x, const int y) {
|
||||
return y * (gp->width + 1) + x;
|
||||
}
|
||||
|
||||
grid *grid_create_from_string(const char* input) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
const size_t len = strlen(input) / sizeof(char);
|
||||
@@ -38,20 +42,20 @@ rooster *grid_from_string(const char* input) {
|
||||
|
||||
const int grid_size = (width + 1) * height + 1;
|
||||
|
||||
rooster *rp = malloc(sizeof(rooster));
|
||||
grid *gp = malloc(sizeof(grid));
|
||||
|
||||
if (rp == NULL) {
|
||||
if (gp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rp->rost = malloc(grid_size * sizeof(char));
|
||||
rp->height = height;
|
||||
rp->width = width;
|
||||
rp->state = STATE_BEGIN;
|
||||
gp->rost = malloc(grid_size * sizeof(char));
|
||||
gp->height = height;
|
||||
gp->width = width;
|
||||
gp->state = STATE_BEGIN;
|
||||
|
||||
strcpy(rp->rost, input);
|
||||
strcpy(gp->rost, input);
|
||||
|
||||
return rp;
|
||||
return gp;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -59,16 +63,16 @@ rooster *grid_from_string(const char* input) {
|
||||
*
|
||||
* Input:
|
||||
* fh: the stream to read the grid from.
|
||||
* rost: a rooster file to store the width and height in.
|
||||
* rost: a grid file to store the width and height in.
|
||||
*
|
||||
* Side effects:
|
||||
* the rooster gets its width and height set
|
||||
* the grid gets its width and height set
|
||||
*
|
||||
* Output:
|
||||
* 1 if the file width and height seem to match with its size
|
||||
* 0 otherwise
|
||||
*/
|
||||
static int get_grid_sizes(FILE *fh, rooster *rost) {
|
||||
static int get_grid_sizes(FILE *fh, grid *rost) {
|
||||
while (getc(fh) != '\n') {
|
||||
if (feof(fh)) {
|
||||
return 0;
|
||||
@@ -94,19 +98,19 @@ static int get_grid_sizes(FILE *fh, rooster *rost) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
rooster *grid_from_file(FILE *fh) {
|
||||
grid *grid_create_from_file(FILE *fh) {
|
||||
if (fh == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rooster rost = {
|
||||
grid rost = {
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
STATE_BEGIN
|
||||
};
|
||||
|
||||
// Sets the width and height of the rooster.
|
||||
// Sets the width and height of the grid.
|
||||
if (get_grid_sizes(fh, &rost) != 1) {
|
||||
// Unlogical file structure.
|
||||
return NULL;
|
||||
@@ -147,76 +151,72 @@ rooster *grid_from_file(FILE *fh) {
|
||||
|
||||
free(line);
|
||||
|
||||
rooster *return_rooster = malloc(sizeof(rost));
|
||||
if (return_rooster == NULL) {
|
||||
grid *return_grid = malloc(sizeof(rost));
|
||||
if (return_grid == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(return_rooster, &rost, sizeof(rost));
|
||||
memcpy(return_grid, &rost, sizeof(rost));
|
||||
|
||||
return return_rooster;
|
||||
return return_grid;
|
||||
}
|
||||
|
||||
toestand rooster_vraag_toestand(const rooster *rp) {
|
||||
if (rp != NULL) {
|
||||
return rp->state;
|
||||
state grid_fetch_state(const grid *gp) {
|
||||
if (gp != NULL) {
|
||||
return gp->state;
|
||||
}
|
||||
return STATE_VERLOREN;
|
||||
}
|
||||
|
||||
void rooster_zet_toestand(rooster *rp, toestand t) {
|
||||
if (rp != NULL) {
|
||||
void grid_put_state(grid *gp, const state t) {
|
||||
if (gp != NULL) {
|
||||
switch (t) {
|
||||
case STATE_BEGIN:
|
||||
rp->state = STATE_BEGIN;
|
||||
gp->state = STATE_BEGIN;
|
||||
break;
|
||||
case STATE_AAN_HET_SPELEN:
|
||||
rp->state = STATE_AAN_HET_SPELEN;
|
||||
gp->state = STATE_AAN_HET_SPELEN;
|
||||
break;
|
||||
case STATE_GEWONNEN:
|
||||
rp->state = STATE_GEWONNEN;
|
||||
gp->state = STATE_GEWONNEN;
|
||||
break;
|
||||
case STATE_VERLOREN:
|
||||
rp->state = STATE_VERLOREN;
|
||||
gp->state = STATE_VERLOREN;
|
||||
break;
|
||||
case STATE_QUIT:
|
||||
rp->state = STATE_QUIT;
|
||||
gp->state = STATE_QUIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rooster_klaar(rooster *rp) {
|
||||
if (rp != NULL) {
|
||||
if (rp->rost != NULL)
|
||||
void grid_cleanup(grid *gp) {
|
||||
if (gp != NULL) {
|
||||
if (gp->rost != NULL)
|
||||
{
|
||||
free(rp->rost);
|
||||
free(gp->rost);
|
||||
}
|
||||
free(rp);
|
||||
free(gp);
|
||||
}
|
||||
}
|
||||
|
||||
int rooster_breedte(const rooster *rp) {
|
||||
if (rp == NULL) {
|
||||
int grid_width(const grid *gp) {
|
||||
if (gp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return rp->width;
|
||||
return gp->width;
|
||||
}
|
||||
|
||||
int rooster_hoogte(const rooster *rp) {
|
||||
if (rp == NULL) {
|
||||
int grid_height(const grid *gp) {
|
||||
if (gp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return rp->height;
|
||||
return gp->height;
|
||||
}
|
||||
|
||||
static int internal_location(const rooster *rp, const int x, const int y) {
|
||||
return y * (rp->width + 1) + x;
|
||||
}
|
||||
|
||||
int rooster_bevat(const rooster *rp, int x, int y) {
|
||||
if (rp != NULL && rp->rost != NULL) {
|
||||
if (x >= 0 && y >= 0 && x < rp->width && y < rp->height)
|
||||
int grid_contains(const grid *gp, const int x, const int y) {
|
||||
if (gp != NULL && gp->rost != NULL) {
|
||||
if (x >= 0 && y >= 0 && x < gp->width && y < gp->height)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -224,58 +224,58 @@ int rooster_bevat(const rooster *rp, int x, int y) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char rooster_kijk(const rooster *rp, int x, int y) {
|
||||
if (rp != NULL && rp->rost != NULL && rooster_bevat(rp, x, y) == 1) {
|
||||
return rp->rost[internal_location(rp, x, y)];
|
||||
char grid_fetch(const grid *gp, const int x, const int y) {
|
||||
if (gp != NULL && gp->rost != NULL && grid_contains(gp, x, y) == 1) {
|
||||
return gp->rost[internal_location(gp, x, y)];
|
||||
}
|
||||
return '\0';
|
||||
}
|
||||
|
||||
int rooster_plaats(rooster *rp, int x, int y, char c) {
|
||||
if (rp != NULL && rp->rost != NULL && rooster_bevat(rp, x, y) == 1) {
|
||||
rp->rost[internal_location(rp, x, y)] = c;
|
||||
int grid_put(grid *gp, const int x, const int y, const char c) {
|
||||
if (gp != NULL && gp->rost != NULL && grid_contains(gp, x, y) == 1) {
|
||||
gp->rost[internal_location(gp, x, y)] = c;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *rooster_vraag_rij(const rooster *rp, int y) {
|
||||
if (rp != NULL && rp->rost != NULL && rooster_bevat(rp, 0, y) == 1) {
|
||||
char *grid_fetch_row(const grid *gp, const int y) {
|
||||
if (gp != NULL && gp->rost != NULL && grid_contains(gp, 0, y) == 1) {
|
||||
// we're going to remove the newline so this is long enough
|
||||
char *row = malloc((rp->width + 1) * sizeof(char));
|
||||
memcpy(row, &rp->rost[internal_location(rp, 0, y)], rp->width * sizeof(char));
|
||||
row[rp->width] = '\0';
|
||||
char *row = malloc((gp->width + 1) * sizeof(char));
|
||||
memcpy(row, &gp->rost[internal_location(gp, 0, y)], gp->width * sizeof(char));
|
||||
row[gp->width] = '\0';
|
||||
return row;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rooster *rooster_kopieer(const rooster *rp) {
|
||||
if (rp != NULL && rp->rost != NULL) {
|
||||
const size_t grid_memory = ((rp->width + 1) * rp->height + 1) * sizeof(char);
|
||||
grid *grid_copy(const grid *gp) {
|
||||
if (gp != NULL && gp->rost != NULL) {
|
||||
const size_t grid_memory = ((gp->width + 1) * gp->height + 1) * sizeof(char);
|
||||
|
||||
char *grid = malloc(grid_memory);
|
||||
if (grid == NULL) {
|
||||
char *rost = malloc(grid_memory);
|
||||
if (rost == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rooster *new_rooster = malloc(sizeof(*rp));
|
||||
if (new_rooster == NULL) {
|
||||
grid *new_grid = malloc(sizeof(*gp));
|
||||
if (new_grid == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(grid, rp->rost, grid_memory);
|
||||
memcpy(rost, gp->rost, grid_memory);
|
||||
|
||||
memcpy(new_rooster, rp, sizeof(*rp));
|
||||
memcpy(new_grid, gp, sizeof(*gp));
|
||||
|
||||
new_rooster->rost = grid;
|
||||
return new_rooster;
|
||||
new_grid->rost = rost;
|
||||
return new_grid;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rooster_zoek(const rooster *rp, char c, int *x, int *y) {
|
||||
if (rp == NULL || rp->rost == NULL) {
|
||||
void grid_find(const grid *gp, const char c, int *x, int *y) {
|
||||
if (gp == NULL || gp->rost == NULL) {
|
||||
*x = -1;
|
||||
*y = -1;
|
||||
return;
|
||||
@@ -283,15 +283,15 @@ void rooster_zoek(const rooster *rp, char c, int *x, int *y) {
|
||||
|
||||
const char search[2] = {c};
|
||||
|
||||
int strpos = strcspn(rp->rost, search);
|
||||
const int char_index = (int)strcspn(gp->rost, search);
|
||||
|
||||
if (rp->rost[strpos] == '\0')
|
||||
if (gp->rost[char_index] == '\0')
|
||||
{
|
||||
*x = -1;
|
||||
*y = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
*x = strpos % (rp->width + 1);
|
||||
*y = strpos / (rp->width + 1);
|
||||
*x = char_index % (gp->width + 1);
|
||||
*y = char_index / (gp->width + 1);
|
||||
}
|
||||
174
grid.h
Normal file
174
grid.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* grid.h
|
||||
*
|
||||
* This module provides a grid api.
|
||||
*
|
||||
* A grid is a rectangular grid of objects. Every object in this grid is a char.
|
||||
*/
|
||||
|
||||
#ifndef _GRID_H
|
||||
#define _GRID_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
struct grid_data;
|
||||
typedef struct grid_data grid;
|
||||
|
||||
typedef enum {
|
||||
STATE_BEGIN,
|
||||
STATE_AAN_HET_SPELEN,
|
||||
STATE_GEWONNEN,
|
||||
STATE_VERLOREN,
|
||||
STATE_QUIT
|
||||
} state;
|
||||
|
||||
/* Maak een rooster op basis van de data in de gegeven stream.
|
||||
*
|
||||
* fh: de stream waaruit het doolhof gelezen moet worden.
|
||||
*
|
||||
* Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is
|
||||
* gealloceerd), dat overeenkomt met de gegeven beschrijving.
|
||||
* De begintoestand is BEGIN.
|
||||
*
|
||||
* Als de beschrijving niet consistent is (bijvoorbeeld
|
||||
* niet alle rijen zijn even lang, of er klopt iets anders niet), of
|
||||
* als niet voldoende geheugen kan worden gereserveerd, dan wordt
|
||||
* NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen
|
||||
* achter.)
|
||||
*/
|
||||
grid *grid_create_from_file(FILE *fh);
|
||||
|
||||
/*
|
||||
* Maak een rooster op basis van een gegeven string.
|
||||
*
|
||||
* Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is
|
||||
* gealloceerd), dat overeenkomt met de gegeven beschrijving.
|
||||
* De begintoestand is BEGIN.
|
||||
*
|
||||
* Als de beschrijving niet consistent is (bijvoorbeeld
|
||||
* niet alle rijen zijn even lang, of er klopt iets anders niet), of
|
||||
* als niet voldoende geheugen kan worden gereserveerd, dan wordt
|
||||
* NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen
|
||||
* achter.)
|
||||
*/
|
||||
grid *grid_create_from_string(const char* input);
|
||||
|
||||
/*
|
||||
* Haal een rij uit het rooster op.
|
||||
*
|
||||
* Input:
|
||||
* gp: een pointer naar het rooster
|
||||
* y: de y-coordinaat van de rij die je wilt hebben.
|
||||
*
|
||||
* Output:
|
||||
* Een pointer naar een nieuwe string met daarin alle karakters in die rij.
|
||||
*/
|
||||
char *grid_fetch_row(const grid *gp, int y);
|
||||
|
||||
/*
|
||||
* Maak een kopie van een rooster
|
||||
*
|
||||
* Input:
|
||||
* gp: Een pointer naar het rooster.
|
||||
*
|
||||
* Output:
|
||||
* Een pointer naar het kopie.
|
||||
*/
|
||||
grid *grid_copy(const grid *gp);
|
||||
|
||||
/* Vraag de huidige toestand van het spel op.
|
||||
*
|
||||
* Input:
|
||||
* gp: een pointer naar het rooster.
|
||||
*
|
||||
* Uitvoer: de toestand.
|
||||
*/
|
||||
state grid_fetch_state(const grid *gp);
|
||||
|
||||
|
||||
/* Verander de huidige toestand van het spel.
|
||||
*
|
||||
* gp: een pointer naar het rooster.
|
||||
* t: de nieuwe toestand.
|
||||
*/
|
||||
void grid_put_state(grid *gp, state t);
|
||||
|
||||
|
||||
/* Geef alle resources vrij die zijn gealloceerd voor een rooster.
|
||||
* De rooster pointer is na aanroep van deze functie niet meer bruikbaar.
|
||||
*
|
||||
* gp: een pointer naar het rooster.
|
||||
*/
|
||||
void grid_cleanup(grid *gp);
|
||||
|
||||
|
||||
/* Vraag de breedte van het rooster op, dat wil zeggen, het aantal kolommen.
|
||||
*
|
||||
* gp: een pointer naar het rooster.
|
||||
*
|
||||
* Uitvoer: de breedte.
|
||||
*/
|
||||
int grid_width(const grid *gp);
|
||||
|
||||
|
||||
/* Vraag de hoogte van het rooster op, dat wil zeggen, het aantal rijen.
|
||||
*
|
||||
* gp: een pointer naar het rooster.
|
||||
*
|
||||
* Uitvoer: de hoogte.
|
||||
*/
|
||||
int grid_height(const grid *gp);
|
||||
|
||||
|
||||
/* Kijk of de gegeven positie binnen het rooster valt.
|
||||
*
|
||||
* gp: een pointer naar het rooster.
|
||||
* x,y: de positie.
|
||||
*
|
||||
* Uitvoer: 1 als de positie binnen het rooster valt, anders 0.
|
||||
*/
|
||||
int grid_contains(const grid *gp, int x, int y);
|
||||
|
||||
|
||||
/* Kijk welk object er staat op een bepaalde positie in het rooster.
|
||||
*
|
||||
* gp : een pointer naar het rooster
|
||||
* x,y: de betreffende positie.
|
||||
*
|
||||
* Uitvoer: het object op die positie, of '\0' als de positie buiten het
|
||||
* rooster valt.
|
||||
*/
|
||||
char grid_fetch(const grid *gp, int x, int y);
|
||||
|
||||
|
||||
/* Schrijf een bepaald object op een bepaalde positie in het rooster.
|
||||
*
|
||||
* gp : een pointer naar het rooster
|
||||
* x,y: de betreffende positie.
|
||||
* c : het object.
|
||||
*
|
||||
* Effect: als (x,y) binnen het rooster ligt, wordt het object op
|
||||
* de opgegeven positie geplaatst, anders verandert er niets.
|
||||
*
|
||||
* Uitvoer: 1 als het object is geplaatst, of 0 als het buiten de grenzen lag.
|
||||
*/
|
||||
int grid_put(grid *gp, int x, int y, char c);
|
||||
|
||||
|
||||
/* Zoek een bepaald object in het rooster, en geef de coordinaten van het
|
||||
* object terug via de gegeven pointers. Let op: als er meerdere objecten van
|
||||
* het gezochte soort in het rooster voorkomen, is niet gedefinieerd van welke
|
||||
* de positie wordt gevonden.
|
||||
*
|
||||
* gp : een pointer naar het rooster
|
||||
* c : het object dat moet worden gezocht
|
||||
* x,y: pointers naar de geheugenlocaties waar de gevonden positie moet worden
|
||||
* geschreven.
|
||||
*
|
||||
* Uitvoer: via de pointers x en y. Als het object niet wordt gevonden worden
|
||||
* *x en *y op -1 gezet.
|
||||
*/
|
||||
void grid_find(const grid *gp, char c, int *x, int *y);
|
||||
|
||||
#endif //_GRID_H
|
||||
@@ -10,8 +10,6 @@
|
||||
#include <ncurses.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rooster.h"
|
||||
|
||||
int same_coordinate(const coordinate a, const coordinate b) {
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
@@ -24,23 +22,23 @@ int modulo(const int number, const int mod) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void show_grid_on_offset(const rooster *gp, const int starting_x, const int starting_y) {
|
||||
const int height = rooster_hoogte(gp);
|
||||
void show_grid_on_offset(const grid *gp, const int starting_x, const int starting_y) {
|
||||
const int height = grid_height(gp);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
char *rij = rooster_vraag_rij(gp, y);
|
||||
char *rij = grid_fetch_row(gp, y);
|
||||
mvprintw(starting_y + y, starting_x, "%s", rij);
|
||||
free(rij);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void show_grid(const rooster *gp) {
|
||||
void show_grid(const grid *gp) {
|
||||
show_grid_on_offset(gp, 0, 0);
|
||||
}
|
||||
|
||||
void update_grid(rooster *gp, const char c, const int x, const int y) {
|
||||
if (rooster_plaats(gp, x, y, c) == 1) {
|
||||
void update_grid(grid *gp, const char c, const int x, const int y) {
|
||||
if (grid_put(gp, x, y, c) == 1) {
|
||||
mvaddch(y, x, c);
|
||||
}
|
||||
}
|
||||
@@ -52,9 +50,7 @@ void update_grid(rooster *gp, const char c, const int x, const int y) {
|
||||
* Clears the console and prints the victory message.
|
||||
*/
|
||||
static void display_victory(const coordinate location) {
|
||||
erase();
|
||||
mvprintw(location.y, location.x, "YOU WON!!!!!");
|
||||
refresh();
|
||||
mvaddstr(location.y, location.x, "YOU WON!!!!!");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -64,9 +60,7 @@ static void display_victory(const coordinate location) {
|
||||
* Clears the console and prints the GAME OVER message.
|
||||
*/
|
||||
static void display_loss(const coordinate location) {
|
||||
erase();
|
||||
mvprintw(location.y, location.x, "GAME OVER");
|
||||
refresh();
|
||||
mvaddstr(location.y, location.x, "GAME OVER");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -76,9 +70,7 @@ static void display_loss(const coordinate location) {
|
||||
* Clears the console and prints the quit message.
|
||||
*/
|
||||
static void display_quit(const coordinate location) {
|
||||
erase();
|
||||
mvprintw(location.y, location.x, "You quit the game");
|
||||
refresh();
|
||||
mvaddstr(location.y, location.x, "You quit the game");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -88,15 +80,15 @@ static void display_quit(const coordinate location) {
|
||||
* Clears the console and prints the hacker man message.
|
||||
*/
|
||||
static void display_hackerman(const coordinate location) {
|
||||
erase();
|
||||
mvprintw(location.y, location.x, "The hacker man strikes again...");
|
||||
refresh();
|
||||
mvaddstr(location.y, location.x, "The hacker man strikes again...");
|
||||
}
|
||||
|
||||
void graceful_exit(const coordinate message_location) {
|
||||
mvprintw(message_location.y, message_location.x, "Press ENTER or SPACE to exit.");
|
||||
mvaddstr(message_location.y, message_location.x, "Press ENTER or SPACE to exit.");
|
||||
while (1) {
|
||||
switch (getch()) {
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_ESCAPE:
|
||||
case KEY_ENTER:
|
||||
case '\n':
|
||||
case ' ':
|
||||
@@ -105,8 +97,8 @@ void graceful_exit(const coordinate message_location) {
|
||||
}
|
||||
}
|
||||
|
||||
void game_exit_screen(const rooster *gp, coordinate location) {
|
||||
const toestand current_state = rooster_vraag_toestand(gp);
|
||||
void game_exit_message(const grid *gp, coordinate location) {
|
||||
const state current_state = grid_fetch_state(gp);
|
||||
switch (current_state) {
|
||||
case STATE_GEWONNEN:
|
||||
display_victory(location);
|
||||
@@ -123,9 +115,24 @@ void game_exit_screen(const rooster *gp, coordinate location) {
|
||||
|
||||
location.y += 2;
|
||||
graceful_exit(location);
|
||||
refresh();
|
||||
}
|
||||
|
||||
static void init_ncurses() {
|
||||
void enable_highlight(const game_colors color) {
|
||||
attron(COLOR_PAIR(color));
|
||||
}
|
||||
|
||||
void disable_highlight(const game_colors color) {
|
||||
attroff(COLOR_PAIR(color));
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ncurses.
|
||||
*
|
||||
* This enables cbreak, noecho, hides the cursor and enables the extra keys.
|
||||
* This also creates the color pairs needed for game_colors and sets ESCDELAY to 0.
|
||||
*/
|
||||
static void init_ncurses(void) {
|
||||
ESCDELAY = 0;
|
||||
setlocale(LC_ALL, "");
|
||||
initscr();
|
||||
@@ -134,9 +141,6 @@ static void init_ncurses() {
|
||||
noecho(); // Don't write the keyboard input to the console.
|
||||
curs_set(0); // Hides the cursor.
|
||||
|
||||
// mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); // Don't mask any mouse events
|
||||
// printf("\033[?1003h\n"); // Makes the terminal report mouse movement events
|
||||
|
||||
start_color();
|
||||
init_pair(BLACK, COLOR_BLACK, COLOR_BLACK);
|
||||
init_pair(WHITE, COLOR_BLACK, COLOR_WHITE);
|
||||
@@ -150,14 +154,10 @@ static void init_ncurses() {
|
||||
clear();
|
||||
}
|
||||
|
||||
static void cleanup_ncurses() {
|
||||
endwin();
|
||||
}
|
||||
|
||||
void init_engine(void) {
|
||||
init_ncurses();
|
||||
}
|
||||
|
||||
void cleanup_engine(void) {
|
||||
cleanup_ncurses();
|
||||
endwin();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#ifndef MINIGAME_MENU_GRID_GAME_ENGINE_H
|
||||
#define MINIGAME_MENU_GRID_GAME_ENGINE_H
|
||||
#include "rooster.h"
|
||||
#include "grid.h"
|
||||
|
||||
#define KEY_ESCAPE 27
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
char name[100];
|
||||
rooster *game_map;
|
||||
grid *game_map;
|
||||
} game_maps;
|
||||
|
||||
/*
|
||||
@@ -63,7 +63,7 @@ int modulo(int number, int mod);
|
||||
* Side effect:
|
||||
* The console is cleared and the grid is printed.
|
||||
*/
|
||||
void show_grid_on_offset(const rooster *gp, int starting_x, int starting_y);
|
||||
void show_grid_on_offset(const grid *gp, int starting_x, int starting_y);
|
||||
|
||||
/*
|
||||
* Displays the given grid with ncurses.
|
||||
@@ -74,7 +74,37 @@ void show_grid_on_offset(const rooster *gp, int starting_x, int starting_y);
|
||||
* Side effect:
|
||||
* The console is cleared and the grid is printed.
|
||||
*/
|
||||
void show_grid(const rooster *gp);
|
||||
void show_grid(const grid *gp);
|
||||
|
||||
/*
|
||||
* Turn on color highlighting for ncurses
|
||||
*
|
||||
* Input:
|
||||
* color: The color you want the highlight to be.
|
||||
*
|
||||
* Side Effects:
|
||||
* Enables color highlighting in ncurses.
|
||||
*
|
||||
* Note:
|
||||
* This should always be disabled with disable_highlights before being called again.
|
||||
* Not disabling the current highlight before enabling another one is undefined behaviour.
|
||||
*/
|
||||
void enable_highlight(game_colors color);
|
||||
|
||||
/*
|
||||
* Turn off color highlighting for ncurses
|
||||
*
|
||||
* Input:
|
||||
* color: The color highlighting you want to turn off.
|
||||
*
|
||||
* Side Effects:
|
||||
* Enables color highlighting in ncurses.
|
||||
*
|
||||
* Note:
|
||||
* This should always be enabled with enable_highlights before being called.
|
||||
* Disabling the highlight before enabling it is undefined behaviour.
|
||||
*/
|
||||
void disable_highlight(game_colors color);
|
||||
|
||||
/*
|
||||
* Updates a single location in the grid.
|
||||
@@ -88,7 +118,7 @@ void show_grid(const rooster *gp);
|
||||
* Side effect:
|
||||
* The update gets applied both on the grid and on the screen.
|
||||
*/
|
||||
void update_grid(rooster *gp, char c, int x, int y);
|
||||
void update_grid(grid *gp, char c, int x, int y);
|
||||
|
||||
/*
|
||||
* Display the ending screen that matches the end state of the grid.
|
||||
@@ -100,7 +130,7 @@ void update_grid(rooster *gp, char c, int x, int y);
|
||||
* Side Effects:
|
||||
* The end of game screen gets displayed with a graceful exit.
|
||||
*/
|
||||
void game_exit_screen(const rooster *gp, coordinate location);
|
||||
void game_exit_message(const grid *gp, coordinate location);
|
||||
|
||||
/*
|
||||
* Waits for you to press ENTER or SPACE before exiting the game.
|
||||
|
||||
18
manual.c
18
manual.c
@@ -5,8 +5,7 @@
|
||||
#include "manual.h"
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "rooster.h"
|
||||
#include "grid_game_engine.h"
|
||||
|
||||
void manual(const coordinate display_location) {
|
||||
erase();
|
||||
@@ -15,19 +14,22 @@ void manual(const coordinate display_location) {
|
||||
return;
|
||||
}
|
||||
|
||||
rooster *grid = grid_from_file(fp);
|
||||
grid *grid = grid_create_from_file(fp);
|
||||
if (grid == NULL) {
|
||||
mvaddstr(display_location.y, display_location.x, "Error loading grid");
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
show_grid_on_offset(grid, display_location.x, display_location.y);
|
||||
|
||||
rooster_klaar(grid);
|
||||
// Wait until ESCAPE or BACKSPACE is pressed.
|
||||
timeout(200);
|
||||
for (int ch = getch(); ch != KEY_ESCAPE && ch != KEY_BACKSPACE && ch != ' '; ch = getch()) {
|
||||
// Update the screen in the meantime to accommodate windows resizes.
|
||||
show_grid_on_offset(grid, display_location.x, display_location.y);
|
||||
}
|
||||
|
||||
int ch = getch();
|
||||
while (ch != KEY_ESCAPE && ch != KEY_BACKSPACE && ch != ' ') {
|
||||
ch = getch();
|
||||
}
|
||||
grid_cleanup(grid);
|
||||
}
|
||||
|
||||
100
maze_runner.c
100
maze_runner.c
@@ -8,37 +8,43 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "grid_game_engine.h"
|
||||
#include "rooster.h"
|
||||
|
||||
#define EMPTY ' '
|
||||
#define WALL '#'
|
||||
#define LIVING_PLAYER '*'
|
||||
#define DEAD_PLAYER '@'
|
||||
#define MAZE_EXIT '$'
|
||||
#define TRAP 'X'
|
||||
|
||||
/*
|
||||
* Reads in the maze from the assets file.
|
||||
*
|
||||
* Side Effects:
|
||||
* Memory is allocated to store the grid in.
|
||||
*/
|
||||
static rooster *get_maze(void) {
|
||||
// TODO: echt opties aanbieden in plaats van hardcoded 1 maze.
|
||||
// Alternatief is om random een maze te genereren. dit is miss beter.
|
||||
|
||||
// 2. Open het doolhof bestand en lees het rooster.
|
||||
static grid *get_maze(void) {
|
||||
// Open het doolhof bestand en lees het rooster.
|
||||
FILE *fh = fopen("assets/maze.txt", "r");
|
||||
if (fh == NULL) {
|
||||
perror("loading maze");
|
||||
return NULL;
|
||||
}
|
||||
rooster *rp = grid_from_file(fh);
|
||||
grid *gp = grid_create_from_file(fh);
|
||||
fclose(fh);
|
||||
|
||||
// 3. Bepaal of het lezen van het rooster is gelukt.
|
||||
if (rp == NULL) {
|
||||
// Bepaal of het lezen van het rooster is gelukt.
|
||||
if (gp == NULL) {
|
||||
fprintf(stderr, "Kan rooster niet maken.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rp;
|
||||
return gp;
|
||||
}
|
||||
|
||||
/* Voert de benodigde veranderingen in het rooster door als de speler in een
|
||||
* bepaalde richting probeert te bewegen.
|
||||
* Input:
|
||||
* rp : een pointer naar het rooster
|
||||
* gp : een pointer naar het rooster
|
||||
* dx,dy: de richting waarin de speler probeert te bewegen. De vier mogelijk-
|
||||
* heden voor (dx,dy) zijn (-1,0), (1,0), (0,-1), (0,1) voor resp.
|
||||
* links, rechts, omhoog en omlaag.
|
||||
@@ -46,33 +52,35 @@ static rooster *get_maze(void) {
|
||||
* Side effect: het rooster wordt aangepast op basis van de handeling van
|
||||
* de speler.
|
||||
*/
|
||||
static void maze_runner_beweeg(rooster *rp, int dx, int dy) {
|
||||
int playerx;
|
||||
int playery;
|
||||
rooster_zoek(rp, '*', &playerx, &playery);
|
||||
static void maze_runner_move(grid *gp, const int dx, const int dy) {
|
||||
coordinate player_position = {0, 0};
|
||||
grid_find(gp, LIVING_PLAYER, &player_position.x, &player_position.y);
|
||||
|
||||
if (playerx == -1 || playery == -1) {
|
||||
if (player_position.y == -1) {
|
||||
printf("Player not found!");
|
||||
rooster_zet_toestand(rp, STATE_BEGIN);
|
||||
grid_put_state(gp, STATE_BEGIN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rooster_bevat(rp, playerx + dx, playery + dy) == 1) {
|
||||
char new_location = rooster_kijk(rp, playerx + dx, playery + dy);
|
||||
if (grid_contains(gp, player_position.x + dx, player_position.y + dy) == 1) {
|
||||
char new_location = grid_fetch(gp, player_position.x + dx, player_position.y + dy);
|
||||
switch (new_location) {
|
||||
case '#':
|
||||
case WALL:
|
||||
break;
|
||||
case 'X':
|
||||
update_grid(rp, ' ', playerx, playery);
|
||||
rooster_zet_toestand(rp, STATE_VERLOREN);
|
||||
case TRAP:
|
||||
grid_put_state(gp, STATE_VERLOREN);
|
||||
|
||||
enable_highlight(RED);
|
||||
update_grid(gp, DEAD_PLAYER, player_position.x, player_position.y);
|
||||
disable_highlight(RED);
|
||||
break;
|
||||
case ' ':
|
||||
update_grid(rp, ' ', playerx, playery);
|
||||
update_grid(rp, '*', playerx + dx, playery + dy);
|
||||
case EMPTY:
|
||||
update_grid(gp, EMPTY, player_position.x, player_position.y);
|
||||
update_grid(gp, LIVING_PLAYER, player_position.x + dx, player_position.y + dy);
|
||||
break;
|
||||
case '$':
|
||||
update_grid(rp, ' ', playerx, playery);
|
||||
rooster_zet_toestand(rp, STATE_GEWONNEN);
|
||||
case MAZE_EXIT:
|
||||
update_grid(gp, EMPTY, player_position.x, player_position.y);
|
||||
grid_put_state(gp, STATE_GEWONNEN);
|
||||
break;
|
||||
}
|
||||
refresh();
|
||||
@@ -82,52 +90,58 @@ static void maze_runner_beweeg(rooster *rp, int dx, int dy) {
|
||||
/*
|
||||
* Speelt het spel met een gegeven rooster tot de toestand niet langer
|
||||
* AAN_HET_SPELEN is.
|
||||
*
|
||||
* Input:
|
||||
* gp: Een pointer naar het rooster.
|
||||
*
|
||||
* Side effects:
|
||||
* Het rooster wordt ge-updated afhankelijk van de user input.
|
||||
*/
|
||||
static void speel_maze(rooster *rp) {
|
||||
static void speel_maze(grid *gp) {
|
||||
switch (getch()) {
|
||||
case KEY_UP: // fallthrough
|
||||
case 'w':
|
||||
case 'W':
|
||||
maze_runner_beweeg(rp, 0, -1);
|
||||
maze_runner_move(gp, 0, -1);
|
||||
break;
|
||||
case KEY_DOWN: // fallthrough
|
||||
case 's':
|
||||
case 'S':
|
||||
maze_runner_beweeg(rp, 0, 1);
|
||||
maze_runner_move(gp, 0, 1);
|
||||
break;
|
||||
case KEY_LEFT: // fallthrough
|
||||
case 'a':
|
||||
case 'A':
|
||||
maze_runner_beweeg(rp, -1, 0);
|
||||
maze_runner_move(gp, -1, 0);
|
||||
break;
|
||||
case KEY_RIGHT: // fallthrough
|
||||
case 'd':
|
||||
case 'D':
|
||||
maze_runner_beweeg(rp, 1, 0);
|
||||
maze_runner_move(gp, 1, 0);
|
||||
break;
|
||||
case 'p':
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_ESCAPE:
|
||||
rooster_zet_toestand(rp, STATE_QUIT);
|
||||
grid_put_state(gp, STATE_QUIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void maze_runner(void) {
|
||||
// Voorbereiding.
|
||||
rooster *rp = get_maze();
|
||||
if (rp == NULL) {
|
||||
grid *gp = get_maze();
|
||||
if (gp == NULL) {
|
||||
return;
|
||||
}
|
||||
show_grid(rp);
|
||||
rooster_zet_toestand(rp, STATE_AAN_HET_SPELEN);
|
||||
show_grid(gp);
|
||||
grid_put_state(gp, STATE_AAN_HET_SPELEN);
|
||||
|
||||
// Game zelf.
|
||||
while (rooster_vraag_toestand(rp) == STATE_AAN_HET_SPELEN) {
|
||||
speel_maze(rp);
|
||||
while (grid_fetch_state(gp) == STATE_AAN_HET_SPELEN) {
|
||||
speel_maze(gp);
|
||||
}
|
||||
|
||||
// Exit game.
|
||||
game_exit_screen(rp, (coordinate){0, 0});
|
||||
rooster_klaar(rp);
|
||||
game_exit_message(gp, (coordinate){0, grid_height(gp) + 2});
|
||||
grid_cleanup(gp);
|
||||
}
|
||||
|
||||
@@ -11,20 +11,19 @@
|
||||
#include "manual.h"
|
||||
#include "maze_runner.h"
|
||||
#include "minesweeper.h"
|
||||
#include "rooster.h"
|
||||
#include "snake.h"
|
||||
|
||||
#define AMOUNT_OF_MENU_OPTIONS 5
|
||||
|
||||
typedef enum {
|
||||
GAME_HELP = 0,
|
||||
GAME_MANUAL = 0,
|
||||
GAME_MAZE_RUNNER = 1,
|
||||
GAME_SNAKE = 2,
|
||||
GAME_MINESWEEPER = 3,
|
||||
GAME_QUIT = 4,
|
||||
} game;
|
||||
|
||||
static game SELECTED_GAME = GAME_HELP;
|
||||
static game SELECTED_GAME = GAME_MANUAL;
|
||||
static int OFFSET_Y = 5;
|
||||
static int OFFSET_X = 5;
|
||||
|
||||
@@ -37,7 +36,7 @@ static int OFFSET_X = 5;
|
||||
*/
|
||||
static void launch_game(const game game) {
|
||||
switch (game) {
|
||||
case GAME_HELP:
|
||||
case GAME_MANUAL:
|
||||
manual((coordinate){0,0});
|
||||
break;
|
||||
case GAME_MAZE_RUNNER:
|
||||
@@ -65,16 +64,16 @@ static void launch_game(const game game) {
|
||||
* If a valid menu option is provided: It will be highlighted in green.
|
||||
* If an invalid menu option is provided: Nothing happens
|
||||
*/
|
||||
static void menu_highlight(const rooster *menu) {
|
||||
static void menu_highlight(const grid *menu) {
|
||||
switch (SELECTED_GAME) {
|
||||
case GAME_HELP:
|
||||
case GAME_MANUAL:
|
||||
case GAME_MAZE_RUNNER:
|
||||
case GAME_SNAKE:
|
||||
case GAME_MINESWEEPER:
|
||||
case GAME_QUIT:
|
||||
attron(COLOR_PAIR(GREEN));
|
||||
|
||||
char* row = rooster_vraag_rij(menu, SELECTED_GAME);
|
||||
char* row = grid_fetch_row(menu, SELECTED_GAME);
|
||||
mvprintw(OFFSET_Y + (int)SELECTED_GAME, OFFSET_X, "%s", row);
|
||||
free(row);
|
||||
|
||||
@@ -94,7 +93,7 @@ static void menu_highlight(const rooster *menu) {
|
||||
* Side Effects:
|
||||
* Displays the menu
|
||||
*/
|
||||
static void show_menu(const rooster *menu) {
|
||||
static void show_menu(const grid *menu) {
|
||||
erase();
|
||||
show_grid_on_offset(menu, OFFSET_X, OFFSET_Y);
|
||||
menu_highlight(menu);
|
||||
@@ -167,19 +166,19 @@ static int navigate_menu(void) {
|
||||
* Output:
|
||||
* A pointer to the menu grid.
|
||||
*/
|
||||
static rooster *initialize_menu(void) {
|
||||
static grid *initialize_menu(void) {
|
||||
char menu[] = "How to play\n"
|
||||
"Maze Runner\n"
|
||||
" Snake \n"
|
||||
"Minesweeper\n"
|
||||
" Leave \n";
|
||||
rooster *rp = grid_from_string(menu);
|
||||
grid *rp = grid_create_from_string(menu);
|
||||
return rp;
|
||||
}
|
||||
|
||||
void minigame_menu(void) {
|
||||
rooster *menu = initialize_menu();
|
||||
|
||||
grid *menu = initialize_menu();
|
||||
launch_game(GAME_MANUAL);
|
||||
while (true) {
|
||||
show_menu(menu);
|
||||
if (navigate_menu() == 1) {
|
||||
@@ -187,5 +186,5 @@ void minigame_menu(void) {
|
||||
}
|
||||
}
|
||||
|
||||
rooster_klaar(menu);
|
||||
grid_cleanup(menu);
|
||||
}
|
||||
|
||||
177
rooster.h
177
rooster.h
@@ -1,177 +0,0 @@
|
||||
/* rooster.h
|
||||
|
||||
Deze module verzorgt het datatype "rooster". Een rooster representeert een
|
||||
rechthoekig grid van objecten. Elk object is in dit rooster een char.
|
||||
|
||||
Deze header file beschrijft het interface voor "rooster".
|
||||
De implementatie, in "rooster.c", moet je grotendeels zelf schrijven.
|
||||
*/
|
||||
|
||||
#ifndef _ROOSTER_H
|
||||
#define _ROOSTER_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Dankzij de typedef hoef je niet telkens "struct rooster_data" te schrijven.
|
||||
// Definieer struct rooster_data in rooster.c.
|
||||
struct rooster_data;
|
||||
typedef struct rooster_data rooster;
|
||||
|
||||
typedef enum {
|
||||
STATE_BEGIN,
|
||||
STATE_AAN_HET_SPELEN,
|
||||
STATE_GEWONNEN,
|
||||
STATE_VERLOREN,
|
||||
STATE_QUIT
|
||||
} toestand;
|
||||
|
||||
/* Maak een rooster op basis van de data in de gegeven stream.
|
||||
|
||||
fh: de stream waaruit het doolhof gelezen moet worden.
|
||||
|
||||
Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is
|
||||
gealloceerd), dat overeenkomt met de gegeven beschrijving.
|
||||
De begintoestand is BEGIN.
|
||||
|
||||
Als de beschrijving niet consistent is (bijvoorbeeld
|
||||
niet alle rijen zijn even lang, of er klopt iets anders niet), of
|
||||
als niet voldoende geheugen kan worden gereserveerd, dan wordt
|
||||
NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen
|
||||
achter.)
|
||||
*/
|
||||
rooster *grid_from_file(FILE *fh);
|
||||
|
||||
/*
|
||||
* Maak een rooster op basis van een gegeven string.
|
||||
*
|
||||
* Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is
|
||||
* gealloceerd), dat overeenkomt met de gegeven beschrijving.
|
||||
* De begintoestand is BEGIN.
|
||||
*
|
||||
* Als de beschrijving niet consistent is (bijvoorbeeld
|
||||
* niet alle rijen zijn even lang, of er klopt iets anders niet), of
|
||||
* als niet voldoende geheugen kan worden gereserveerd, dan wordt
|
||||
* NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen
|
||||
* achter.)
|
||||
*/
|
||||
rooster *grid_from_string(const char* input);
|
||||
|
||||
/*
|
||||
* Haal een rij uit het rooster op.
|
||||
*
|
||||
* Input:
|
||||
* rp: een pointer naar het rooster
|
||||
* y: de y-coordinaat van de rij die je wilt hebben.
|
||||
*
|
||||
* Output:
|
||||
* Een pointer naar een nieuwe string met daarin alle karakters in die rij.
|
||||
*/
|
||||
char *rooster_vraag_rij(const rooster *rp, int y);
|
||||
|
||||
/*
|
||||
* Maak een kopie van een rooster
|
||||
*
|
||||
* Input:
|
||||
* rp: Een pointer naar het rooster.
|
||||
*
|
||||
* Output:
|
||||
* Een pointer naar het kopie.
|
||||
*/
|
||||
rooster *rooster_kopieer(const rooster *rp);
|
||||
|
||||
/* Vraag de huidige toestand van het spel op.
|
||||
|
||||
Input:
|
||||
rp: een pointer naar het rooster.
|
||||
|
||||
Uitvoer: de toestand.
|
||||
*/
|
||||
toestand rooster_vraag_toestand(const rooster *rp);
|
||||
|
||||
|
||||
/* Verander de huidige toestand van het spel.
|
||||
|
||||
rp: een pointer naar het rooster.
|
||||
t: de nieuwe toestand.
|
||||
*/
|
||||
void rooster_zet_toestand(rooster *rp, toestand t);
|
||||
|
||||
|
||||
/* Geef alle resources vrij die zijn gealloceerd voor een rooster.
|
||||
De rooster pointer is na aanroep van deze functie niet meer bruikbaar.
|
||||
|
||||
rp: een pointer naar het rooster.
|
||||
*/
|
||||
void rooster_klaar(rooster *rp);
|
||||
|
||||
|
||||
/* Vraag de breedte van het rooster op, dat wil zeggen, het aantal kolommen.
|
||||
|
||||
rp: een pointer naar het rooster.
|
||||
|
||||
Uitvoer: de breedte.
|
||||
*/
|
||||
int rooster_breedte(const rooster *rp);
|
||||
|
||||
|
||||
/* Vraag de hoogte van het rooster op, dat wil zeggen, het aantal rijen.
|
||||
|
||||
rp: een pointer naar het rooster.
|
||||
|
||||
Uitvoer: de hoogte.
|
||||
*/
|
||||
int rooster_hoogte(const rooster *rp);
|
||||
|
||||
|
||||
/* Kijk of de gegeven positie binnen het rooster valt.
|
||||
|
||||
rp: een pointer naar het rooster.
|
||||
x,y: de positie.
|
||||
|
||||
Uitvoer: 1 als de positie binnen het rooster valt, anders 0.
|
||||
*/
|
||||
int rooster_bevat(const rooster *rp, int x, int y);
|
||||
|
||||
|
||||
/* Kijk welk object er staat op een bepaalde positie in het rooster.
|
||||
|
||||
rp : een pointer naar het rooster
|
||||
x,y: de betreffende positie.
|
||||
|
||||
Uitvoer: het object op die positie, of '\0' als de positie buiten het
|
||||
rooster valt.
|
||||
*/
|
||||
char rooster_kijk(const rooster *rp, int x, int y);
|
||||
|
||||
|
||||
/* Schrijf een bepaald object op een bepaalde positie in het rooster.
|
||||
|
||||
rp : een pointer naar het rooster
|
||||
x,y: de betreffende positie.
|
||||
c : het object.
|
||||
|
||||
Effect: als (x,y) binnen het rooster ligt, wordt het object op
|
||||
de opgegeven positie geplaatst, anders verandert er niets.
|
||||
|
||||
Uitvoer: 1 als het object is geplaatst, of 0 als het buiten de grenzen lag.
|
||||
*/
|
||||
int rooster_plaats(rooster *rp, int x, int y, char c);
|
||||
|
||||
|
||||
/* Zoek een bepaald object in het rooster, en geef de coordinaten van het
|
||||
object terug via de gegeven pointers. Let op: als er meerdere objecten van
|
||||
het gezochte soort in het rooster voorkomen, is niet gedefinieerd van welke
|
||||
de positie wordt gevonden.
|
||||
|
||||
rp : een pointer naar het rooster
|
||||
c : het object dat moet worden gezocht
|
||||
x,y: pointers naar de geheugenlocaties waar de gevonden positie moet worden
|
||||
geschreven.
|
||||
|
||||
Uitvoer: via de pointers x en y. Als het object niet wordt gevonden worden
|
||||
*x en *y op -1 gezet.
|
||||
*/
|
||||
void rooster_zoek(const rooster *rp, char c, int *x, int *y);
|
||||
|
||||
|
||||
#endif
|
||||
262
snake.c
262
snake.c
@@ -34,15 +34,19 @@ typedef enum {
|
||||
DIRECTION_LEFT = 3
|
||||
} direction;
|
||||
|
||||
static direction PREVIOUS_DIRECTION = DIRECTION_DOWN;
|
||||
static direction CURRENT_DIRECTION = DIRECTION_DOWN;
|
||||
// Snake globals
|
||||
static direction PREVIOUS_DIRECTION;
|
||||
static direction CURRENT_DIRECTION;
|
||||
static coordinate SNAKE_HEAD;
|
||||
static coordinate SNAKE_TAIL;
|
||||
|
||||
// Map globals
|
||||
static coordinate RENDER_AT;
|
||||
static coordinate MESSAGE_LOCATION = {0,0};
|
||||
static int MAP_HEIGHT = 20;
|
||||
static int MAP_WIDTH = 20;
|
||||
static rooster *GRID;
|
||||
static pthread_mutex_t MUTEX;
|
||||
static grid *GRID;
|
||||
static pthread_mutex_t SNAKE_MUTEX;
|
||||
|
||||
/*
|
||||
* Create a snake body part.
|
||||
@@ -54,7 +58,7 @@ static pthread_mutex_t MUTEX;
|
||||
* a character representing that body part.
|
||||
*/
|
||||
static char get_body_part(const direction dir) {
|
||||
return (char)dir + '0';
|
||||
return (char)(dir + '0');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -89,15 +93,15 @@ static void create_grid(void) {
|
||||
|
||||
for (int i = 1; i <= (MAP_WIDTH + 1) * MAP_HEIGHT; i++) {
|
||||
// Also subtract the null terminator
|
||||
int bottom_line = i > grid_size - (MAP_WIDTH + 2);
|
||||
int top_line = i < MAP_WIDTH + 1;
|
||||
const int bottom_line = i > grid_size - (MAP_WIDTH + 2);
|
||||
const int top_line = i < MAP_WIDTH + 1;
|
||||
|
||||
int line_position = modulo(i, MAP_WIDTH + 1);
|
||||
const int line_position = modulo(i, MAP_WIDTH + 1);
|
||||
|
||||
int line_start = line_position == 1;
|
||||
int line_end = line_position == MAP_WIDTH;
|
||||
const int line_start = line_position == 1;
|
||||
const int line_end = line_position == MAP_WIDTH;
|
||||
|
||||
int newline = line_position == 0;
|
||||
const int newline = line_position == 0;
|
||||
|
||||
if (newline) {
|
||||
map[i - 1] = '\n';
|
||||
@@ -110,36 +114,35 @@ static void create_grid(void) {
|
||||
|
||||
map[grid_size - 1] = '\0';
|
||||
|
||||
GRID = grid_from_string(map);
|
||||
free(map);
|
||||
GRID = grid_create_from_string(map);
|
||||
|
||||
rooster_plaats(GRID, rooster_breedte(GRID) / 2, rooster_hoogte(GRID) / 2, get_body_part(CURRENT_DIRECTION));
|
||||
free(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* Spawn a piece of food at an empty grid location.
|
||||
*
|
||||
* Side Effect:
|
||||
* One of the empty grid spaces gets replaced with a piece of food.
|
||||
*/
|
||||
static void generate_food() {
|
||||
static void generate_food(void) {
|
||||
coordinate empty_spots[MAP_HEIGHT * MAP_WIDTH];
|
||||
|
||||
// Usable as index when initialized like this.
|
||||
int available_spots = 0;
|
||||
|
||||
for (int x = 0; x < MAP_WIDTH; x++) {
|
||||
for (int y = 0; y < MAP_HEIGHT; y++) {
|
||||
if (rooster_kijk(GRID, x, y) == CELL_EMPTY) {
|
||||
coordinate new_spot = {x, y};
|
||||
if (grid_fetch(GRID, x, y) == CELL_EMPTY) {
|
||||
const coordinate new_spot = {x, y};
|
||||
empty_spots[available_spots] = new_spot;
|
||||
available_spots++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Available spots will now be a counter.
|
||||
|
||||
const coordinate food_location = empty_spots[modulo(rand(), available_spots)];
|
||||
|
||||
rooster_plaats(GRID, food_location.x, food_location.y, CELL_FOOD);
|
||||
grid_put(GRID, food_location.x, food_location.y, CELL_FOOD);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -150,34 +153,33 @@ static void generate_food() {
|
||||
*
|
||||
* Side Effects:
|
||||
* Seed random with the current time.
|
||||
* (Re)set the global variables.
|
||||
* initialize the mutex
|
||||
*
|
||||
*/
|
||||
static void initialize(void) {
|
||||
// Seed random.
|
||||
srand(time(NULL));
|
||||
|
||||
// Create the grid.
|
||||
create_grid();
|
||||
|
||||
if (GRID == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set globals.
|
||||
CURRENT_DIRECTION = DIRECTION_DOWN;
|
||||
PREVIOUS_DIRECTION = DIRECTION_DOWN;
|
||||
SNAKE_HEAD = (coordinate){.x = grid_width(GRID) / 2, .y = grid_height(GRID) / 2};
|
||||
SNAKE_TAIL = SNAKE_HEAD;
|
||||
RENDER_AT = (coordinate){.x = OFFSET_X, .y = OFFSET_Y};
|
||||
MESSAGE_LOCATION = (coordinate){.x = RENDER_AT.x, .y = RENDER_AT.y + grid_height(GRID) + 2};
|
||||
|
||||
// Set snake head and snake tail.
|
||||
rooster_zoek(GRID, get_body_part(CURRENT_DIRECTION), &SNAKE_HEAD.x, &SNAKE_HEAD.y);
|
||||
SNAKE_TAIL.x = SNAKE_HEAD.x;
|
||||
SNAKE_TAIL.y = SNAKE_HEAD.y;
|
||||
|
||||
// Create the first body part and spawn the first piece of food.
|
||||
grid_put(GRID, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(CURRENT_DIRECTION));
|
||||
generate_food();
|
||||
|
||||
if (SNAKE_HEAD.x == -1) {
|
||||
free(GRID);
|
||||
GRID = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
MESSAGE_LOCATION.y = rooster_hoogte(GRID) + OFFSET_Y + 5;
|
||||
MESSAGE_LOCATION.x = OFFSET_X - 5 >= 0 ? OFFSET_X - 5 : 0;
|
||||
pthread_mutex_init(&SNAKE_MUTEX, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -187,11 +189,11 @@ static void initialize(void) {
|
||||
* c: The char to check.
|
||||
*
|
||||
* Returns:
|
||||
* The snake will move forward: 0
|
||||
* The snake will eat an apple: 1
|
||||
* The snake will die: 2
|
||||
* The snake will move forward: SNAKE_MOVE
|
||||
* The snake will eat an apple: SNAKE_EAT
|
||||
* The snake will die: SNAKE_DIE
|
||||
*/
|
||||
static snake_action collision_check(char c) {
|
||||
static snake_action collision_check(const char c) {
|
||||
if (c == CELL_EMPTY) {
|
||||
return SNAKE_MOVE;
|
||||
}
|
||||
@@ -203,16 +205,32 @@ static snake_action collision_check(char c) {
|
||||
return SNAKE_DIE;
|
||||
}
|
||||
|
||||
static void update_snake(coordinate new_head, snake_action action) {
|
||||
/*
|
||||
* Move the snake to a given location
|
||||
*
|
||||
* Input:
|
||||
* new_location: The location the snake should move to.
|
||||
*
|
||||
* Side effects:
|
||||
* In case nothing is encountered: The snake moves to the new location.
|
||||
* In case an obstacle is encountered (wall or part of the snake): The snake dies.
|
||||
* In case a piece of food is encountered: The snake eats the food and grows by 1.
|
||||
*
|
||||
* Note:
|
||||
* Moving to a location the snake can't reach is undefined behaviour.
|
||||
*/
|
||||
static void update_snake(const coordinate new_location) {
|
||||
const snake_action action = collision_check(grid_fetch(GRID, new_location.x, new_location.y));
|
||||
|
||||
if (action == SNAKE_DIE) {
|
||||
rooster_zet_toestand(GRID, STATE_VERLOREN);
|
||||
grid_put_state(GRID, STATE_VERLOREN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == SNAKE_MOVE) {
|
||||
coordinate new_tail = SNAKE_TAIL;
|
||||
|
||||
switch (get_body_part_direction(rooster_kijk(GRID, SNAKE_TAIL.x, SNAKE_TAIL.y))) {
|
||||
switch (get_body_part_direction(grid_fetch(GRID, SNAKE_TAIL.x, SNAKE_TAIL.y))) {
|
||||
case DIRECTION_UP:
|
||||
new_tail.y--;
|
||||
break;
|
||||
@@ -227,13 +245,13 @@ static void update_snake(coordinate new_head, snake_action action) {
|
||||
break;
|
||||
}
|
||||
|
||||
rooster_plaats(GRID, SNAKE_TAIL.x, SNAKE_TAIL.y, CELL_EMPTY);
|
||||
grid_put(GRID, SNAKE_TAIL.x, SNAKE_TAIL.y, CELL_EMPTY);
|
||||
SNAKE_TAIL = new_tail;
|
||||
}
|
||||
|
||||
// New head placed after tail moves. It can occupy the empty space created by the tail moving.
|
||||
rooster_plaats(GRID, new_head.x, new_head.y, get_body_part(CURRENT_DIRECTION));
|
||||
SNAKE_HEAD = new_head;
|
||||
grid_put(GRID, new_location.x, new_location.y, get_body_part(CURRENT_DIRECTION));
|
||||
SNAKE_HEAD = new_location;
|
||||
PREVIOUS_DIRECTION = CURRENT_DIRECTION;
|
||||
|
||||
if (action == SNAKE_EAT) {
|
||||
@@ -241,54 +259,96 @@ static void update_snake(coordinate new_head, snake_action action) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle snake movement.
|
||||
*
|
||||
* Input:
|
||||
* NULL, this signature is so it can be run as a thread.
|
||||
*
|
||||
* Returns:
|
||||
* NULL when finished
|
||||
*
|
||||
* Side Effects:
|
||||
* While the game is running, it moves the snake forward every 0.25 seconds and updates the game
|
||||
* state accordingly.
|
||||
*
|
||||
* In case an obstacle is encountered (wall or part of the snake): It dies.
|
||||
* In case a piece of food is encountered: It eats the apple and grows by 1.
|
||||
*/
|
||||
static void *snake_move(void *arg) {
|
||||
struct timespec timer;
|
||||
timer.tv_sec = 0;
|
||||
timer.tv_nsec = 250000000L; // Snake moves every 0.25 seconds.
|
||||
|
||||
while (rooster_vraag_toestand(GRID) == STATE_AAN_HET_SPELEN) {
|
||||
while (grid_fetch_state(GRID) == STATE_AAN_HET_SPELEN) {
|
||||
nanosleep(&timer, NULL);
|
||||
pthread_mutex_lock(&MUTEX);
|
||||
coordinate new_head = SNAKE_HEAD;
|
||||
pthread_mutex_lock(&SNAKE_MUTEX);
|
||||
coordinate new_location = SNAKE_HEAD;
|
||||
switch (CURRENT_DIRECTION) {
|
||||
case DIRECTION_UP:
|
||||
new_head.y--;
|
||||
new_location.y--;
|
||||
break;
|
||||
case DIRECTION_RIGHT:
|
||||
new_head.x++;
|
||||
new_location.x++;
|
||||
break;
|
||||
case DIRECTION_DOWN:
|
||||
new_head.y++;
|
||||
new_location.y++;
|
||||
break;
|
||||
case DIRECTION_LEFT:
|
||||
new_head.x--;
|
||||
new_location.x--;
|
||||
break;
|
||||
}
|
||||
|
||||
snake_action action = collision_check(rooster_kijk(GRID, new_head.x, new_head.y));
|
||||
|
||||
update_snake(new_head, action);
|
||||
update_snake(new_location);
|
||||
show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y);
|
||||
pthread_mutex_unlock(&MUTEX);
|
||||
pthread_mutex_unlock(&SNAKE_MUTEX);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void turn_snake(direction dir) {
|
||||
if ((direction)modulo(dir + 2, DIRECTION_COUNT) == PREVIOUS_DIRECTION
|
||||
/*
|
||||
* Turn the snake in the given direction.
|
||||
*
|
||||
* Input:
|
||||
* direction: The direction the snake should turn to.
|
||||
*
|
||||
* Side effects:
|
||||
* If the snake is able to turn in the given direction:
|
||||
* The snake's direction gets updated with the new value.
|
||||
* If the snake is unable to turn in the given direction:
|
||||
* The snake's direction remains unchanged.
|
||||
*/
|
||||
static void turn_snake(const direction dir) {
|
||||
// If the snake has a length of 1, it is able to turn around on the spot.
|
||||
if ((direction)modulo((int)dir + 2, DIRECTION_COUNT) == PREVIOUS_DIRECTION
|
||||
&& !same_coordinate(SNAKE_HEAD, SNAKE_TAIL)) {
|
||||
return;
|
||||
}
|
||||
rooster_plaats(GRID, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir));
|
||||
grid_put(GRID, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir));
|
||||
CURRENT_DIRECTION = dir;
|
||||
show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y);
|
||||
}
|
||||
|
||||
static void *play_snake(void *arg) {
|
||||
while (rooster_vraag_toestand(GRID) == STATE_AAN_HET_SPELEN) {
|
||||
/*
|
||||
* Handle user input.
|
||||
*
|
||||
* Input:
|
||||
* NULL, this signature is so it can be run as a thread.
|
||||
*
|
||||
* Returns:
|
||||
* NULL when finished
|
||||
*
|
||||
* Side Effects:
|
||||
* While the game is running, it constantly updates the direction the snake is pointing to
|
||||
* based on user input.
|
||||
*
|
||||
* In case ESCAPE or BACKSPACE is pressed it quits the game.
|
||||
*/
|
||||
static void *user_input(void *arg) {
|
||||
while (grid_fetch_state(GRID) == STATE_AAN_HET_SPELEN) {
|
||||
timeout(1);
|
||||
int c = getch();
|
||||
pthread_mutex_lock(&MUTEX);
|
||||
const int c = getch();
|
||||
pthread_mutex_lock(&SNAKE_MUTEX);
|
||||
switch (c) {
|
||||
case KEY_UP: // fallthrough
|
||||
case 'w':
|
||||
@@ -312,46 +372,78 @@ static void *play_snake(void *arg) {
|
||||
break;
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_ESCAPE:
|
||||
rooster_zet_toestand(GRID, STATE_QUIT);
|
||||
grid_put_state(GRID, STATE_QUIT);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&MUTEX);
|
||||
pthread_mutex_unlock(&SNAKE_MUTEX);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//todo: ?? Win condition?
|
||||
/*
|
||||
* Waits for the user to press their SPACEBAR before starting the game.
|
||||
*
|
||||
* Side Effects:
|
||||
* If the user presses backspace or escape, the whole game quits.
|
||||
*/
|
||||
static void wait_for_start(void) {
|
||||
const char start_message[] = "Press SPACEBAR to start playing.";
|
||||
const char empty_message[] = " ";
|
||||
mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, start_message);
|
||||
|
||||
grid_put_state(GRID, STATE_AAN_HET_SPELEN);
|
||||
|
||||
for (int c = getch(); c != ' '; c = getch()) {
|
||||
switch (c) {
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_ESCAPE:
|
||||
grid_put_state(GRID, STATE_QUIT);
|
||||
mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, empty_message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the start playing message.
|
||||
mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, empty_message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup the snake memory
|
||||
*
|
||||
* Side effects:
|
||||
* Frees all the memory used by the snake
|
||||
*/
|
||||
static void snake_cleanup(void) {
|
||||
pthread_mutex_destroy(&SNAKE_MUTEX);
|
||||
grid_cleanup(GRID);
|
||||
}
|
||||
|
||||
void snake(void) {
|
||||
initialize();
|
||||
if (GRID == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Created necessary threads.
|
||||
pthread_mutex_init(&MUTEX, NULL);
|
||||
pthread_t user_input;
|
||||
pthread_t game_tick;
|
||||
|
||||
// Show game.
|
||||
erase();
|
||||
show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y);
|
||||
mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, "Press SPACEBAR to start playing.");
|
||||
|
||||
rooster_zet_toestand(GRID, STATE_AAN_HET_SPELEN);
|
||||
wait_for_start();
|
||||
|
||||
while (getch() != ' ') {}
|
||||
if (grid_fetch_state(GRID) == STATE_AAN_HET_SPELEN) {
|
||||
// Create and start necessary threads.
|
||||
pthread_t input_thread;
|
||||
pthread_t game_tick_thread;
|
||||
|
||||
// Cleanup the start playing message.
|
||||
mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, " ");
|
||||
|
||||
pthread_create(&user_input, NULL, play_snake, NULL);
|
||||
pthread_create(&game_tick, NULL, snake_move, NULL);
|
||||
pthread_create(&input_thread, NULL, user_input, NULL);
|
||||
pthread_create(&game_tick_thread, NULL, snake_move, NULL);
|
||||
|
||||
// Wait until the gamestate is no longer STATE_AAN_HET_SPELEN
|
||||
pthread_join(game_tick, NULL);
|
||||
pthread_join(user_input, NULL);
|
||||
pthread_mutex_destroy(&MUTEX);
|
||||
|
||||
rooster_klaar(GRID);
|
||||
graceful_exit(MESSAGE_LOCATION);
|
||||
pthread_join(game_tick_thread, NULL);
|
||||
pthread_join(input_thread, NULL);
|
||||
}
|
||||
|
||||
game_exit_message(GRID, MESSAGE_LOCATION);
|
||||
|
||||
snake_cleanup();
|
||||
}
|
||||
|
||||
8
spel.c
8
spel.c
@@ -12,14 +12,18 @@
|
||||
*/
|
||||
|
||||
#include "grid_game_engine.h"
|
||||
#include "manual.h"
|
||||
#include "minigame_menu.h"
|
||||
|
||||
/*
|
||||
* Play minigame menu.
|
||||
*
|
||||
* Side effect:
|
||||
* Minigame menu starts.
|
||||
*/
|
||||
int main(void) {
|
||||
init_engine();
|
||||
|
||||
// Speel het spel.
|
||||
// manual((coordinate){0,3});
|
||||
minigame_menu();
|
||||
|
||||
cleanup_engine();
|
||||
|
||||
Reference in New Issue
Block a user