Compare commits
	
		
			17 Commits
		
	
	
		
			cfd4ccad64
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 93a2c37a6c | |||
| fb7856b69e | |||
| 299612634b | |||
| 665b8135a3 | |||
| ae65999622 | |||
| f5ea2867b8 | |||
| 9a6d15f269 | |||
| 40a3668049 | |||
| 4f6bbdd775 | |||
| e124e3071e | |||
| 6b0e858064 | |||
| 103f85d07e | |||
| a2d4290076 | |||
| f0e9d5c751 | |||
| 0e133aff32 | |||
| d09742ba79 | |||
| 027dbb8882 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,9 @@ | ||||
| # Clion files | ||||
| .idea | ||||
|  | ||||
| # Cmake files | ||||
| cmake-build-debug | ||||
|  | ||||
| # Prerequisites | ||||
| *.d | ||||
|  | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| cmake_minimum_required() | ||||
|  | ||||
| # ------------------------------------------------------------------ | ||||
| #  Define a custom target to run the default 'make' command | ||||
| # ------------------------------------------------------------------ | ||||
| add_custom_target( | ||||
|         MinigameMenu | ||||
|         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( | ||||
|         Clean | ||||
|         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( | ||||
|         Tarball | ||||
|         COMMAND ${MAKE_EXECUTABLE} tarball2 | ||||
|         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|         COMMENT "Running make tarball in the project directory..." | ||||
|         VERBATIM | ||||
| ) | ||||
							
								
								
									
										55
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,27 +1,40 @@ | ||||
| CC = gcc | ||||
| CFLAGS = -std=c11 -Wextra -Wpedantic -g3 -fsanitize=address | ||||
| LDFLAGS = -lncursesw -fsanitize=address | ||||
| SRC = $(filter-out voorbeeld.c,$(wildcard *.c)) | ||||
| HEADERS = $(wildcard *.h) | ||||
| CFLAGS = -std=gnu11 -Wextra -pedantic -Wall -O0 -g3 -fsanitize=address | ||||
| LDFLAGS = -lncurses -fsanitize=address | ||||
| BUILD_DIR = build | ||||
| SRC_DIR = src | ||||
| GAMES_DIR = games | ||||
| ENGINE_DIR = engine | ||||
|  | ||||
| .PHONY: tarball1 tarball2 clean | ||||
| MAIN_OBJECTS = $(BUILD_DIR)/main.o \ | ||||
| 	$(BUILD_DIR)/minigame_menu.o \ | ||||
| 	$(BUILD_DIR)/snake.o \ | ||||
| 	$(BUILD_DIR)/maze_runner.o \ | ||||
| 	$(BUILD_DIR)/minesweeper.o \ | ||||
| 	$(BUILD_DIR)/manual.o \ | ||||
| 	$(BUILD_DIR)/grid.o \ | ||||
| 	$(BUILD_DIR)/grid_game_engine.o | ||||
|  | ||||
| all: spel | ||||
| .PHONY: clean | ||||
|  | ||||
| spel: $(SRC) | ||||
| 	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) | ||||
| main: $(MAIN_OBJECTS) | ||||
| 	cc $(LDFLAGS) -o main $^ | ||||
|  | ||||
| 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 | ||||
| $(BUILD_DIR)/main.o: src/main.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/minigame_menu.o: src/games/minigame-menu/minigame_menu.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/snake.o: src/games/snake/snake.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/maze_runner.o: src/games/maze-runner/maze_runner.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/minesweeper.o: src/games/minesweeper/minesweeper.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/manual.o: src/games/manual/manual.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/grid.o: src/engine/grid/grid.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
| $(BUILD_DIR)/grid_game_engine.o: src/engine/engine/grid_game_engine.c | ||||
| 	cc $(CFLAGS) -o $@ -c $^ | ||||
|  | ||||
| clean: | ||||
| 	rm -f *~ *.o voorbeeld spel deel?.tar.gz | ||||
| 	rm -f build/*.o main | ||||
|   | ||||
							
								
								
									
										17
									
								
								Makefile.trial
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Makefile.trial
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| CC = gcc | ||||
| CFLAGS = -std=gnu11 -Wextra -pedantic -Wall -O0 -g3 -fsanitize=address | ||||
| LDFLAGS = -lncursesw -fsanitize=address | ||||
| OBJ_DIR = ./build | ||||
| OBJECTS = $(wildcard $(OBJ_DIR)/*.o) | ||||
| BUILD_COMMAND = $(CC) -c $^ -o $(OBJ_DIR)/$@ $(CFLAGS) $(LDFLAGS) | ||||
|  | ||||
| main: build | ||||
|  | ||||
| build: main.o | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *~ $(OBJECTS) main | ||||
|  | ||||
| grid: engine/grid/grid.c | ||||
| 	$(CC) -c $^ -o $(OBJ_DIR)/$@.o $(CFLAGS) $(LDFLAGS) | ||||
							
								
								
									
										45
									
								
								Makefile.yeet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Makefile.yeet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| CC = gcc | ||||
| CFLAGS = -std=gnu11 -Wextra -pedantic -Wall -O0 -g3 -fsanitize=address | ||||
| LDFLAGS = -lncurses -fsanitize=address | ||||
| OBJ_DIR = build/ | ||||
|  | ||||
| OBJECTS = $(wildcard $(OBJ_DIR)/*.o) | ||||
| BUILD_COMMAND = $(CC) -c $^ -o $(OBJ_DIR)/$@ $(CFLAGS) $(LDFLAGS) | ||||
|  | ||||
| SRC = src/ | ||||
| GAMES_DIR = games/ | ||||
| ENGINE_DIR = engine/ | ||||
|  | ||||
|  | ||||
|  | ||||
| .PHONY: clean | ||||
|  | ||||
| build: $(OBJECTS) | ||||
| 	$(CC) -o minigame-menu $(CFLAGS) $(LDFLAGS) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *~ $(OBJECTS) main | ||||
|  | ||||
| main.o: src/main.c | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| minigame-menu.o: src/games | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| manual.o: src/games | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| maze-runner.o: src/games | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| snake.o: src/games | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| minesweeper.o: src/games | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| engine.o: src/engine | ||||
| 	$(BUILD_COMMAND) | ||||
|  | ||||
| grid.o: src/engine | ||||
| 	$(BUILD_COMMAND) | ||||
							
								
								
									
										87
									
								
								assets/manual.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								assets/manual.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| *--------------------------------------------* | ||||
| |                                            | | ||||
| |           !!! 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.                        | | ||||
| |                                            | | ||||
| |             *________________*             | | ||||
| |             |    GENERAL     |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |        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      |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Move down:             |   | | ||||
| |   |          's', arrow_down           |   | | ||||
| |   |                                    |   | | ||||
| |   |              Move up:              |   | | ||||
| |   |           'w', arrow_up            |   | | ||||
| |   |                                    |   | | ||||
| |   |              Select:               |   | | ||||
| |   |        'f', enter, space bar       |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| |                                            | | ||||
| |            *-----------------*             | | ||||
| |            |   MAZE RUNNER   |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Move up:               |   | | ||||
| |   |          'w', arrow_up             |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move down:              |   | | ||||
| |   |         's', arrow_down            |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move right:             |   | | ||||
| |   |         'd', arrow_right           |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move left:              |   | | ||||
| |   |         'a', arrow_left            |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| |                                            | | ||||
| |            *-----------------*             | | ||||
| |            |      SNAKE      |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Turn up:               |   | | ||||
| |   |          'w', arrow_up             |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn down:              |   | | ||||
| |   |         's', arrow_down            |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn right:             |   | | ||||
| |   |         'd', arrow_right           |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn left:              |   | | ||||
| |   |         'a', arrow_left            |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| *--------------------------------------------* | ||||
| @@ -1,153 +0,0 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 10/15/25. | ||||
|  * | ||||
|  * A game engine that can run and display games in square grids. | ||||
|  */ | ||||
|  | ||||
| #include "grid_game_engine.h" | ||||
|  | ||||
| #include <locale.h> | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "rooster.h" | ||||
|  | ||||
| int modulo(const int number, const int mod) { | ||||
|     int result = number % mod; | ||||
|     if (result < 0) { | ||||
|         result += mod;    //This is not how math is supposed to work C, damnit. | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void show_grid_on_offset(const rooster *gp, const int starting_x, const int starting_y) { | ||||
|     const int height = rooster_hoogte(gp); | ||||
|  | ||||
|     for (int y = 0; y < height; y++) { | ||||
|         char *rij = rooster_vraag_rij(gp, y); | ||||
|         mvprintw(starting_y + y, starting_x, "%s", rij); | ||||
|         free(rij); | ||||
|     } | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| void show_grid(const rooster *rp) { | ||||
|     show_grid_on_offset(rp, 0, 0); | ||||
| } | ||||
|  | ||||
| void update_grid(rooster *rp, char c, int x, int y) { | ||||
|     if (rooster_plaats(rp, x, y, c) == 1) { | ||||
|         mvaddch(y, x, c); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the victory screen. | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the victory message. | ||||
|  */ | ||||
| static void display_victory(void) { | ||||
|     clear(); | ||||
|     mvprintw(2,5, "YOU WON!!!!!"); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the GAME OVER screen. | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the GAME OVER message. | ||||
|  */ | ||||
| static void display_loss(void) { | ||||
|     clear(); | ||||
|     mvprintw(2,5, "GAME OVER"); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the quit screen | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the quit message. | ||||
|  */ | ||||
| static void display_quit(void) { | ||||
|     clear(); | ||||
|     mvprintw(2,5, "You quit the game"); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the hacker man screen | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the hacker man message. | ||||
|  */ | ||||
| static void display_hackerman(void) { | ||||
|     clear(); | ||||
|     mvprintw(2,5, "The hacker man strikes again..."); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| void graceful_exit(void) { | ||||
|     mvprintw(5, 5, "Press 'q' to exit."); | ||||
|     while (1) { | ||||
|         switch (getch()) { | ||||
|             case 'q': | ||||
|             case 'Q': | ||||
|                 return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void game_exit_screen(rooster *gp) { | ||||
|     toestand current_state = rooster_vraag_toestand(gp); | ||||
|     switch (current_state) { | ||||
|         case STATE_GEWONNEN: | ||||
|             display_victory(); | ||||
|             break; | ||||
|         case STATE_VERLOREN: | ||||
|             display_loss(); | ||||
|             break; | ||||
|         case STATE_QUIT: | ||||
|             display_quit(); | ||||
|             break; | ||||
|         default: | ||||
|             display_hackerman(); | ||||
|     } | ||||
|     graceful_exit(); | ||||
| } | ||||
|  | ||||
| static void init_ncurses() { | ||||
|     setlocale(LC_ALL, ""); | ||||
|     initscr(); | ||||
|     cbreak();             // So you can cancel the game with ctrl + c. | ||||
|     keypad(stdscr, TRUE); // Enable extra keys like the arrow keys. | ||||
|     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); | ||||
|     init_pair(BLUE, COLOR_BLACK, COLOR_BLUE); | ||||
|     init_pair(GREEN, COLOR_BLACK, COLOR_GREEN); | ||||
|     init_pair(CYAN, COLOR_BLACK, COLOR_CYAN); | ||||
|     init_pair(MAGENTA, COLOR_BLACK, COLOR_MAGENTA); | ||||
|     init_pair(YELLOW, COLOR_BLACK, COLOR_YELLOW); | ||||
|     init_pair(RED, COLOR_BLACK, COLOR_RED); | ||||
| } | ||||
|  | ||||
| static void cleanup_ncurses() { | ||||
|     endwin(); | ||||
| } | ||||
|  | ||||
| void init_engine(void) { | ||||
|     init_ncurses(); | ||||
| } | ||||
|  | ||||
| void cleanup_engine(void) { | ||||
|     cleanup_ncurses(); | ||||
| } | ||||
| @@ -1,103 +0,0 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 10/15/25. | ||||
|  * | ||||
|  * A game engine that uses the can run and display games in rectangular grids. | ||||
|  * The graphics are made using ncursesw. | ||||
|  * | ||||
|  * Please make sure to initialize the game engine before running any games. | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
| #define MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
| #include "rooster.h" | ||||
|  | ||||
| typedef enum { | ||||
|     BLACK = 1, | ||||
|     WHITE = 2, | ||||
|     BLUE = 3, | ||||
|     GREEN = 4, | ||||
|     CYAN = 5, | ||||
|     MAGENTA = 6, | ||||
|     YELLOW = 7, | ||||
|     RED = 8 | ||||
| } game_colors; | ||||
|  | ||||
| typedef struct { | ||||
|     char name[100]; | ||||
|     rooster *game_map; | ||||
| } game_maps; | ||||
|  | ||||
| /* | ||||
|  * A proper modulo function. | ||||
|  * The one provided by c: '%' doesn't work according to the mathematical definition. | ||||
|  */ | ||||
| int modulo(int number, int mod); | ||||
|  | ||||
| /* | ||||
|  * Displays the given grid with the given offset using ncurses. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * | ||||
|  * 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); | ||||
|  | ||||
| /* | ||||
|  * Displays the given grid with ncurses. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The console is cleared and the grid is printed. | ||||
|  */ | ||||
| void show_grid(const rooster *gp); | ||||
|  | ||||
| /* | ||||
|  * Updates a single location in the grid. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * c: The character to update the location with. | ||||
|  * x: The x-coordinate of the spot you want to update. | ||||
|  * y: The y-coordinate of the spot you want to update. | ||||
|  * | ||||
|  * 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); | ||||
|  | ||||
| /* | ||||
|  * Display the ending screen that matches the end state of the grid. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * The end of game screen gets displayed with a graceful exit. | ||||
|  */ | ||||
| void game_exit_screen(rooster *gp); | ||||
|  | ||||
| /* | ||||
|  * Waits for you to press 'q' before exiting the game. | ||||
|  * | ||||
|  * Side effect: Prints "Press 'q' to exit." game to the console. | ||||
|  */ | ||||
| void graceful_exit(void); | ||||
|  | ||||
| /* | ||||
|  * Initialize ncurses. | ||||
|  * | ||||
|  * This enables cbreak, noecho, hides the cursor and enables the extra keys. | ||||
|  * This also creates the color pairs needed for game_colors, | ||||
|  */ | ||||
| void init_engine(void); | ||||
|  | ||||
| /* | ||||
|  * Cleanup ncurses. | ||||
|  */ | ||||
| void cleanup_engine(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
							
								
								
									
										128
									
								
								maze_runner.c
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								maze_runner.c
									
									
									
									
									
								
							| @@ -1,128 +0,0 @@ | ||||
| // | ||||
| // Created by snapshot112 on 10/15/25. | ||||
| // | ||||
|  | ||||
| #include "maze_runner.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "grid_game_engine.h" | ||||
| #include "rooster.h" | ||||
|  | ||||
| /* | ||||
|  * Reads in the maze from the assets file. | ||||
|  */ | ||||
| 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. | ||||
|     FILE *fh = fopen("assets/maze.txt", "r"); | ||||
|     if (fh == NULL) { | ||||
|         perror("loading maze"); | ||||
|         exit(EXIT_FAILURE); | ||||
|     } | ||||
|     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"); | ||||
|         exit(EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|     return rp; | ||||
| } | ||||
|  | ||||
| /* 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. | ||||
|  */ | ||||
| static void maze_runner_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, STATE_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, STATE_GEWONNEN); | ||||
|                 break; | ||||
|         } | ||||
|         refresh(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Speelt het spel met een gegeven rooster tot de toestand niet langer | ||||
|  * AAN_HET_SPELEN is. | ||||
|  */ | ||||
| static void speel_maze(rooster *rp) { | ||||
|     while (rooster_vraag_toestand(rp) == STATE_AAN_HET_SPELEN) | ||||
|     { | ||||
|         switch (getch()) { | ||||
|             case KEY_UP: // fallthrough | ||||
|             case 'w': | ||||
|             case 'W': | ||||
|                 maze_runner_beweeg(rp, 0, -1); | ||||
|                 break; | ||||
|             case KEY_DOWN: // fallthrough | ||||
|             case 's': | ||||
|             case 'S': | ||||
|                 maze_runner_beweeg(rp, 0, 1); | ||||
|                 break; | ||||
|             case KEY_LEFT: // fallthrough | ||||
|             case 'a': | ||||
|             case 'A': | ||||
|                 maze_runner_beweeg(rp, -1, 0); | ||||
|                 break; | ||||
|             case KEY_RIGHT: // fallthrough | ||||
|             case 'd': | ||||
|             case 'D': | ||||
|                 maze_runner_beweeg(rp, 1, 0); | ||||
|                 break; | ||||
|             case KEY_BACKSPACE: | ||||
|                 rooster_zet_toestand(rp, STATE_QUIT); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void maze_runner(void) { | ||||
|     // Voorbereiding. | ||||
|     rooster *rp = get_maze(); | ||||
|     show_grid(rp); | ||||
|     rooster_zet_toestand(rp, STATE_AAN_HET_SPELEN); | ||||
|  | ||||
|     // Game zelf. | ||||
|     speel_maze(rp); | ||||
|  | ||||
|     // Exit game. | ||||
|     game_exit_screen(rp); | ||||
|     rooster_klaar(rp); | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 10/15/25. | ||||
|  * | ||||
|  * A minigame menu for games configured to run on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling menu(); | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_MINIGAME_MENU_H | ||||
| #define MINIGAME_MENU_MINIGAME_MENU_H | ||||
|  | ||||
| typedef enum { | ||||
|     GAME_MAZE_RUNNER = 0, | ||||
|     GAME_SNAKE = 1, | ||||
|     GAME_MINESWEEPER = 2, | ||||
|     GAME_QUIT = 3, | ||||
| } game; | ||||
|  | ||||
| /* | ||||
|  * A minigame menu for games configured to run on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling menu(); | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to display a minigame menu. | ||||
|  * | ||||
|  * Controls: | ||||
|  * 'w'/'arr_up:     Next menu item. | ||||
|  * 's'/'arr_down':  Previous menu item. | ||||
|  * 'f':             Select current menu item. | ||||
|  * 'BACKSPACE':     Exit the menu. | ||||
|  */ | ||||
| void minigame_menu(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_MINIGAME_MENU_H | ||||
							
								
								
									
										295
									
								
								rooster.c
									
									
									
									
									
								
							
							
						
						
									
										295
									
								
								rooster.c
									
									
									
									
									
								
							| @@ -1,295 +0,0 @@ | ||||
| #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; | ||||
|  | ||||
| rooster *rooster_maak(const char* input) { | ||||
|     int width = 0; | ||||
|     int height = 0; | ||||
|     const size_t len = strlen(input) / sizeof(char); | ||||
|     if (input == NULL || len == 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; input[i] != '\n'; i++) { | ||||
|         width++; | ||||
|     } | ||||
|  | ||||
|     height = (int)len / (width + 1); | ||||
|  | ||||
|     for (int i = 0; i < height; i = i + width + 1) { | ||||
|         if ((int)strcspn(&input[i], "\n") != width) { | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const int grid_size = (width + 1) * height + 1; | ||||
|  | ||||
|     rooster *rp = malloc(sizeof(rooster)); | ||||
|  | ||||
|     if (rp == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     rp->rost = malloc(grid_size * sizeof(char)); | ||||
|     rp->height = height; | ||||
|     rp->width = width; | ||||
|     rp->state = STATE_BEGIN; | ||||
|  | ||||
|     strcpy(rp->rost, input); | ||||
|  | ||||
|     return rp; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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) { | ||||
|     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, | ||||
|         STATE_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 STATE_VERLOREN; | ||||
| } | ||||
|  | ||||
| void rooster_zet_toestand(rooster *rp, toestand t) { | ||||
|     if (rp != NULL) { | ||||
|         switch (t) { | ||||
|             case STATE_BEGIN: | ||||
|                 rp->state = STATE_BEGIN; | ||||
|                 break; | ||||
|             case STATE_AAN_HET_SPELEN: | ||||
|                 rp->state = STATE_AAN_HET_SPELEN; | ||||
|                 break; | ||||
|             case STATE_GEWONNEN: | ||||
|                 rp->state = STATE_GEWONNEN; | ||||
|                 break; | ||||
|             case STATE_VERLOREN: | ||||
|                 rp->state = STATE_VERLOREN; | ||||
|                 break; | ||||
|             case STATE_QUIT: | ||||
|                 rp->state = STATE_QUIT; | ||||
|                 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; | ||||
| } | ||||
|  | ||||
| char *rooster_vraag_rij(const rooster *rp, int y) { | ||||
| 	if (rp != NULL && rp->rost != NULL && rooster_bevat(rp, 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'; | ||||
| 		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); | ||||
|  | ||||
|         char *grid = malloc(grid_memory); | ||||
|         if (grid == NULL) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         rooster *new_rooster = malloc(sizeof(*rp)); | ||||
|         if (new_rooster == NULL) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
| 	    memcpy(grid, rp->rost, grid_memory); | ||||
|  | ||||
|         memcpy(new_rooster, rp, sizeof(*rp)); | ||||
|  | ||||
|         new_rooster->rost = grid; | ||||
|         return new_rooster; | ||||
|     } | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										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 *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(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 | ||||
							
								
								
									
										16
									
								
								snake.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								snake.c
									
									
									
									
									
								
							| @@ -1,16 +0,0 @@ | ||||
| // | ||||
| // Created by snapshot112 on 10/15/25. | ||||
| // | ||||
|  | ||||
| #include "snake.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
|  | ||||
| #include "grid_game_engine.h" | ||||
|  | ||||
| void snake(void) { | ||||
|     clear(); | ||||
|     mvprintw(0,0, "Snake has not yet been created"); | ||||
|     graceful_exit(); | ||||
|     refresh(); | ||||
| } | ||||
							
								
								
									
										24
									
								
								spel.c
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								spel.c
									
									
									
									
									
								
							| @@ -1,24 +0,0 @@ | ||||
| /* | ||||
|  * Naam: Jeroen Boxhoorn | ||||
|  * UvAnetID: 16333969 | ||||
|  * Studie: BSc Informatica | ||||
|  * | ||||
|  * A minigame menu that lets you play games on the grid game engine. | ||||
|  * | ||||
|  * Currently the following games are included: | ||||
|  * - maze runner | ||||
|  * - snake | ||||
|  * - minesweeper | ||||
|  */ | ||||
|  | ||||
| #include "grid_game_engine.h" | ||||
| #include "minigame_menu.h" | ||||
|  | ||||
| int main(void) { | ||||
|     init_engine(); | ||||
|  | ||||
|     // 5. Speel het spel. | ||||
|     minigame_menu(); | ||||
|  | ||||
|     cleanup_engine(); | ||||
| } | ||||
							
								
								
									
										160
									
								
								src/engine/engine/grid_game_engine.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/engine/engine/grid_game_engine.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 8/10/2025 | ||||
|  */ | ||||
|  | ||||
| #include "grid_game_engine.h" | ||||
|  | ||||
| #include <locale.h> | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| int same_coordinate(const coordinate a, const coordinate b) { | ||||
|     return a.x == b.x && a.y == b.y; | ||||
| } | ||||
|  | ||||
| int modulo(const int number, const int mod) { | ||||
|     int result = number % mod; | ||||
|     if (result < 0) { | ||||
|         result += mod;    //This is not how math is supposed to work C, damnit. | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void show_grid_on_offset(const grid *gp, const coordinate offset) { | ||||
|     const int height = grid_height(gp); | ||||
|  | ||||
|     for (int y = 0; y < height; y++) { | ||||
|         char *rij = grid_fetch_row(gp, y); | ||||
|         mvprintw(offset.y + y, offset.x, "%s", rij); | ||||
|         free(rij); | ||||
|     } | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| void show_grid(const grid *gp) { | ||||
|     show_grid_on_offset(gp, (coordinate){.x = 0, .y = 0}); | ||||
| } | ||||
|  | ||||
| void update_grid(grid *gp, const char ch, coordinate c) { | ||||
|     if (grid_put(gp, ch, c) == 1) { | ||||
|         mvaddch(c.y, c.x, ch); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the victory screen. | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the victory message. | ||||
|  */ | ||||
| static void display_victory(const coordinate location) { | ||||
|     mvaddstr(location.y, location.x, "YOU WON!!!!!"); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the GAME OVER screen. | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the GAME OVER message. | ||||
|  */ | ||||
| static void display_loss(const coordinate location) { | ||||
|     mvaddstr(location.y, location.x, "GAME OVER"); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the quit screen | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the quit message. | ||||
|  */ | ||||
| static void display_quit(const coordinate location) { | ||||
|     mvaddstr(location.y, location.x, "You quit the game"); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Shows the hacker man screen | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Clears the console and prints the hacker man message. | ||||
|  */ | ||||
| static void display_hackerman(const coordinate location) { | ||||
|     mvaddstr(location.y, location.x, "The hacker man strikes again..."); | ||||
| } | ||||
|  | ||||
| void graceful_exit(const coordinate message_location) { | ||||
|     mvaddstr(message_location.y, message_location.x, "Press ENTER to exit."); | ||||
|     while (1) { | ||||
|         switch (getch()) { | ||||
|             case KEY_BACKSPACE: | ||||
|             case KEY_ESCAPE: | ||||
|             case KEY_ENTER: | ||||
|             case '\n': | ||||
|                 return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
|             break; | ||||
|         case STATE_VERLOREN: | ||||
|             display_loss(location); | ||||
|             break; | ||||
|         case STATE_QUIT: | ||||
|             display_quit(location); | ||||
|             break; | ||||
|         default: | ||||
|             display_hackerman(location); | ||||
|     } | ||||
|  | ||||
|     location.y += 2; | ||||
|     graceful_exit(location); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| 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(); | ||||
|     cbreak();             // So you can cancel the game with ctrl + c. | ||||
|     keypad(stdscr, TRUE); // Enable extra keys like the arrow keys. | ||||
|     noecho();             // Don't write the keyboard input to the console. | ||||
|     curs_set(0);          // Hides the cursor. | ||||
|  | ||||
|     start_color(); | ||||
|     init_pair(BLACK, COLOR_BLACK, COLOR_BLACK); | ||||
|     init_pair(WHITE, COLOR_BLACK, COLOR_WHITE); | ||||
|     init_pair(BLUE, COLOR_BLACK, COLOR_BLUE); | ||||
|     init_pair(GREEN, COLOR_BLACK, COLOR_GREEN); | ||||
|     init_pair(CYAN, COLOR_BLACK, COLOR_CYAN); | ||||
|     init_pair(MAGENTA, COLOR_BLACK, COLOR_MAGENTA); | ||||
|     init_pair(YELLOW, COLOR_BLACK, COLOR_YELLOW); | ||||
|     init_pair(RED, COLOR_BLACK, COLOR_RED); | ||||
|  | ||||
|     clear(); | ||||
| } | ||||
|  | ||||
| void init_engine(void) { | ||||
|     init_ncurses(); | ||||
| } | ||||
|  | ||||
| void cleanup_engine(void) { | ||||
|     endwin(); | ||||
| } | ||||
							
								
								
									
										152
									
								
								src/engine/engine/grid_game_engine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/engine/engine/grid_game_engine.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025. | ||||
|  * | ||||
|  * A game engine build on top of a grid api to run and display games using ncurses. | ||||
|  * | ||||
|  * Please make sure to initialize the game engine before running any games. | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
| #define MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
| #include "../grid/grid.h" | ||||
|  | ||||
| #define KEY_ESCAPE 27 | ||||
|  | ||||
| // Start at 1 since the color 0 is reserved for no_color. | ||||
| typedef enum { | ||||
|     BLACK = 1, | ||||
|     WHITE = 2, | ||||
|     BLUE = 3, | ||||
|     GREEN = 4, | ||||
|     CYAN = 5, | ||||
|     MAGENTA = 6, | ||||
|     YELLOW = 7, | ||||
|     RED = 8 | ||||
| } game_colors; | ||||
|  | ||||
| typedef struct { | ||||
|     char name[100]; | ||||
|     grid *game_map; | ||||
| } game_maps; | ||||
|  | ||||
| /* | ||||
|  * Checks if 2 coordinates are the same. | ||||
|  * | ||||
|  * Input: | ||||
|  * a: coordinate 1 | ||||
|  * b: coordinate 2 | ||||
|  * | ||||
|  * Returns: | ||||
|  * If they point to the same location:  1 | ||||
|  * Otherwise:                           0 | ||||
|  */ | ||||
| int same_coordinate(coordinate a, coordinate b); | ||||
|  | ||||
| /* | ||||
|  * A proper modulo function. | ||||
|  * The one provided by c: '%' doesn't work according to the mathematical definition. | ||||
|  */ | ||||
| int modulo(int number, int mod); | ||||
|  | ||||
| /* | ||||
|  * Displays the given grid with the given offset using ncurses. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * c: The location you want to show the grid at. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The console is cleared and the grid is printed. | ||||
|  */ | ||||
| void show_grid_on_offset(const grid *gp, coordinate c); | ||||
|  | ||||
| /* | ||||
|  * Displays the given grid with ncurses. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The console is cleared and the grid is printed. | ||||
|  */ | ||||
| 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. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: A pointer to the grid. | ||||
|  * ch: The character to update the location with. | ||||
|  * c: The spot you want to update. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The update gets applied both on the grid and on the screen. | ||||
|  */ | ||||
| void update_grid(grid *gp, char ch, coordinate c); | ||||
|  | ||||
| /* | ||||
|  * Display the ending screen that matches the end state of the grid. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp:          A pointer to the grid. | ||||
|  * coordinate:  The location to show the ending screen. | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * The end of game screen gets displayed with a graceful exit. | ||||
|  */ | ||||
| void game_exit_message(const grid *gp, coordinate location); | ||||
|  | ||||
| /* | ||||
|  * Waits for you to press ENTER before exiting the game. | ||||
|  * | ||||
|  * Input: | ||||
|  * coordinate: The location to show the message. | ||||
|  * | ||||
|  * Side effect: Prints "Press ENTER to exit." game to the console. | ||||
|  */ | ||||
| void graceful_exit(coordinate message_location); | ||||
|  | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| void init_engine(void); | ||||
|  | ||||
| /* | ||||
|  * Cleanup ncurses. | ||||
|  */ | ||||
| void cleanup_engine(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_GRID_GAME_ENGINE_H | ||||
							
								
								
									
										302
									
								
								src/engine/grid/grid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								src/engine/grid/grid.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 2/10/2025 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #include "grid.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| /* | ||||
|  * The grid type this program is build around. | ||||
|  */ | ||||
| typedef struct grid_data { | ||||
|     char *locations; | ||||
|     int height; | ||||
|     int width; | ||||
|     state state; | ||||
| } grid; | ||||
|  | ||||
| /* | ||||
|  * Translate x and y coordinates to a location index on the grid. | ||||
|  */ | ||||
| static int internal_location(const grid *gp, const coordinate c) { | ||||
|     return c.y * (gp->width + 1) + c.x; | ||||
| } | ||||
|  | ||||
| grid *grid_create_from_string(const char* input) { | ||||
|     int width = 0; | ||||
|     int height = 0; | ||||
|     const size_t len = strlen(input) / sizeof(char); | ||||
|     if (input == NULL || len == 0) { | ||||
|         printf("invalid input\n"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; input[i] != '\n'; i++) { | ||||
|         width++; | ||||
|     } | ||||
|  | ||||
|     height = (int)len / (width + 1); | ||||
|  | ||||
|     for (int i = 0; i < height; i = i + width + 1) { | ||||
|         if ((int)strcspn(&input[i], "\n") != width) { | ||||
|             printf("line %d was not %d wide\n", i, width); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const int grid_size = (width + 1) * height + 1; | ||||
|  | ||||
|     grid *gp = malloc(sizeof(grid)); | ||||
|  | ||||
|     if (gp == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     gp->locations = malloc(grid_size * sizeof(char)); | ||||
|     gp->height = height; | ||||
|     gp->width = width; | ||||
|     gp->state = STATE_BEGIN; | ||||
|  | ||||
|     strcpy(gp->locations, input); | ||||
|  | ||||
|     return gp; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Sets a grids width and height | ||||
|  * | ||||
|  * Input: | ||||
|  * fh: The stream to read the grid from. | ||||
|  * gp: A pointer to the grid whose width and height you want to set. | ||||
|  * | ||||
|  * Side effects: | ||||
|  * 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, grid *gp) { | ||||
|     while (getc(fh) != '\n') { | ||||
|         if (feof(fh)) { | ||||
|             return 0; | ||||
|         } | ||||
|         gp->width++; | ||||
|     } | ||||
|  | ||||
|     if (gp->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) % (gp->width + 1) != 0) { | ||||
|         // Not all lines are the same width | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     gp->height = (int)ftell(fh) / (int)sizeof(char) / (gp->width + 1); | ||||
|     fseek(fh, 0, SEEK_SET); | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| grid *grid_create_from_file(FILE *fh) { | ||||
|     if (fh == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     grid temp_grid = { | ||||
|         NULL, | ||||
|         0, | ||||
|         0, | ||||
|         STATE_BEGIN | ||||
|     }; | ||||
|  | ||||
|     // Sets the width and height of the grid. | ||||
|     if (get_grid_sizes(fh, &temp_grid) != 1) { | ||||
|         // Unlogical file structure. | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     const int grid_size = (temp_grid.width + 1) * temp_grid.height + 1; | ||||
|  | ||||
|     temp_grid.locations = malloc(grid_size * sizeof(char)); | ||||
|     if (temp_grid.locations == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // This makes the strncat() work. | ||||
|     temp_grid.locations[0] = '\0'; | ||||
|  | ||||
|     char *line = malloc((temp_grid.width + 2) * sizeof(char)); | ||||
|     if (line == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < temp_grid.height; i++) { | ||||
|         if (fgets(line, temp_grid.width + 2, fh) == NULL) { | ||||
|             free(temp_grid.locations); | ||||
|             free(line); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Validate that the line length is correct | ||||
|         if ((int)strcspn(line, "\n") != temp_grid.width) { | ||||
|             free(temp_grid.locations); | ||||
|             free(line); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Width is without the newline at the end. | ||||
|         strncat(temp_grid.locations, line, temp_grid.width + 1); | ||||
|     } | ||||
|  | ||||
|     free(line); | ||||
|  | ||||
|     grid *return_grid = malloc(sizeof(temp_grid)); | ||||
|     if (return_grid == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     memcpy(return_grid, &temp_grid, sizeof(temp_grid)); | ||||
|  | ||||
|     return return_grid; | ||||
| } | ||||
|  | ||||
| state grid_fetch_state(const grid *gp) { | ||||
|     if (gp != NULL) { | ||||
|         return gp->state; | ||||
|     } | ||||
|     return STATE_VERLOREN; | ||||
| } | ||||
|  | ||||
| void grid_put_state(grid *gp, const state t) { | ||||
|     if (gp != NULL) { | ||||
|         switch (t) { | ||||
|             case STATE_BEGIN: | ||||
|                 gp->state = STATE_BEGIN; | ||||
|                 break; | ||||
|             case STATE_AAN_HET_SPELEN: | ||||
|                 gp->state = STATE_AAN_HET_SPELEN; | ||||
|                 break; | ||||
|             case STATE_GEWONNEN: | ||||
|                 gp->state = STATE_GEWONNEN; | ||||
|                 break; | ||||
|             case STATE_VERLOREN: | ||||
|                 gp->state = STATE_VERLOREN; | ||||
|                 break; | ||||
|             case STATE_QUIT: | ||||
|                 gp->state = STATE_QUIT; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void grid_cleanup(grid *gp) { | ||||
|     if (gp != NULL) { | ||||
|         if (gp->locations != NULL) | ||||
|         { | ||||
|             free(gp->locations); | ||||
|         } | ||||
|         free(gp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int grid_width(const grid *gp) { | ||||
|     if (gp == NULL) { | ||||
|         return 0; | ||||
|     } | ||||
|     return gp->width; | ||||
| } | ||||
|  | ||||
| int grid_height(const grid *gp) { | ||||
|     if (gp == NULL) { | ||||
|         return 0; | ||||
|     } | ||||
|     return gp->height; | ||||
| } | ||||
|  | ||||
| int grid_contains(const grid *gp, const coordinate c) { | ||||
|     if (gp != NULL && gp->locations != NULL) { | ||||
|         if (c.x >= 0 && c.y >= 0 && c.x < gp->width && c.y < gp->height) | ||||
|         { | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| char grid_fetch(const grid *gp, const coordinate c) { | ||||
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, c) == 1) { | ||||
|         return gp->locations[internal_location(gp, c)]; | ||||
|     } | ||||
|     return '\0'; | ||||
| } | ||||
|  | ||||
| int grid_put(grid *gp, const char ch, const coordinate c) { | ||||
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, c) == 1) { | ||||
|         gp->locations[internal_location(gp, c)] = ch; | ||||
|         return 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| char *grid_fetch_row(const grid *gp, const int y) { | ||||
|     const coordinate row_start = {.x = 0, .y = y}; | ||||
| 	if (gp != NULL && gp->locations != NULL && grid_contains(gp, row_start) == 1) { | ||||
| 	    // we're going to remove the newline so this is long enough | ||||
| 		char *row = malloc((gp->width + 1) * sizeof(char)); | ||||
| 		memcpy(row, &gp->locations[internal_location(gp, row_start)], gp->width * sizeof(char)); | ||||
| 	    row[gp->width] = '\0'; | ||||
| 		return row; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| grid *grid_copy(const grid *gp) { | ||||
| 	if (gp != NULL && gp->locations != NULL) { | ||||
| 	    const size_t grid_memory = ((gp->width + 1) * gp->height + 1) * sizeof(char); | ||||
|  | ||||
|         char *locations = malloc(grid_memory); | ||||
|         if (locations == NULL) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
| 	    grid *new_grid = malloc(sizeof(*gp)); | ||||
| 	    if (new_grid == NULL) { | ||||
| 	        return NULL; | ||||
| 	    } | ||||
|  | ||||
| 	    memcpy(locations, gp->locations, grid_memory); | ||||
|  | ||||
| 	    memcpy(new_grid, gp, sizeof(*gp)); | ||||
|  | ||||
| 	    new_grid->locations = locations; | ||||
| 	    return new_grid; | ||||
|     } | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| coordinate grid_find(const grid *gp, const char c) { | ||||
|     const coordinate not_found = {.x = -1, .y = -1}; | ||||
|     if (gp == NULL || gp->locations == NULL) { | ||||
|         return not_found; | ||||
|     } | ||||
|  | ||||
|     const char search[2] = {c}; | ||||
|  | ||||
|     const int char_index = (int)strcspn(gp->locations, search); | ||||
|  | ||||
|     if (gp->locations[char_index] == '\0') | ||||
|     { | ||||
|         return not_found; | ||||
|     } | ||||
|  | ||||
|     return (coordinate){.x = char_index % (gp->width + 1), .y = char_index / (gp->width + 1)}; | ||||
| } | ||||
							
								
								
									
										176
									
								
								src/engine/grid/grid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/engine/grid/grid.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 2/10/2025 | ||||
|  * | ||||
|  * 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 struct { | ||||
|     int x; | ||||
|     int y; | ||||
| } coordinate; | ||||
|  | ||||
| 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. | ||||
|  * c: de positie. | ||||
|  * | ||||
|  * Uitvoer: 1 als de positie binnen het rooster valt, anders 0. | ||||
|  */ | ||||
| int grid_contains(const grid *gp, coordinate c); | ||||
|  | ||||
|  | ||||
| /* Kijk welk object er staat op een bepaalde positie in het rooster. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster | ||||
|  * c: de betreffende positie. | ||||
|  * | ||||
|  * Uitvoer: het object op die positie, of '\0' als de positie buiten het | ||||
|  *          rooster valt. | ||||
|  */ | ||||
| char grid_fetch(const grid *gp, coordinate c); | ||||
|  | ||||
|  | ||||
| /* Schrijf een bepaald object op een bepaalde positie in het rooster. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster | ||||
|  * ch: het object. | ||||
|  * c: de locatie. | ||||
|  * | ||||
|  * Effect: als c 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, char ch, coordinate 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 | ||||
|  * | ||||
|  * Uitvoer: de locatie van het object. (x and y zijn -1 als het niet gevonden is.) | ||||
|  */ | ||||
| coordinate grid_find(const grid *gp, char c); | ||||
|  | ||||
| #endif //_GRID_H | ||||
							
								
								
									
										87
									
								
								src/games/manual/assets/manual.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/games/manual/assets/manual.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| *--------------------------------------------* | ||||
| |                                            | | ||||
| |           !!! 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.                        | | ||||
| |                                            | | ||||
| |             *________________*             | | ||||
| |             |    GENERAL     |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |        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      |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Move down:             |   | | ||||
| |   |          's', arrow_down           |   | | ||||
| |   |                                    |   | | ||||
| |   |              Move up:              |   | | ||||
| |   |           'w', arrow_up            |   | | ||||
| |   |                                    |   | | ||||
| |   |              Select:               |   | | ||||
| |   |        'f', enter, space bar       |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| |                                            | | ||||
| |            *-----------------*             | | ||||
| |            |   MAZE RUNNER   |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Move up:               |   | | ||||
| |   |          'w', arrow_up             |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move down:              |   | | ||||
| |   |         's', arrow_down            |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move right:             |   | | ||||
| |   |         'd', arrow_right           |   | | ||||
| |   |                                    |   | | ||||
| |   |            Move left:              |   | | ||||
| |   |         'a', arrow_left            |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| |                                            | | ||||
| |            *-----------------*             | | ||||
| |            |      SNAKE      |             | | ||||
| |   *------------------------------------*   | | ||||
| |   |                                    |   | | ||||
| |   |             Turn up:               |   | | ||||
| |   |          'w', arrow_up             |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn down:              |   | | ||||
| |   |         's', arrow_down            |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn right:             |   | | ||||
| |   |         'd', arrow_right           |   | | ||||
| |   |                                    |   | | ||||
| |   |            Turn left:              |   | | ||||
| |   |         'a', arrow_left            |   | | ||||
| |   |                                    |   | | ||||
| |   *------------------------------------*   | | ||||
| *--------------------------------------------* | ||||
							
								
								
									
										36
									
								
								src/games/manual/manual.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/games/manual/manual.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 10/17/2025 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #include "manual.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include "../../engine/engine/grid_game_engine.h" | ||||
|  | ||||
| void manual(const coordinate display_location) { | ||||
|     erase(); | ||||
|     FILE *fp = fopen("assets/manual.txt", "r"); | ||||
|     if (fp == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|  | ||||
|     // Wait until ESCAPE or BACKSPACE is pressed. | ||||
|     timeout(200); | ||||
|     for (int ch = getch(); ch != KEY_ESCAPE && ch != KEY_BACKSPACE; ch = getch()) { | ||||
|         // Update the screen in the meantime to accommodate windows resizes. | ||||
|         show_grid_on_offset(grid, display_location); | ||||
|     } | ||||
|  | ||||
|     grid_cleanup(grid); | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/games/manual/manual.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/games/manual/manual.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 10/17/2025 | ||||
|  * | ||||
|  * Provides a way to display the minigame manual in the assets in game. | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_MANUAL_H | ||||
| #define MINIGAME_MENU_MANUAL_H | ||||
| #include "../../engine/engine/grid_game_engine.h" | ||||
|  | ||||
| /* | ||||
|  * An in game manual for the minigames menu. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before opening the manual | ||||
|  * | ||||
|  * Input: | ||||
|  * display_location: The location to display the manual. | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to display the manual. | ||||
|  * | ||||
|  * Controls: | ||||
|  * Press ESCAPE or BACKSPACE to exit the manual. | ||||
|  */ | ||||
| void manual(coordinate display_location); | ||||
|  | ||||
| #endif //MINIGAME_MENU_MANUAL_H | ||||
							
								
								
									
										153
									
								
								src/games/maze-runner/maze_runner.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/games/maze-runner/maze_runner.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 6/10/2025 | ||||
|  */ | ||||
|  | ||||
| #include "maze_runner.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "../../engine/engine/grid_game_engine.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 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; | ||||
|     } | ||||
|     grid *gp = grid_create_from_file(fh); | ||||
|     fclose(fh); | ||||
|  | ||||
|     // Bepaal of het lezen van het rooster is gelukt. | ||||
|     if (gp == NULL) { | ||||
|         fprintf(stderr, "Kan rooster niet maken.\n"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return gp; | ||||
| } | ||||
|  | ||||
| /* Voert de benodigde veranderingen in het rooster door als de speler in een | ||||
|  * bepaalde richting probeert te bewegen. | ||||
|  * Input: | ||||
|  * gp: een pointer naar het rooster | ||||
|  * offset_vector: 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. | ||||
|  */ | ||||
| static void maze_runner_move(grid *gp, const coordinate offset_vector) { | ||||
|     const coordinate player_position = grid_find(gp, LIVING_PLAYER); | ||||
|  | ||||
|     if (player_position.y == -1) { | ||||
|         printf("Player not found!"); | ||||
|         // State begin for the hackerman ending screen. | ||||
|         grid_put_state(gp, STATE_BEGIN); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     coordinate new_player_position = player_position; | ||||
|     new_player_position.x += offset_vector.x; | ||||
|     new_player_position.y += offset_vector.y; | ||||
|  | ||||
|     if (grid_contains(gp, new_player_position) == 1) { | ||||
|         const char new_location = grid_fetch(gp, new_player_position); | ||||
|         switch (new_location) { | ||||
|             case WALL: | ||||
|                 break; | ||||
|             case TRAP: | ||||
|                 grid_put_state(gp, STATE_VERLOREN); | ||||
|  | ||||
|                 enable_highlight(RED); | ||||
|                 update_grid(gp, DEAD_PLAYER, player_position); | ||||
|                 disable_highlight(RED); | ||||
|                 break; | ||||
|             case EMPTY: | ||||
|                 update_grid(gp, EMPTY, player_position); | ||||
|                 update_grid(gp, LIVING_PLAYER, new_player_position); | ||||
|                 break; | ||||
|             case MAZE_EXIT: | ||||
|                 update_grid(gp, EMPTY, player_position); | ||||
|                 grid_put_state(gp, STATE_GEWONNEN); | ||||
|                 break; | ||||
|         } | ||||
|         refresh(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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(grid *gp) { | ||||
|     coordinate offset_vector = {.x = 0, .y = 0}; | ||||
|     switch (getch()) { | ||||
|         case KEY_UP: // fallthrough | ||||
|         case 'w': | ||||
|         case 'W': | ||||
|             offset_vector.y--; | ||||
|             break; | ||||
|         case KEY_DOWN: // fallthrough | ||||
|         case 's': | ||||
|         case 'S': | ||||
|             offset_vector.y++; | ||||
|             break; | ||||
|         case KEY_LEFT: // fallthrough | ||||
|         case 'a': | ||||
|         case 'A': | ||||
|             offset_vector.x--; | ||||
|             break; | ||||
|         case KEY_RIGHT: // fallthrough | ||||
|         case 'd': | ||||
|         case 'D': | ||||
|             offset_vector.x++; | ||||
|             break; | ||||
|         case 'p': | ||||
|         case KEY_BACKSPACE: | ||||
|         case KEY_ESCAPE: | ||||
|             grid_put_state(gp, STATE_QUIT); | ||||
|             return; | ||||
|     } | ||||
|     maze_runner_move(gp, offset_vector); | ||||
| } | ||||
|  | ||||
| void maze_runner(void) { | ||||
|     // Voorbereiding. | ||||
|     grid *gp = get_maze(); | ||||
|     if (gp == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     show_grid(gp); | ||||
|     grid_put_state(gp, STATE_AAN_HET_SPELEN); | ||||
|  | ||||
|     // Game zelf. | ||||
|     while (grid_fetch_state(gp) == STATE_AAN_HET_SPELEN) { | ||||
|         speel_maze(gp); | ||||
|     } | ||||
|  | ||||
|     // Exit game. | ||||
|     game_exit_message(gp, (coordinate){0, grid_height(gp) + 2}); | ||||
|     grid_cleanup(gp); | ||||
| } | ||||
| @@ -1,9 +1,11 @@ | ||||
| /*
 | ||||
|  * Created by snapshot112 on 6/10/2025 | ||||
|  * | ||||
|  * Naam: Jeroen Boxhoorn | ||||
|  * UvAnetID: 16333969 | ||||
|  * Studie: BSc Informatica | ||||
|  * Studie: BSC Informatica | ||||
|  * | ||||
|  * A game of maze runner configured to run on the grid game engine. | ||||
|  * A game of maze runner build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling maze_runner(); | ||||
|  * | ||||
| @@ -23,7 +25,7 @@ | ||||
| #define MINIGAME_MENU_MAZE_RUNNER_H | ||||
| 
 | ||||
| /*
 | ||||
|  * A game of maze runner configured to run on the grid game engine. | ||||
|  * A game of maze runner build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling maze_runner(); | ||||
|  * | ||||
| @@ -32,7 +34,7 @@ | ||||
|  * | ||||
|  * Controls: | ||||
|  * use WSAD or arrow keys to move through the maze. | ||||
|  * Use BACKSPACE to exit the game early. | ||||
|  * Use BACKSPACE or ESCAPE to exit the game early. | ||||
|  */ | ||||
| void maze_runner(void); | ||||
| 
 | ||||
| @@ -1,16 +1,16 @@ | ||||
| //
 | ||||
| // Created by snapshot112 on 10/15/25.
 | ||||
| //
 | ||||
| /*
 | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  */ | ||||
| 
 | ||||
| #include "minesweeper.h" | ||||
| 
 | ||||
| #include <ncurses.h> | ||||
| 
 | ||||
| #include "grid_game_engine.h" | ||||
| #include "../../engine/engine/grid_game_engine.h" | ||||
| 
 | ||||
| void minesweeper() { | ||||
|     clear(); | ||||
|     mvprintw(0,0, "Minesweeper has not yet been created"); | ||||
|     graceful_exit(); | ||||
|     graceful_exit((coordinate){0, 3}); | ||||
|     refresh(); | ||||
| } | ||||
| @@ -1,18 +1,18 @@ | ||||
| /*
 | ||||
|  * Created by snapshot112 on 10/15/25. | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * A game of minesweeper runner configured to run on the grid game engine. | ||||
|  * A game of minesweeper build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  * Please make sure to include and initialize the game engine before calling minesweeper(); | ||||
|  */ | ||||
| 
 | ||||
| #ifndef MINIGAME_MENU_MINESWEEPER_H | ||||
| #define MINIGAME_MENU_MINESWEEPER_H | ||||
| 
 | ||||
| /*
 | ||||
|  * A game of minesweeper configured to run on the grid game engine. | ||||
|  * A game of minesweeper build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  * Please make sure to include and initialize the game engine before calling minesweeper(); | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to play a game of minesweeper. | ||||
| @@ -1,23 +1,31 @@ | ||||
| //
 | ||||
| // Created by snapshot112 on 10/15/25.
 | ||||
| //
 | ||||
| /*
 | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  */ | ||||
| 
 | ||||
| #include "minigame_menu.h" | ||||
| 
 | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "grid_game_engine.h" | ||||
| #include "maze_runner.h" | ||||
| #include "minesweeper.h" | ||||
| #include "rooster.h" | ||||
| #include "snake.h" | ||||
| #include "../../engine/engine/grid_game_engine.h" | ||||
| #include "../manual/manual.h" | ||||
| #include "../maze-runner/maze_runner.h" | ||||
| #include "../minesweeper/minesweeper.h" | ||||
| #include "../snake/snake.h" | ||||
| 
 | ||||
| #define AMOUNT_OF_MENU_OPTIONS 4 | ||||
| #define AMOUNT_OF_MENU_OPTIONS 5 | ||||
| 
 | ||||
| static game SELECTED_GAME = GAME_MAZE_RUNNER; | ||||
| static int OFFSET_Y = 5; | ||||
| static int OFFSET_X = 5; | ||||
| typedef enum { | ||||
|     GAME_MANUAL = 0, | ||||
|     GAME_MAZE_RUNNER = 1, | ||||
|     GAME_SNAKE = 2, | ||||
|     GAME_MINESWEEPER = 3, | ||||
|     GAME_QUIT = 4, | ||||
| } game; | ||||
| 
 | ||||
| static grid *MENU; | ||||
| static game SELECTED_GAME = GAME_MANUAL; | ||||
| static coordinate OFFSET = {.x = 5, .y = 5}; | ||||
| 
 | ||||
| /*
 | ||||
|  * Launch a game from the menu. | ||||
| @@ -26,8 +34,12 @@ static int OFFSET_X = 5; | ||||
|  * menu: A pointer to the menu grid. | ||||
|  * game: The game you want to launch. | ||||
|  */ | ||||
| static void launch_game(rooster *menu, const game game) { | ||||
| static void launch_game(const game game) { | ||||
|     clear(); | ||||
|     switch (game) { | ||||
|         case GAME_MANUAL: | ||||
|             manual((coordinate){0,0}); | ||||
|             break; | ||||
|         case GAME_MAZE_RUNNER: | ||||
|             maze_runner(); | ||||
|             break; | ||||
| @@ -46,23 +58,22 @@ static void launch_game(rooster *menu, const game game) { | ||||
|  * Input: | ||||
|  * menu:        A pointer to the menu grid. | ||||
|  * target:      The menu option to highlight. | ||||
|  * offset_x:    The x offset of the menu. | ||||
|  * offset_y:    The y offset of the menu. | ||||
|  * | ||||
|  * Side effects: | ||||
|  * 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_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); | ||||
|             mvprintw(OFFSET_Y + (int)SELECTED_GAME, OFFSET_X,  "%s", row); | ||||
|             char* row = grid_fetch_row(menu, SELECTED_GAME); | ||||
|             mvprintw(OFFSET.y + (int)SELECTED_GAME, OFFSET.x,  "%s", row); | ||||
|             free(row); | ||||
| 
 | ||||
|             attroff(COLOR_PAIR(GREEN)); | ||||
| @@ -75,16 +86,14 @@ static void menu_highlight(const rooster *menu) { | ||||
|  * Input: | ||||
|  * menu:                A pointer to the menu grid. | ||||
|  * default_selection:   The starting selection in the menu. | ||||
|  * offset_x:            The x offset of the menu. | ||||
|  * offset_y:            The y offset of the menu. | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Displays the menu | ||||
|  */ | ||||
| static void show_menu(const rooster *menu) { | ||||
|     clear(); | ||||
|     show_grid_on_offset(menu, OFFSET_X, OFFSET_Y); | ||||
|     menu_highlight(menu); | ||||
| static void show_menu() { | ||||
|     erase(); | ||||
|     show_grid_on_offset(MENU, OFFSET); | ||||
|     menu_highlight(MENU); | ||||
|     refresh(); | ||||
| } | ||||
| 
 | ||||
| @@ -114,11 +123,10 @@ static void menu_move(const int offset) { | ||||
|  * 0: Continue running. | ||||
|  * 1: Exit the menu. | ||||
|  * | ||||
|  * | ||||
|  * Side Effect: | ||||
|  * Changes the SELECTED_GAME as needed and launches selected games on select. | ||||
|  */ | ||||
| static int navigate_menu(rooster *menu) { | ||||
| static int navigate_menu(void) { | ||||
|     switch (getch()) { | ||||
|         case KEY_UP: | ||||
|         case 'w': | ||||
| @@ -131,14 +139,17 @@ static int navigate_menu(rooster *menu) { | ||||
|             menu_move(1); | ||||
|             break; | ||||
|         case KEY_ENTER: | ||||
|         case '\n': | ||||
|         case 'f': | ||||
|         case 'F': | ||||
|         case ' ': | ||||
|             if (SELECTED_GAME == GAME_QUIT) { | ||||
|                 return 1; | ||||
|             } | ||||
|             launch_game(menu, SELECTED_GAME); | ||||
|             launch_game(SELECTED_GAME); | ||||
|             break; | ||||
|         case KEY_BACKSPACE: | ||||
|         case KEY_ESCAPE: | ||||
|             return 1; | ||||
|     } | ||||
|     return 0; | ||||
| @@ -146,28 +157,25 @@ static int navigate_menu(rooster *menu) { | ||||
| 
 | ||||
| /*
 | ||||
|  * Create the menu grid. | ||||
|  * | ||||
|  * Output: | ||||
|  * A pointer to the menu grid. | ||||
|  */ | ||||
| static rooster *initialize_menu(void) { | ||||
|     char menu[] = "Maze Runner\n" | ||||
|                   "   Snake   \n" | ||||
|                   "Minesweeper\n" | ||||
|                   "   Leave   \n"; | ||||
|     rooster *rp = rooster_maak(menu); | ||||
|     return rp; | ||||
| static void initialize_menu(void) { | ||||
|     const char menu[] = "How to play\n" | ||||
|                         "Maze Runner\n" | ||||
|                         "   Snake   \n" | ||||
|                         "Minesweeper\n" | ||||
|                         "   Leave   \n"; | ||||
|     MENU = grid_create_from_string(menu); | ||||
| } | ||||
| 
 | ||||
| void minigame_menu(void) { | ||||
|     rooster *menu = initialize_menu(); | ||||
|     initialize_menu(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         show_menu(menu); | ||||
|         if (navigate_menu(menu) == 1) { | ||||
|         show_menu(); | ||||
|         if (navigate_menu() == 1) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     rooster_klaar(menu); | ||||
|     grid_cleanup(MENU); | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/games/minigame-menu/minigame_menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/games/minigame-menu/minigame_menu.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * A minigame menu for games build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling menu(); | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_MINIGAME_MENU_H | ||||
| #define MINIGAME_MENU_MINIGAME_MENU_H | ||||
|  | ||||
| /* | ||||
|  * A minigame menu for games build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling menu(); | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to display a minigame menu. | ||||
|  * | ||||
|  * Controls: | ||||
|  * 'w'/'arr_up':        Next menu item. | ||||
|  * 's'/'arr_down':      Previous menu item. | ||||
|  * 'f'/'ENTER':         Select current menu item. | ||||
|  * 'BACKSPACE'/'ESC':   Exit the menu. | ||||
|  */ | ||||
| void minigame_menu(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_MINIGAME_MENU_H | ||||
							
								
								
									
										449
									
								
								src/games/snake/snake.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										449
									
								
								src/games/snake/snake.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,449 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  */ | ||||
|  | ||||
| #define _POSIX_C_SOURCE 199309 | ||||
|  | ||||
| #include "snake.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
| #include <time.h> | ||||
| #include <pthread.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "../../engine/engine/grid_game_engine.h" | ||||
|  | ||||
| #define CELL_EMPTY ' ' | ||||
| #define CELL_FOOD '$' | ||||
| #define CELL_WALL '#' | ||||
| #define DIRECTION_COUNT 4 | ||||
| #define OFFSET_X 6 | ||||
| #define OFFSET_Y 3 | ||||
|  | ||||
| typedef enum { | ||||
|     SNAKE_MOVE = 0, | ||||
|     SNAKE_EAT = 1, | ||||
|     SNAKE_DIE = 2 | ||||
| } snake_action; | ||||
|  | ||||
| typedef enum { | ||||
|     DIRECTION_UP = 0, | ||||
|     DIRECTION_RIGHT = 1, | ||||
|     DIRECTION_DOWN = 2, | ||||
|     DIRECTION_LEFT = 3 | ||||
| } direction; | ||||
|  | ||||
| // 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 grid *GRID; | ||||
| static pthread_mutex_t SNAKE_MUTEX; | ||||
|  | ||||
| /* | ||||
|  * Create a snake body part. | ||||
|  * | ||||
|  * Input: | ||||
|  * dir: the direction the body part should point to. | ||||
|  * | ||||
|  * Output: | ||||
|  * a character representing that body part. | ||||
|  */ | ||||
| static char get_body_part(const direction dir) { | ||||
|     return (char)(dir + '0'); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Gets the direction of a body part. | ||||
|  * | ||||
|  * Input: | ||||
|  * body_part: A part of the snake's body. | ||||
|  * | ||||
|  * Output: | ||||
|  * The direction the next body part is pointing to. | ||||
|  */ | ||||
| static direction get_body_part_direction(const char body_part) { | ||||
|     return (direction)(body_part - '0'); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Create a grid for snake with a given height and width. | ||||
|  * | ||||
|  * Input: | ||||
|  * height: The height of the map. | ||||
|  * width:  The width of the map. | ||||
|  * | ||||
|  * Returns: | ||||
|  * A pointer to the grid. | ||||
|  */ | ||||
| static void create_grid(void) { | ||||
|     const int grid_size = (MAP_WIDTH + 1) * MAP_HEIGHT + 1; | ||||
|     char *map = malloc(grid_size * sizeof(char)); | ||||
|     if (map == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (int i = 1; i <= (MAP_WIDTH + 1) * MAP_HEIGHT; i++) { | ||||
|         // Also subtract the null terminator | ||||
|         const int bottom_line = i > grid_size - (MAP_WIDTH + 2); | ||||
|         const int top_line = i < MAP_WIDTH + 1; | ||||
|  | ||||
|         const int line_position = modulo(i, MAP_WIDTH + 1); | ||||
|  | ||||
|         const int line_start = line_position == 1; | ||||
|         const int line_end = line_position == MAP_WIDTH; | ||||
|  | ||||
|         const int newline = line_position == 0; | ||||
|  | ||||
|         if (newline) { | ||||
|             map[i - 1] = '\n'; | ||||
|         } else if (top_line || bottom_line || line_start || line_end) { | ||||
|             map[i - 1] = CELL_WALL; | ||||
|         } else { | ||||
|             map[i - 1] = CELL_EMPTY; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     map[grid_size - 1] = '\0'; | ||||
|  | ||||
|     GRID = grid_create_from_string(map); | ||||
|  | ||||
|     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(void) { | ||||
|     coordinate empty_spots[MAP_HEIGHT * MAP_WIDTH]; | ||||
|  | ||||
|     int available_spots = 0; | ||||
|  | ||||
|     for (int x = 0; x < MAP_WIDTH; x++) { | ||||
|         for (int y = 0; y < MAP_HEIGHT; y++) { | ||||
|             const coordinate location = {x, y}; | ||||
|             if (grid_fetch(GRID, location) == CELL_EMPTY) { | ||||
|                 empty_spots[available_spots] = location; | ||||
|                 available_spots++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const coordinate food_location = empty_spots[modulo(rand(), available_spots)]; | ||||
|  | ||||
|     grid_put(GRID, CELL_FOOD, food_location); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Setup the game(map) | ||||
|  * | ||||
|  * Output: | ||||
|  * A pointer to the game grid. | ||||
|  * | ||||
|  * 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}; | ||||
|  | ||||
|     // Create the first body part and spawn the first piece of food. | ||||
|     grid_put(GRID, get_body_part(CURRENT_DIRECTION), SNAKE_HEAD); | ||||
|     generate_food(); | ||||
|  | ||||
|     pthread_mutex_init(&SNAKE_MUTEX, NULL); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Checks what happens when the snake moves over a given char. | ||||
|  * | ||||
|  * Input: | ||||
|  * c: The char to check. | ||||
|  * | ||||
|  * Returns: | ||||
|  * 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(const char c) { | ||||
|     if (c == CELL_EMPTY) { | ||||
|         return SNAKE_MOVE; | ||||
|     } | ||||
|  | ||||
|     if (c == CELL_FOOD) { | ||||
|         return SNAKE_EAT; | ||||
|     } | ||||
|  | ||||
|     return SNAKE_DIE; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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)); | ||||
|  | ||||
|     if (action == SNAKE_DIE) { | ||||
|         grid_put_state(GRID, STATE_VERLOREN); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (action == SNAKE_MOVE) { | ||||
|         coordinate new_tail = SNAKE_TAIL; | ||||
|  | ||||
|         switch (get_body_part_direction(grid_fetch(GRID, SNAKE_TAIL))) { | ||||
|             case DIRECTION_UP: | ||||
|                 new_tail.y--; | ||||
|                 break; | ||||
|             case DIRECTION_RIGHT: | ||||
|                 new_tail.x++; | ||||
|                 break; | ||||
|             case DIRECTION_DOWN: | ||||
|                 new_tail.y++; | ||||
|                 break; | ||||
|             case DIRECTION_LEFT: | ||||
|                 new_tail.x--; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         grid_put(GRID, CELL_EMPTY, SNAKE_TAIL); | ||||
|         SNAKE_TAIL = new_tail; | ||||
|     } | ||||
|  | ||||
|     // New head placed after tail moves. It can occupy the empty space created by the tail moving. | ||||
|     grid_put(GRID, get_body_part(CURRENT_DIRECTION), new_location); | ||||
|     SNAKE_HEAD = new_location; | ||||
|     PREVIOUS_DIRECTION = CURRENT_DIRECTION; | ||||
|  | ||||
|     if (action == SNAKE_EAT) { | ||||
|         generate_food(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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 (grid_fetch_state(GRID) == STATE_AAN_HET_SPELEN) { | ||||
|         nanosleep(&timer, NULL); | ||||
|         pthread_mutex_lock(&SNAKE_MUTEX); | ||||
|         coordinate new_location = SNAKE_HEAD; | ||||
|         switch (CURRENT_DIRECTION) { | ||||
|             case DIRECTION_UP: | ||||
|                 new_location.y--; | ||||
|                 break; | ||||
|             case DIRECTION_RIGHT: | ||||
|                 new_location.x++; | ||||
|                 break; | ||||
|             case DIRECTION_DOWN: | ||||
|                 new_location.y++; | ||||
|                 break; | ||||
|             case DIRECTION_LEFT: | ||||
|                 new_location.x--; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         update_snake(new_location); | ||||
|         show_grid_on_offset(GRID, RENDER_AT); | ||||
|         pthread_mutex_unlock(&SNAKE_MUTEX); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
|     } | ||||
|     grid_put(GRID, get_body_part(dir), SNAKE_HEAD); | ||||
|     CURRENT_DIRECTION = dir; | ||||
|     show_grid_on_offset(GRID, RENDER_AT); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
|         const int c = getch(); | ||||
|         pthread_mutex_lock(&SNAKE_MUTEX); | ||||
|         switch (c) { | ||||
|             case KEY_UP: // fallthrough | ||||
|             case 'w': | ||||
|             case 'W': | ||||
|                 turn_snake(DIRECTION_UP); | ||||
|                 break; | ||||
|             case KEY_DOWN: // fallthrough | ||||
|             case 's': | ||||
|             case 'S': | ||||
|                 turn_snake(DIRECTION_DOWN); | ||||
|                 break; | ||||
|             case KEY_LEFT: // fallthrough | ||||
|             case 'a': | ||||
|             case 'A': | ||||
|                 turn_snake(DIRECTION_LEFT); | ||||
|                 break; | ||||
|             case KEY_RIGHT: // fallthrough | ||||
|             case 'd': | ||||
|             case 'D': | ||||
|                 turn_snake(DIRECTION_RIGHT); | ||||
|                 break; | ||||
|             case KEY_BACKSPACE: | ||||
|             case KEY_ESCAPE: | ||||
|                 grid_put_state(GRID, STATE_QUIT); | ||||
|                 break; | ||||
|         } | ||||
|         pthread_mutex_unlock(&SNAKE_MUTEX); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
|     } | ||||
|  | ||||
|     // Show game. | ||||
|     erase(); | ||||
|     show_grid_on_offset(GRID, RENDER_AT); | ||||
|  | ||||
|     wait_for_start(); | ||||
|  | ||||
|     if (grid_fetch_state(GRID) == STATE_AAN_HET_SPELEN) { | ||||
|         // Create and start necessary threads. | ||||
|         pthread_t input_thread; | ||||
|         pthread_t game_tick_thread; | ||||
|  | ||||
|         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_thread, NULL); | ||||
|         pthread_join(input_thread, NULL); | ||||
|     } | ||||
|  | ||||
|     game_exit_message(GRID, MESSAGE_LOCATION); | ||||
|  | ||||
|     snake_cleanup(); | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  * Created by snapshot112 on 10/15/25. | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * A game of maze runner configured to run on the grid game engine. | ||||
|  * A game of maze runner build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  */ | ||||
| @@ -10,7 +10,7 @@ | ||||
| #define MINIGAME_MENU_SNAKE_H | ||||
| 
 | ||||
| /*
 | ||||
|  * A game of snake configured to run on the grid game engine. | ||||
|  * A game of snake build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  * | ||||
| @@ -19,7 +19,7 @@ | ||||
|  * | ||||
|  * Controls: | ||||
|  * Use WSAD or arrow keys to redirect the snake. | ||||
|  * Use BACKSPACE to exit the game early. | ||||
|  * Use BACKSPACE or ESCAPE to exit the game early. | ||||
|  */ | ||||
| void snake(void); | ||||
| 
 | ||||
							
								
								
									
										34
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * Naam: Jeroen Boxhoorn | ||||
|  * UvAnetID: 16333969 | ||||
|  * Studie: BSC Informatica | ||||
|  * | ||||
|  * A minigame menu that lets you play games on the grid game engine. | ||||
|  * | ||||
|  * Currently the following games are included: | ||||
|  * - maze runner | ||||
|  * - snake | ||||
|  * - minesweeper | ||||
|  * | ||||
|  * A user manual can be found in the assets or by selected it in the menu using ENTER or 'f'. | ||||
|  */ | ||||
|  | ||||
| #include "engine/engine/grid_game_engine.h" | ||||
| #include "games/minigame-menu/minigame_menu.h" | ||||
|  | ||||
| /* | ||||
|  * Play minigame menu. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * Minigame menu starts. | ||||
|  */ | ||||
| int main(void) { | ||||
|     init_engine(); | ||||
|  | ||||
|     // Speel het spel. | ||||
|     minigame_menu(); | ||||
|  | ||||
|     cleanup_engine(); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user