forked from snapshot112/minigame-menu
		
	refactored file structure
This commit is contained in:
		
							
								
								
									
										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 int starting_x, const int starting_y) { | ||||
|     const int height = grid_height(gp); | ||||
|  | ||||
|     for (int y = 0; y < height; y++) { | ||||
|         char *rij = grid_fetch_row(gp, y); | ||||
|         mvprintw(starting_y + y, starting_x, "%s", rij); | ||||
|         free(rij); | ||||
|     } | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| void show_grid(const grid *gp) { | ||||
|     show_grid_on_offset(gp, 0, 0); | ||||
| } | ||||
|  | ||||
| void update_grid(grid *gp, const char c, const int x, const int y) { | ||||
|     if (grid_put(gp, x, y, c) == 1) { | ||||
|         mvaddch(y, x, c); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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(); | ||||
| } | ||||
							
								
								
									
										157
									
								
								src/engine/engine/grid_game_engine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/engine/engine/grid_game_engine.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  | ||||
| typedef struct { | ||||
|     int x; | ||||
|     int y; | ||||
| } coordinate; | ||||
|  | ||||
| // 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. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The console is cleared and the grid is printed. | ||||
|  */ | ||||
| void show_grid_on_offset(const grid *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 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. | ||||
|  * 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(grid *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. | ||||
|  * 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 | ||||
							
								
								
									
										305
									
								
								src/engine/grid/grid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								src/engine/grid/grid.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,305 @@ | ||||
| /* | ||||
|  * 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 int x, const int y) { | ||||
|     return y * (gp->width + 1) + x; | ||||
| } | ||||
|  | ||||
| grid *grid_create_from_string(const char* input) { | ||||
|     int width = 0; | ||||
|     int height = 0; | ||||
|     const size_t len = strlen(input) / sizeof(char); | ||||
|     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 int x, const int y) { | ||||
|     if (gp != NULL && gp->locations != NULL) { | ||||
|         if (x >= 0 && y >= 0 && x < gp->width && y < gp->height) | ||||
|         { | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| char grid_fetch(const grid *gp, const int x, const int y) { | ||||
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, x, y) == 1) { | ||||
|         return gp->locations[internal_location(gp, x, y)]; | ||||
|     } | ||||
|     return '\0'; | ||||
| } | ||||
|  | ||||
| int grid_put(grid *gp, const int x, const int y, const char c) { | ||||
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, x, y) == 1) { | ||||
|         gp->locations[internal_location(gp, x, y)] = c; | ||||
|         return 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| char *grid_fetch_row(const grid *gp, const int y) { | ||||
| 	if (gp != NULL && gp->locations != NULL && grid_contains(gp, 0, y) == 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, 0, y)], 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; | ||||
| } | ||||
|  | ||||
| void grid_find(const grid *gp, const char c, int *x, int *y) { | ||||
|     if (gp == NULL || gp->locations == NULL) { | ||||
|         *x = -1; | ||||
|         *y = -1; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char search[2] = {c}; | ||||
|  | ||||
|     const int char_index = (int)strcspn(gp->locations, search); | ||||
|  | ||||
|     if (gp->locations[char_index] == '\0') | ||||
|     { | ||||
|         *x = -1; | ||||
|         *y = -1; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *x = char_index % (gp->width + 1); | ||||
|     *y = char_index / (gp->width + 1); | ||||
| } | ||||
							
								
								
									
										174
									
								
								src/engine/grid/grid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/engine/grid/grid.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| /* | ||||
|  * 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 enum { | ||||
|     STATE_BEGIN, | ||||
|     STATE_AAN_HET_SPELEN, | ||||
|     STATE_GEWONNEN, | ||||
|     STATE_VERLOREN, | ||||
|     STATE_QUIT | ||||
| } state; | ||||
|  | ||||
| /* Maak een rooster op basis van de data in de gegeven stream. | ||||
|  * | ||||
|  * fh: de stream waaruit het doolhof gelezen moet worden. | ||||
|  * | ||||
|  * Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is | ||||
|  *          gealloceerd), dat overeenkomt met de gegeven beschrijving. | ||||
|  *          De begintoestand is BEGIN. | ||||
|  * | ||||
|  *          Als de beschrijving niet consistent is (bijvoorbeeld | ||||
|  *          niet alle rijen zijn even lang, of er klopt iets anders niet), of | ||||
|  *          als niet voldoende geheugen kan worden gereserveerd, dan wordt | ||||
|  *          NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen | ||||
|  *          achter.) | ||||
|  */ | ||||
| grid *grid_create_from_file(FILE *fh); | ||||
|  | ||||
| /* | ||||
|  * Maak een rooster op basis van een gegeven string. | ||||
|  * | ||||
|  * Uitvoer: als alles goed gaat, een pointer naar een rooster (die op de heap is | ||||
|  *         gealloceerd), dat overeenkomt met de gegeven beschrijving. | ||||
|  *         De begintoestand is BEGIN. | ||||
|  * | ||||
|  *         Als de beschrijving niet consistent is (bijvoorbeeld | ||||
|  *         niet alle rijen zijn even lang, of er klopt iets anders niet), of | ||||
|  *         als niet voldoende geheugen kan worden gereserveerd, dan wordt | ||||
|  *         NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen | ||||
|  *         achter.) | ||||
|  */ | ||||
| grid *grid_create_from_string(const char* input); | ||||
|  | ||||
| /* | ||||
|  * Haal een rij uit het rooster op. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: een pointer naar het rooster | ||||
|  * y: de y-coordinaat van de rij die je wilt hebben. | ||||
|  * | ||||
|  * Output: | ||||
|  * Een pointer naar een nieuwe string met daarin alle karakters in die rij. | ||||
|  */ | ||||
| char *grid_fetch_row(const grid *gp, int y); | ||||
|  | ||||
| /* | ||||
|  * Maak een kopie van een rooster | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: Een pointer naar het rooster. | ||||
|  * | ||||
|  * Output: | ||||
|  * Een pointer naar het kopie. | ||||
|  */ | ||||
| grid *grid_copy(const grid *gp); | ||||
|  | ||||
| /* Vraag de huidige toestand van het spel op. | ||||
|  * | ||||
|  * Input: | ||||
|  * gp: een pointer naar het rooster. | ||||
|  * | ||||
|  * Uitvoer: de toestand. | ||||
|  */ | ||||
| state grid_fetch_state(const grid *gp); | ||||
|  | ||||
|  | ||||
| /* Verander de huidige toestand van het spel. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster. | ||||
|  * t:  de nieuwe toestand. | ||||
|  */ | ||||
| void grid_put_state(grid *gp, state t); | ||||
|  | ||||
|  | ||||
| /* Geef alle resources vrij die zijn gealloceerd voor een rooster. | ||||
|  * De rooster pointer is na aanroep van deze functie niet meer bruikbaar. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster. | ||||
|  */ | ||||
| void grid_cleanup(grid *gp); | ||||
|  | ||||
|  | ||||
| /* Vraag de breedte van het rooster op, dat wil zeggen, het aantal kolommen. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster. | ||||
|  * | ||||
|  * Uitvoer: de breedte. | ||||
|  */ | ||||
| int grid_width(const grid *gp); | ||||
|  | ||||
|  | ||||
| /* Vraag de hoogte van het rooster op, dat wil zeggen, het aantal rijen. | ||||
|  * | ||||
|  * gp: een pointer naar het rooster. | ||||
|  * | ||||
|  * Uitvoer: de hoogte. | ||||
|  */ | ||||
| int grid_height(const grid *gp); | ||||
|  | ||||
|  | ||||
| /* Kijk of de gegeven positie binnen het rooster valt. | ||||
|  * | ||||
|  * gp:  een pointer naar het rooster. | ||||
|  * x,y: de positie. | ||||
|  * | ||||
|  * Uitvoer: 1 als de positie binnen het rooster valt, anders 0. | ||||
|  */ | ||||
| int grid_contains(const grid *gp, int x, int y); | ||||
|  | ||||
|  | ||||
| /* Kijk welk object er staat op een bepaalde positie in het rooster. | ||||
|  * | ||||
|  * gp : een pointer naar het rooster | ||||
|  * x,y: de betreffende positie. | ||||
|  * | ||||
|  * Uitvoer: het object op die positie, of '\0' als de positie buiten het | ||||
|  *          rooster valt. | ||||
|  */ | ||||
| char grid_fetch(const grid *gp, int x, int y); | ||||
|  | ||||
|  | ||||
| /* Schrijf een bepaald object op een bepaalde positie in het rooster. | ||||
|  * | ||||
|  * gp : een pointer naar het rooster | ||||
|  * x,y: de betreffende positie. | ||||
|  * c  : het object. | ||||
|  * | ||||
|  * Effect: als (x,y) binnen het rooster ligt, wordt het object op | ||||
|  *         de opgegeven positie geplaatst, anders verandert er niets. | ||||
|  * | ||||
|  * Uitvoer: 1 als het object is geplaatst, of 0 als het buiten de grenzen lag. | ||||
|  */ | ||||
| int grid_put(grid *gp, int x, int y, char c); | ||||
|  | ||||
|  | ||||
| /* Zoek een bepaald object in het rooster, en geef de coordinaten van het | ||||
|  * object terug via de gegeven pointers. Let op: als er meerdere objecten van | ||||
|  * het gezochte soort in het rooster voorkomen, is niet gedefinieerd van welke | ||||
|  * de positie wordt gevonden. | ||||
|  * | ||||
|  * gp : een pointer naar het rooster | ||||
|  * c  : het object dat moet worden gezocht | ||||
|  * x,y: pointers naar de geheugenlocaties waar de gevonden positie moet worden | ||||
|  *      geschreven. | ||||
|  * | ||||
|  * Uitvoer: via de pointers x en y. Als het object niet wordt gevonden worden | ||||
|  *          *x en *y op -1 gezet. | ||||
|  */ | ||||
| void grid_find(const grid *gp, char c, int *x, int *y); | ||||
|  | ||||
| #endif //_GRID_H | ||||
							
								
								
									
										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/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.x, display_location.y); | ||||
|  | ||||
|     // 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.x, display_location.y); | ||||
|     } | ||||
|  | ||||
|     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/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 | ||||
							
								
								
									
										13
									
								
								src/games/maze-runner/assets/maze.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/games/maze-runner/assets/maze.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| ####################### | ||||
| #*#                   # | ||||
| # #  $   ###########  # | ||||
| # ########            # | ||||
| #        # ############ | ||||
| # ###### #            # | ||||
| # #    # ############ # | ||||
| # #### #     #        # | ||||
| #    # ##### #  XXXXXX# | ||||
| # XXX#       #        # | ||||
| #    #########XXXXXXX # | ||||
| #  X                  # | ||||
| ####################### | ||||
							
								
								
									
										147
									
								
								src/games/maze-runner/maze_runner.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/games/maze-runner/maze_runner.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 6/10/2025 | ||||
|  */ | ||||
|  | ||||
| #include "maze_runner.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "../../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 | ||||
|  * 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_move(grid *gp, const int dx, const int dy) { | ||||
|     coordinate player_position = {0, 0}; | ||||
|     grid_find(gp, LIVING_PLAYER, &player_position.x, &player_position.y); | ||||
|  | ||||
|     if (player_position.y == -1) { | ||||
|         printf("Player not found!"); | ||||
|         grid_put_state(gp, STATE_BEGIN); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (grid_contains(gp, player_position.x + dx, player_position.y + dy) == 1) { | ||||
|         char new_location = grid_fetch(gp, player_position.x + dx, player_position.y + dy); | ||||
|         switch (new_location) { | ||||
|             case WALL: | ||||
|                 break; | ||||
|             case TRAP: | ||||
|                 grid_put_state(gp, STATE_VERLOREN); | ||||
|  | ||||
|                 enable_highlight(RED); | ||||
|                 update_grid(gp, DEAD_PLAYER, player_position.x, player_position.y); | ||||
|                 disable_highlight(RED); | ||||
|                 break; | ||||
|             case EMPTY: | ||||
|                 update_grid(gp, EMPTY, player_position.x, player_position.y); | ||||
|                 update_grid(gp, LIVING_PLAYER, player_position.x + dx, player_position.y + dy); | ||||
|                 break; | ||||
|             case MAZE_EXIT: | ||||
|                 update_grid(gp, EMPTY, player_position.x, player_position.y); | ||||
|                 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) { | ||||
|     switch (getch()) { | ||||
|         case KEY_UP: // fallthrough | ||||
|         case 'w': | ||||
|         case 'W': | ||||
|             maze_runner_move(gp, 0, -1); | ||||
|             break; | ||||
|         case KEY_DOWN: // fallthrough | ||||
|         case 's': | ||||
|         case 'S': | ||||
|             maze_runner_move(gp, 0, 1); | ||||
|             break; | ||||
|         case KEY_LEFT: // fallthrough | ||||
|         case 'a': | ||||
|         case 'A': | ||||
|             maze_runner_move(gp, -1, 0); | ||||
|             break; | ||||
|         case KEY_RIGHT: // fallthrough | ||||
|         case 'd': | ||||
|         case 'D': | ||||
|             maze_runner_move(gp, 1, 0); | ||||
|             break; | ||||
|         case 'p': | ||||
|         case KEY_BACKSPACE: | ||||
|         case KEY_ESCAPE: | ||||
|             grid_put_state(gp, STATE_QUIT); | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/games/maze-runner/maze_runner.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/games/maze-runner/maze_runner.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 6/10/2025 | ||||
|  * | ||||
|  * Naam: Jeroen Boxhoorn | ||||
|  * UvAnetID: 16333969 | ||||
|  * Studie: BSC Informatica | ||||
|  * | ||||
|  * 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(); | ||||
|  * | ||||
|  * How to play the game: | ||||
|  * - You are the '*' character. | ||||
|  * - Use either WSAD or the arrow keys to navigate through the maze. | ||||
|  * - The exit of the maze is marked with a '$'. | ||||
|  * - Walls are '#'. | ||||
|  * - Traps are 'X'. These kill you. | ||||
|  * | ||||
|  * You can quit the program at any time by pressing CTRL + C. | ||||
|  * | ||||
|  * Have fun playing! | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_MAZE_RUNNER_H | ||||
| #define MINIGAME_MENU_MAZE_RUNNER_H | ||||
|  | ||||
| /* | ||||
|  * 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(); | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to play a game of maze runner. | ||||
|  * | ||||
|  * Controls: | ||||
|  * use WSAD or arrow keys to move through the maze. | ||||
|  * Use BACKSPACE or ESCAPE to exit the game early. | ||||
|  */ | ||||
| void maze_runner(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_MAZE_RUNNER_H | ||||
							
								
								
									
										16
									
								
								src/games/minesweeper/minesweeper.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/games/minesweeper/minesweeper.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  */ | ||||
|  | ||||
| #include "minesweeper.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
|  | ||||
| #include "../../engine/grid_game_engine.h" | ||||
|  | ||||
| void minesweeper() { | ||||
|     clear(); | ||||
|     mvprintw(0,0, "Minesweeper has not yet been created"); | ||||
|     graceful_exit((coordinate){0, 3}); | ||||
|     refresh(); | ||||
| } | ||||
							
								
								
									
										30
									
								
								src/games/minesweeper/minesweeper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/games/minesweeper/minesweeper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * A game of minesweeper build on the grid game engine. | ||||
|  * | ||||
|  * 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 build on the grid game engine. | ||||
|  * | ||||
|  * 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. | ||||
|  * | ||||
|  * Instructions: | ||||
|  * use WSAD or arrow keys to select a grid square. | ||||
|  * use SPACEBAR to open the current square. | ||||
|  * use 'f' to mark/unmark a square. | ||||
|  * use BACKSPACE to exit the game early. | ||||
|  * | ||||
|  * marked squares can't be opened. | ||||
|  */ | ||||
| void minesweeper(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_MINESWEEPER_H | ||||
							
								
								
									
										187
									
								
								src/games/minigame-menu/minigame_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/games/minigame-menu/minigame_menu.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  */ | ||||
|  | ||||
| #include "minigame_menu.h" | ||||
|  | ||||
| #include <ncurses.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "../../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 5 | ||||
|  | ||||
| typedef enum { | ||||
|     GAME_MANUAL = 0, | ||||
|     GAME_MAZE_RUNNER = 1, | ||||
|     GAME_SNAKE = 2, | ||||
|     GAME_MINESWEEPER = 3, | ||||
|     GAME_QUIT = 4, | ||||
| } game; | ||||
|  | ||||
| static game SELECTED_GAME = GAME_MANUAL; | ||||
| static int OFFSET_Y = 5; | ||||
| static int OFFSET_X = 5; | ||||
|  | ||||
| /* | ||||
|  * Launch a game from the menu. | ||||
|  * | ||||
|  * Input: | ||||
|  * menu: A pointer to the menu grid. | ||||
|  * game: The game you want to launch. | ||||
|  */ | ||||
| static void launch_game(const game game) { | ||||
|     switch (game) { | ||||
|         case GAME_MANUAL: | ||||
|             manual((coordinate){0,0}); | ||||
|             break; | ||||
|         case GAME_MAZE_RUNNER: | ||||
|             maze_runner(); | ||||
|             break; | ||||
|         case GAME_SNAKE: | ||||
|             snake(); | ||||
|             break; | ||||
|         case GAME_MINESWEEPER: | ||||
|             minesweeper(); | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Highlight a valid menu option. | ||||
|  * | ||||
|  * 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 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 = grid_fetch_row(menu, SELECTED_GAME); | ||||
|             mvprintw(OFFSET_Y + (int)SELECTED_GAME, OFFSET_X,  "%s", row); | ||||
|             free(row); | ||||
|  | ||||
|             attroff(COLOR_PAIR(GREEN)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Show the menu screen. | ||||
|  * | ||||
|  * 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 grid *menu) { | ||||
|     erase(); | ||||
|     show_grid_on_offset(menu, OFFSET_X, OFFSET_Y); | ||||
|     menu_highlight(menu); | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Select the game on a location determined by a given offset. | ||||
|  * Negative values go up and positive values go down. | ||||
|  * Out of bounds selections loop around. | ||||
|  * | ||||
|  * Input: | ||||
|  * selected_game:  The currently selected game. | ||||
|  * offset:         The amount offset the current selection by. | ||||
|  * | ||||
|  * Side effect: | ||||
|  * The game on the location of the given offset will be selected. | ||||
|  */ | ||||
| static void menu_move(const int offset) { | ||||
|     SELECTED_GAME = modulo(SELECTED_GAME + offset, AMOUNT_OF_MENU_OPTIONS); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Navigate through the menu. | ||||
|  * | ||||
|  * Input: | ||||
|  * menu: A pointer to the menu grid. | ||||
|  * | ||||
|  * Output: A code that reflects the current menu state. | ||||
|  * 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(void) { | ||||
|     switch (getch()) { | ||||
|         case KEY_UP: | ||||
|         case 'w': | ||||
|         case 'W': | ||||
|             menu_move(-1); | ||||
|             break; | ||||
|         case KEY_DOWN: | ||||
|         case 's': | ||||
|         case 'S': | ||||
|             menu_move(1); | ||||
|             break; | ||||
|         case KEY_ENTER: | ||||
|         case '\n': | ||||
|         case 'f': | ||||
|         case 'F': | ||||
|         case ' ': | ||||
|             if (SELECTED_GAME == GAME_QUIT) { | ||||
|                 return 1; | ||||
|             } | ||||
|             launch_game(SELECTED_GAME); | ||||
|             break; | ||||
|         case KEY_BACKSPACE: | ||||
|         case KEY_ESCAPE: | ||||
|             return 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Create the menu grid. | ||||
|  * | ||||
|  * Output: | ||||
|  * A pointer to the menu grid. | ||||
|  */ | ||||
| static grid *initialize_menu(void) { | ||||
|     char menu[] = "How to play\n" | ||||
|                   "Maze Runner\n" | ||||
|                   "   Snake   \n" | ||||
|                   "Minesweeper\n" | ||||
|                   "   Leave   \n"; | ||||
|     grid *rp = grid_create_from_string(menu); | ||||
|     return rp; | ||||
| } | ||||
|  | ||||
| void minigame_menu(void) { | ||||
|     grid *menu = initialize_menu(); | ||||
|     while (true) { | ||||
|         if (navigate_menu() == 1) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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++) { | ||||
|             if (grid_fetch(GRID, x, y) == CELL_EMPTY) { | ||||
|                 const coordinate new_spot = {x, y}; | ||||
|                 empty_spots[available_spots] = new_spot; | ||||
|                 available_spots++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const coordinate food_location = empty_spots[modulo(rand(), available_spots)]; | ||||
|  | ||||
|     grid_put(GRID, food_location.x, food_location.y, CELL_FOOD); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(CURRENT_DIRECTION)); | ||||
|     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.x, new_location.y)); | ||||
|  | ||||
|     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.x, SNAKE_TAIL.y))) { | ||||
|             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, SNAKE_TAIL.x, SNAKE_TAIL.y, CELL_EMPTY); | ||||
|         SNAKE_TAIL = new_tail; | ||||
|     } | ||||
|  | ||||
|     // New head placed after tail moves. It can occupy the empty space created by the tail moving. | ||||
|     grid_put(GRID, new_location.x, new_location.y, get_body_part(CURRENT_DIRECTION)); | ||||
|     SNAKE_HEAD = new_location; | ||||
|     PREVIOUS_DIRECTION = CURRENT_DIRECTION; | ||||
|  | ||||
|     if (action == SNAKE_EAT) { | ||||
|         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, OFFSET_X, OFFSET_Y); | ||||
|         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, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir)); | ||||
|     CURRENT_DIRECTION = dir; | ||||
|     show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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, OFFSET_X, OFFSET_Y); | ||||
|  | ||||
|     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(); | ||||
| } | ||||
							
								
								
									
										26
									
								
								src/games/snake/snake.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/games/snake/snake.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| /* | ||||
|  * Created by snapshot112 on 15/10/2025 | ||||
|  * | ||||
|  * A game of maze runner build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  */ | ||||
|  | ||||
| #ifndef MINIGAME_MENU_SNAKE_H | ||||
| #define MINIGAME_MENU_SNAKE_H | ||||
|  | ||||
| /* | ||||
|  * A game of snake build on the grid game engine. | ||||
|  * | ||||
|  * Please make sure to include and initialize the game engine before calling snake(); | ||||
|  * | ||||
|  * Side Effects: | ||||
|  * Clears the console and uses it to play a game of snake. | ||||
|  * | ||||
|  * Controls: | ||||
|  * Use WSAD or arrow keys to redirect the snake. | ||||
|  * Use BACKSPACE or ESCAPE to exit the game early. | ||||
|  */ | ||||
| void snake(void); | ||||
|  | ||||
| #endif //MINIGAME_MENU_SNAKE_H | ||||
							
								
								
									
										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