Added initial project files

yeet
This commit is contained in:
snapshot112
2025-10-13 15:29:56 +02:00
committed by GitHub
parent bf1a906682
commit 10c3545488
7 changed files with 759 additions and 0 deletions

34
CMakeLists.txt Normal file
View File

@@ -0,0 +1,34 @@
cmake_minimum_required()
# ------------------------------------------------------------------
# Define a custom target to run the default 'make' command
# ------------------------------------------------------------------
add_custom_target(
Week6
COMMAND ${MAKE_EXECUTABLE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running make in the project directory..."
VERBATIM
)
# ------------------------------------------------------------------
# Define a custom target to run 'make clean'
# ------------------------------------------------------------------
add_custom_target(
Week6Clean
COMMAND ${MAKE_EXECUTABLE} clean
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running make clean in the project directory..."
VERBATIM
)
# ------------------------------------------------------------------
# Define a custom target to run 'make clean'
# ------------------------------------------------------------------
add_custom_target(
Week6Tarball
COMMAND ${MAKE_EXECUTABLE} tarball1
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running make tarball in the project directory..."
VERBATIM
)

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
CC = gcc
CFLAGS = -std=c11 -Wextra -Wpedantic -g3 -fsanitize=address
LDFLAGS = -lncursesw -fsanitize=address
SRC = $(filter-out voorbeeld.c,$(wildcard *.c))
HEADERS = $(wildcard *.h)
.PHONY: tarball1 tarball2 clean
all: spel
spel: $(SRC)
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
voorbeeld:
$(CC) -o voorbeeld voorbeeld.c $(CFLAGS) $(LDFLAGS)
tarball1: deel1.tar.gz
tarball2: deel2.tar.gz
deel1.tar.gz: spel.c rooster.h rooster.c Makefile
tar czf $@ $^
deel2.tar.gz: $(SRC) $(HEADERS) Makefile
tar czf $@ $^ assets
clean:
rm -f *~ *.o voorbeeld spel deel?.tar.gz

224
rooster.c Normal file
View File

@@ -0,0 +1,224 @@
#include "rooster.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/*
* The rooster type this program is build around.
*/
typedef struct rooster_data {
char *rost;
int height;
int width;
toestand state;
} rooster;
/*
* Sets a grids width and height
*
* Input:
* fh: the stream to read the grid from.
* rost: a rooster file to store the width and height in.
*
* Side effects:
* the rooster 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) {
if (fh == NULL) {
return 0;
}
while (getc(fh) != '\n') {
if (feof(fh)) {
return 0;
}
rost->width++;
}
if (rost->width == 0) {
return 0;
}
fseek(fh, 0, SEEK_END);
// Get file size (- 1 for the blank newline and EOF at the end)
if (ftell(fh) % (rost->width + 1) != 0) {
// Not all lines are the same width
return 0;
}
rost->height = (int)ftell(fh) / (int)sizeof(char) / rost->width;
fseek(fh, 0, SEEK_SET);
return 1;
}
rooster *rooster_lees(FILE *fh) {
if (fh == NULL) {
return NULL;
}
rooster rost = {
NULL,
0,
0,
BEGIN
};
// Sets the width and height of the rooster.
if (get_grid_sizes(fh, &rost) != 1) {
// Unlogical file structure.
return NULL;
}
const int grid_size = (rost.width + 1) * rost.height + 1;
rost.rost = malloc(grid_size * sizeof(char));
if (rost.rost == NULL) {
return NULL;
}
// This makes the strncat() work.
rost.rost[0] = '\0';
char *line = malloc((rost.width + 2) * sizeof(char));
if (line == NULL) {
return NULL;
}
for (int i = 0; i < rost.height; i++) {
if (fgets(line, rost.width + 2, fh) == NULL) {
free(rost.rost);
free(line);
return NULL;
}
// Validate that the line length is correct
if ((int)strcspn(line, "\n") != rost.width) {
free(rost.rost);
free(line);
return NULL;
}
// Width is without the newline at the end.
strncat(rost.rost, line, rost.width + 1);
}
free(line);
rooster *return_rooster = malloc(sizeof(rost));
if (return_rooster == NULL) {
return NULL;
}
memcpy(return_rooster, &rost, sizeof(rost));
return return_rooster;
}
toestand rooster_vraag_toestand(const rooster *rp) {
if (rp != NULL) {
return rp->state;
}
return VERLOREN;
}
void rooster_zet_toestand(rooster *rp, toestand t) {
if (rp != NULL) {
switch (t) {
case BEGIN:
rp->state = BEGIN;
break;
case AAN_HET_SPELEN:
rp->state = AAN_HET_SPELEN;
break;
case GEWONNEN:
rp->state = GEWONNEN;
break;
case VERLOREN:
rp->state = VERLOREN;
break;
}
}
}
void rooster_klaar(rooster *rp) {
if (rp != NULL) {
if (rp->rost != NULL)
{
free(rp->rost);
}
free(rp);
}
}
int rooster_breedte(const rooster *rp) {
if (rp == NULL) {
return 0;
}
return rp->width;
}
int rooster_hoogte(const rooster *rp) {
if (rp == NULL) {
return 0;
}
return rp->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)
{
return 1;
}
}
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)];
}
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;
return 1;
}
return 0;
}
void rooster_zoek(const rooster *rp, char c, int *x, int *y) {
if (rp == NULL || rp->rost == NULL) {
*x = -1;
*y = -1;
return;
}
const char search[2] = {c};
int strpos = strcspn(rp->rost, search);
if (rp->rost[strpos] == '\0')
{
*x = -1;
*y = -1;
return;
}
*x = strpos % (rp->width + 1);
*y = strpos / (rp->width + 1);
}

152
rooster.h Normal file
View File

@@ -0,0 +1,152 @@
/* 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 {
BEGIN,
AAN_HET_SPELEN,
GEWONNEN,
VERLOREN
} 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 *rooster_lees(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 *rooster_maak(char* template);
/* Vraag de huidige toestand van het spel op.
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

265
spel.c Normal file
View File

@@ -0,0 +1,265 @@
/*
* Naam: Jeroen Boxhoorn
* UvAnetID: 16333969
* Studie: BSc Informatica
*
* This program loads in a maze from a text file and then lets you play that maze.
*
* Params when running the program:
* - The path to the text file containing the maze.
*
* How to play the game:
* - You are the '*' character.
* - Use either WSAD or the arrow keys to navigate through the maze.
* - The exit of the maze is marked with a '$'.
* - Walls are '#'.
* - Traps are 'X'. These kill you.
*
* You can quit the program at any time by pressing CTRL + C.
*
* Have fun playing!
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <wchar.h>
#include <locale.h>
#include "rooster.h"
/* Toont het gegeven rooster met ncurses.
*
* Input:
* rp: een pointer naar het rooster.
*
* Side effect:
* De console wordt geleegd en het rooster wordt erin gezet.
*/
void toon_rooster(rooster *rp) {
clear();
int height = rooster_hoogte(rp);
int width = rooster_breedte(rp);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (rooster_bevat(rp, x, y) == 1)
{
mvaddch(y, x, rooster_kijk(rp, x, y));
}
}
}
refresh();
}
void update_grid(rooster *rp, char c, int x, int y) {
if (rooster_plaats(rp, x, y, c) == 1) {
mvaddch(y, x, c);
}
}
/* 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
* 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.
*
* Side effect: het rooster wordt aangepast op basis van de handeling van
* de speler.
*/
void beweeg(rooster *rp, int dx, int dy) {
int playerx;
int playery;
rooster_zoek(rp, '*', &playerx, &playery);
if (playerx == -1 || playery == -1) {
printf("Player not found!");
exit(1);
}
if (rooster_bevat(rp, playerx + dx, playery + dy) == 1) {
char new_location = rooster_kijk(rp, playerx + dx, playery + dy);
switch (new_location) {
case '#':
break;
case 'X':
update_grid(rp, ' ', playerx, playery);
rooster_zet_toestand(rp, VERLOREN);
break;
case ' ':
update_grid(rp, ' ', playerx, playery);
update_grid(rp, '*', playerx + dx, playery + dy);
break;
case '$':
update_grid(rp, ' ', playerx, playery);
rooster_zet_toestand(rp, GEWONNEN);
break;
}
refresh();
}
}
/*
* Speel een dolhof spel.
*
* Input:
* rp: Een pointer naar een rooster met een valide doolhof erin.
*
* Side Effects:
* Met WSAD en arrow keys kun je door het doolhof heen bewegen.
*/
void run_maze(rooster *rp) {
while (rooster_vraag_toestand(rp) == AAN_HET_SPELEN)
{
switch (getch()) {
case KEY_UP: // fallthrough
case 'w':
beweeg(rp, 0, -1);
break;
case KEY_DOWN: // fallthrough
case 's':
beweeg(rp, 0, 1);
break;
case KEY_LEFT: // fallthrough
case 'a':
beweeg(rp, -1, 0);
break;
case KEY_RIGHT: // fallthrough
case 'd':
beweeg(rp, 1, 0);
break;
case KEY_EXIT:
rooster_zet_toestand(rp, VERLOREN);
break;
}
}
}
/*
* Toont het maze victory screen.
*
* Side Effect:
* De victory message wordt op een schone console geprint.
*/
void display_maze_victory() {
clear();
mvprintw(2,5, "YOU WON!!!!!");
refresh();
}
/*
* Toont het maze loss screen.
*
* Side Effect:
* De loss message wordt op een schone console geprint.
*/
void display_maze_loss() {
clear();
mvprintw(2,5, "RIP, YOU DIED...");
refresh();
}
/*
* Toont het maze broken screen.
*
* Side Effect:
* De hackerman message wordt op een schone console geprint.
*/
void display_hackerman() {
clear();
mvprintw(2,5, "The hacker man strikes again...");
refresh();
}
/*
* Bepaalt afhankelijk van de eindtoestand van het rooster
* welk afsluitscherm er getoond moet worden en toont dan dat rooster.
*
* Input: Het rooster om de toestand uit af te lezen.
*
* Side Effects:
* Het end-of-game scherm wordt op een blanke console geprint.
*/
void maze_exit_screen(rooster *rp) {
toestand current_state = rooster_vraag_toestand(rp);
switch (current_state) {
case GEWONNEN:
display_maze_victory();
return;
case VERLOREN:
display_maze_loss();
return;
}
display_hackerman();
}
/*
* Waits for the user to press an exit key 'q' before continuing.
*/
void graceful_exit() {
mvprintw(5, 0, "Press 'q' to exit the game.");
while (1) {
switch (getch()) {
case 'q':
return;
}
}
}
/*
* Speelt het spel met een gegeven rooster tot de toestand niet langer
* AAN_HET_SPELEN is.
*/
void speel(rooster *rp) {
toon_rooster(rp);
rooster_zet_toestand(rp, AAN_HET_SPELEN);
run_maze(rp);
maze_exit_screen(rp);
graceful_exit();
}
int main(int argc, char *argv[]) {
// 1. Controleer dat er een doolhofbestand is opgegeven op de command line.
if (argc != 2) {
fprintf(stderr, "gebruik: ./spel assets/voorbeeld_doolhof.txt\n");
return 1;
}
// 2. Open het doolhofbestand en lees het rooster.
FILE *fh = fopen(argv[1], "r");
if (fh == NULL) {
perror("main");
return 1;
}
rooster *rp = rooster_lees(fh);
fclose(fh);
// 3. Bepaal of het lezen van het rooster is gelukt.
if (rp == NULL) {
fprintf(stderr, "Kan rooster niet maken.\n");
return 1;
}
// 4. Initialiseer ncurses
setlocale(LC_ALL, "");
initscr();
cbreak(); // zodat je kunt onderbreken met Ctrl+C
keypad(stdscr, TRUE); // luister ook naar extra toetsen zoals pijltjes
noecho(); // druk niet de letters af die je intypt
curs_set(0); // hides the cursor
// 5. Speel het spel.
speel(rp);
// 6. Sluit af.
rooster_klaar(rp);
endwin();
return 0;
}

44
voorbeeld.c Normal file
View File

@@ -0,0 +1,44 @@
/* Voorbeeldje van het gebruik van ncurses.
In dit voorbeeld kun je alleen met een + naar links en naar rechts wandelen
over een lijn van -.
*/
#include <ncurses.h>
int RANGE = 20;
/* Toont de situatie met ncurses.
pos: de positie van de +.
*/
void laat_zien(int pos) {
clear(); // begin met een nieuw ncurses scherm
for (int i = 0; i < RANGE; i++) {
addch(i == pos ? '+' : '-');
}
refresh(); // zorg dat het scherm ook echt getoond wordt
}
int main(void) {
// Initialiseer ncurses
initscr();
cbreak(); // zodat je kunt onderbreken met Ctrl+C
keypad(stdscr, TRUE); // luister ook naar extra toetsen zoals pijltjes
noecho(); // druk niet de letters af die je intypt
int pos = RANGE / 2; // begin in het midden van de lijn.
while (1) {
laat_zien(pos);
int toets = getch();
switch (toets) {
case KEY_LEFT:
pos--;
break;
case KEY_RIGHT:
pos++;
break;
}
}
return 0;
}

13
voorbeeld_doolhof.txt Normal file
View File

@@ -0,0 +1,13 @@
#######################
#*# #
# # $ ########### #
# ######## #
# # ############
# ###### # #
# # # ############ #
# #### # # #
# # ##### # XXXXXX#
# XXX# # #
# #########XXXXXXX #
# X #
#######################