diff --git a/deel2.tar.gz b/deel2.tar.gz new file mode 100644 index 0000000..5d29674 Binary files /dev/null and b/deel2.tar.gz differ diff --git a/rooster.c b/rooster.c index 0b25756..b312092 100644 --- a/rooster.c +++ b/rooster.c @@ -5,6 +5,8 @@ #include +// TODO: End the eternal NULL checks. + /* * The rooster type this program is build around. */ @@ -16,7 +18,40 @@ typedef struct rooster_data { } rooster; -rooster *rooster_maak(char* input); +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) { + perror("rooster_maak"); + exit(1); + } + + 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) { + perror("rooster_maak"); + exit(1); + }; + } + + const int grid_size = (width + 1) * height + 1; + + rooster *rp = malloc(sizeof(rooster)); + 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 @@ -71,7 +106,7 @@ rooster *rooster_lees(FILE *fh) { NULL, 0, 0, - BEGIN + STATE_BEGIN }; // Sets the width and height of the rooster. @@ -129,23 +164,26 @@ toestand rooster_vraag_toestand(const rooster *rp) { if (rp != NULL) { return rp->state; } - return VERLOREN; + return STATE_VERLOREN; } void rooster_zet_toestand(rooster *rp, toestand t) { if (rp != NULL) { switch (t) { - case BEGIN: - rp->state = BEGIN; + case STATE_BEGIN: + rp->state = STATE_BEGIN; break; - case AAN_HET_SPELEN: - rp->state = AAN_HET_SPELEN; + case STATE_AAN_HET_SPELEN: + rp->state = STATE_AAN_HET_SPELEN; break; - case GEWONNEN: - rp->state = GEWONNEN; + case STATE_GEWONNEN: + rp->state = STATE_GEWONNEN; break; - case VERLOREN: - rp->state = VERLOREN; + case STATE_VERLOREN: + rp->state = STATE_VERLOREN; + break; + case STATE_QUIT: + rp->state = STATE_QUIT; break; } } diff --git a/rooster.h b/rooster.h index 7933fd1..229dc87 100644 --- a/rooster.h +++ b/rooster.h @@ -18,10 +18,11 @@ struct rooster_data; typedef struct rooster_data rooster; typedef enum { - BEGIN, - AAN_HET_SPELEN, - GEWONNEN, - VERLOREN + 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. @@ -53,7 +54,7 @@ rooster *rooster_lees(FILE *fh); * NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen * achter.) */ -rooster *rooster_maak(char* input); +rooster *rooster_maak(const char* input); /* * Haal een rij uit het rooster op. diff --git a/spel b/spel index b1fb5bc..ce67212 100755 Binary files a/spel and b/spel differ diff --git a/spel.c b/spel.c index 3b58c1f..c8116ff 100644 --- a/spel.c +++ b/spel.c @@ -21,15 +21,24 @@ */ #include +#include #include #include #include #include #include +#include #include "rooster.h" +typedef enum { + GAME_QUIT, + GAME_MAZE_RUNNER, + GAME_SNAKE, + GAME_MINESWEEPER +} game; + typedef struct { char name[100]; rooster *game_map; @@ -66,6 +75,115 @@ void update_grid(rooster *rp, char c, int x, int y) { } } +void game_error(void) { + endwin(); + exit(EXIT_FAILURE); +} + +void quit_game(rooster *menu) { + if (menu != NULL) { + rooster_klaar(menu); + } + endwin(); + exit(EXIT_SUCCESS); +} + +/* + * Toont het victory screen. + * + * Side Effect: + * De victory message wordt op een schone console geprint. + */ +void display_victory(void) { + clear(); + mvprintw(2,5, "YOU WON!!!!!"); + refresh(); +} + +/* + * Toont het loss screen. + * + * Side Effect: + * De loss message wordt op een schone console geprint. + */ +void display_loss(void) { + clear(); + mvprintw(2,5, "RIP, YOU DIED..."); + refresh(); +} + +/* + * Toont het quit screen. + * + * Side Effect: + * De quit message wordt op een schone console geprint. + */ +void display_quit(void) { + clear(); + mvprintw(2,5, "You quit the game"); + refresh(); +} + +/* + * Toont het hackerman screen. + * + * Side Effect: + * De hackerman message wordt op een schone console geprint. + */ +void display_hackerman(void) { + clear(); + mvprintw(2,5, "The hacker man strikes again..."); + refresh(); +} + +/* + * Bepaalt afhankelijk van de eindtoestand van het rooster + * welk afsluitscherm er getoond moet worden en toont dan dat rooster. + * + * Input: Het rooster om de toestand uit af te lezen. + * + * Side Effects: + * Het end-of-game scherm wordt op een blanke console geprint. + */ +void game_exit_screen(rooster *rp) { + toestand current_state = rooster_vraag_toestand(rp); + switch (current_state) { + case STATE_GEWONNEN: + display_victory(); + return; + case STATE_VERLOREN: + display_loss(); + return; + case GAME_QUIT: + display_quit(); + return; + } + display_hackerman(); +} + +/* + * Waits for the user to press an exit key 'q' before continuing. + */ +void graceful_exit(void) { + mvprintw(5, 0, "Press 'q' to exit the game."); + while (1) { + switch (getch()) { + case 'q': + return; + } + } +} + +void speel_snake(void) { + mvprintw(0,0, "Snake has not yet been created"); + graceful_exit(); +} + +void speel_minesweeper() { + mvprintw(0,0, "Minesweeper has not yet been created"); + graceful_exit(); +} + /* Voert de benodigde veranderingen in het rooster door als de speler in een * bepaalde richting probeert te bewegen. * Input: @@ -77,7 +195,7 @@ void update_grid(rooster *rp, char c, int x, int y) { * Side effect: het rooster wordt aangepast op basis van de handeling van * de speler. */ -void beweeg(rooster *rp, int dx, int dy) { +void maze_runner_beweeg(rooster *rp, int dx, int dy) { int playerx; int playery; rooster_zoek(rp, '*', &playerx, &playery); @@ -94,7 +212,7 @@ void beweeg(rooster *rp, int dx, int dy) { break; case 'X': update_grid(rp, ' ', playerx, playery); - rooster_zet_toestand(rp, VERLOREN); + rooster_zet_toestand(rp, STATE_VERLOREN); break; case ' ': update_grid(rp, ' ', playerx, playery); @@ -102,7 +220,7 @@ void beweeg(rooster *rp, int dx, int dy) { break; case '$': update_grid(rp, ' ', playerx, playery); - rooster_zet_toestand(rp, GEWONNEN); + rooster_zet_toestand(rp, STATE_GEWONNEN); break; } refresh(); @@ -117,133 +235,44 @@ void beweeg(rooster *rp, int dx, int dy) { * * Side Effects: * Met WSAD en arrow keys kun je door het doolhof heen bewegen. + * */ -void run_maze(rooster *rp) { - while (rooster_vraag_toestand(rp) == AAN_HET_SPELEN) +void maze_runner(rooster *rp) { + while (rooster_vraag_toestand(rp) == STATE_AAN_HET_SPELEN) { switch (getch()) { case KEY_UP: // fallthrough case 'w': - beweeg(rp, 0, -1); + maze_runner_beweeg(rp, 0, -1); break; case KEY_DOWN: // fallthrough case 's': - beweeg(rp, 0, 1); + maze_runner_beweeg(rp, 0, 1); break; case KEY_LEFT: // fallthrough case 'a': - beweeg(rp, -1, 0); + maze_runner_beweeg(rp, -1, 0); break; case KEY_RIGHT: // fallthrough case 'd': - beweeg(rp, 1, 0); + maze_runner_beweeg(rp, 1, 0); break; - case KEY_EXIT: - rooster_zet_toestand(rp, VERLOREN); + case KEY_BACKSPACE: + rooster_zet_toestand(rp, STATE_QUIT); break; } } } -/* - * Toont het maze victory screen. - * - * Side Effect: - * De victory message wordt op een schone console geprint. - */ -void display_maze_victory() { - clear(); - mvprintw(2,5, "YOU WON!!!!!"); - refresh(); -} +rooster *choose_maze(void) { + // TODO: echt opties aanbieden in plaats van hardcoded 1 maze. + // Alternatief is om random een maze te genereren. dit is miss beter. -/* - * Toont het maze loss screen. - * - * Side Effect: - * De loss message wordt op een schone console geprint. - */ -void display_maze_loss() { - clear(); - mvprintw(2,5, "RIP, YOU DIED..."); - refresh(); -} - -/* - * Toont het maze broken screen. - * - * Side Effect: - * De hackerman message wordt op een schone console geprint. - */ -void display_hackerman() { - clear(); - mvprintw(2,5, "The hacker man strikes again..."); - refresh(); -} - -/* - * Bepaalt afhankelijk van de eindtoestand van het rooster - * welk afsluitscherm er getoond moet worden en toont dan dat rooster. - * - * Input: Het rooster om de toestand uit af te lezen. - * - * Side Effects: - * Het end-of-game scherm wordt op een blanke console geprint. - */ -void maze_exit_screen(rooster *rp) { - toestand current_state = rooster_vraag_toestand(rp); - switch (current_state) { - case GEWONNEN: - display_maze_victory(); - return; - case VERLOREN: - display_maze_loss(); - return; - } - display_hackerman(); -} - -/* - * Waits for the user to press an exit key 'q' before continuing. - */ -void graceful_exit() { - mvprintw(5, 0, "Press 'q' to exit the game."); - while (1) { - switch (getch()) { - case 'q': - return; - } - } -} - -void toon_menu(rooster *rp) { - toon_rooster(rp); - rooster_zet_toestand(rp, AAN_HET_SPELEN); - run_maze(rp); - maze_exit_screen(rp); - graceful_exit(); -} - -/* - * Speelt het spel met een gegeven rooster tot de toestand niet langer - * AAN_HET_SPELEN is. - */ -void speel(rooster *rp) { - toon_menu(rp); -} - -int main(int argc, char *argv[]) { - // 1. Controleer dat er een doolhofbestand is opgegeven op de command line. - if (argc != 2) { - fprintf(stderr, "gebruik: ./spel assets/voorbeeld_doolhof.txt\n"); - return 1; - } - - // 2. Open het doolhofbestand en lees het rooster. - FILE *fh = fopen(argv[1], "r"); + // 2. Open het doolhof bestand en lees het rooster. + FILE *fh = fopen("assets/voorbeeld_doolhof.txt", "r"); if (fh == NULL) { - perror("main"); - return 1; + perror("loading maze"); + game_error(); } rooster *rp = rooster_lees(fh); fclose(fh); @@ -251,23 +280,147 @@ int main(int argc, char *argv[]) { // 3. Bepaal of het lezen van het rooster is gelukt. if (rp == NULL) { fprintf(stderr, "Kan rooster niet maken.\n"); - return 1; + game_error(); } + return rp; +} + +/* + * Speelt het spel met een gegeven rooster tot de toestand niet langer + * AAN_HET_SPELEN is. + */ +void speel_maze(void) { + // Voorbereiding. + rooster *rp = choose_maze(); + toon_rooster(rp); + rooster_zet_toestand(rp, STATE_AAN_HET_SPELEN); + + // Game zelf. + maze_runner(rp); + + // Exit game. + game_exit_screen(rp); + rooster_klaar(rp); + graceful_exit(); +} + +void launch_game(rooster *menu, const game game) { + switch (game) { + case GAME_MAZE_RUNNER: + speel_maze(); + return; + case GAME_SNAKE: + speel_snake(); + return; + case GAME_MINESWEEPER: + speel_minesweeper(); + return; + case GAME_QUIT: + quit_game(menu); + } +} + +void menu_highlight(rooster *rp, const game target, const int offset_x, const int offset_y) { + switch (target) { + case GAME_MAZE_RUNNER: // Fallthrough + case GAME_SNAKE: // Fallthrough + case GAME_MINESWEEPER: // Fallthrough + case GAME_QUIT: // Fallthrough + // TODO: Properly highlight this shit. + mvprintw(offset_y + (int)target, offset_x, rooster_vraag_rij(rp, target)); + } +} + +game menu_try_move(const game selected_game, const int offset) { + switch (selected_game + offset) { + case GAME_MAZE_RUNNER: + return GAME_MAZE_RUNNER; + case GAME_SNAKE: + return GAME_SNAKE; + case GAME_MINESWEEPER: + return GAME_MINESWEEPER; + case GAME_QUIT: + return GAME_QUIT; + } + return selected_game; +} + +void navigate_menu(rooster *menu, const game default_game, const int offset_x, const int offset_y) { + game selected_game = default_game; + while (true) { + switch (getch()) { + case KEY_UP: // fallthrough + case 'w': + selected_game = menu_try_move(selected_game, -1); + menu_highlight(menu, selected_game, offset_x, offset_y); + break; + case KEY_DOWN: // fallthrough + case 's': + selected_game = menu_try_move(selected_game, 1); + menu_highlight(menu, selected_game, offset_x, offset_y); + break; + case KEY_ENTER: + // select current game somehow + launch_game(menu, GAME_QUIT); + menu_highlight(menu, selected_game, offset_x, offset_y); + break; + case KEY_BACKSPACE: + launch_game(menu, GAME_QUIT); + break; + } + } +} + +rooster *laad_menu(void) { + char menu[] = "Maze Runner\n" + " Snake \n" + "Minesweeper\n" + " Leave \n"; + rooster *rp = rooster_maak(menu); + return rp; +} + +void toon_menu(rooster *menu, const game default_game, const int offset_x, const int offset_y) { + toon_rooster_op_locatie(menu, offset_x, offset_y); + menu_highlight(menu, default_game, offset_x, offset_y); + refresh(); +} + +void menu(void) { + rooster *menu = laad_menu(); + const game default_game = GAME_MAZE_RUNNER; + + while (true) { + clear(); + const int offset_x = 5; + const int offset_y = 5; + toon_menu(menu, default_game, offset_x, offset_y); + navigate_menu(menu, default_game, offset_x, offset_y); + } +} + +void startup_sequence(void) { + // TODO: Nice entry screen +} + +int main(void) { // 4. Initialiseer ncurses setlocale(LC_ALL, ""); initscr(); cbreak(); // zodat je kunt onderbreken met Ctrl+C keypad(stdscr, TRUE); // luister ook naar extra toetsen zoals pijltjes noecho(); // druk niet de letters af die je intypt - curs_set(0); // hides the cursor + curs_set(0); // hides the cursor// Don't mask any mouse events + + // 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 // 5. Speel het spel. - speel(rp); + startup_sequence(); + menu(); // 6. Sluit af. - rooster_klaar(rp); - endwin(); - return 0; + quit_game(NULL); }