diff --git a/deel2.tar.gz b/deel2.tar.gz new file mode 100644 index 0000000..67bd8b7 Binary files /dev/null and b/deel2.tar.gz differ diff --git a/grid_game_engine.c b/grid_game_engine.c index 8cd7267..5a63a64 100644 --- a/grid_game_engine.c +++ b/grid_game_engine.c @@ -90,7 +90,7 @@ static void display_hackerman(void) { } void graceful_exit(void) { - mvprintw(5, 5, "Press 'q' to exit."); + mvprintw(25, 5, "Press 'q' to exit."); while (1) { switch (getch()) { case 'q': diff --git a/grid_game_engine.h b/grid_game_engine.h index 030a241..25fe2b9 100644 --- a/grid_game_engine.h +++ b/grid_game_engine.h @@ -11,6 +11,11 @@ #define MINIGAME_MENU_GRID_GAME_ENGINE_H #include "rooster.h" +typedef struct { + int x; + int y; +} coordinate; + typedef enum { BLACK = 1, WHITE = 2, diff --git a/maze_runner.c b/maze_runner.c index 6727a87..201d499 100644 --- a/maze_runner.c +++ b/maze_runner.c @@ -23,7 +23,7 @@ static rooster *get_maze(void) { perror("loading maze"); exit(EXIT_FAILURE); } - rooster *rp = rooster_lees(fh); + rooster *rp = grid_from_file(fh); fclose(fh); // 3. Bepaal of het lezen van het rooster is gelukt. @@ -83,33 +83,30 @@ static void maze_runner_beweeg(rooster *rp, int dx, int dy) { * 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; - } + 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; } } diff --git a/minigame_menu.c b/minigame_menu.c index aaa1489..c886127 100644 --- a/minigame_menu.c +++ b/minigame_menu.c @@ -155,7 +155,7 @@ static rooster *initialize_menu(void) { " Snake \n" "Minesweeper\n" " Leave \n"; - rooster *rp = rooster_maak(menu); + rooster *rp = grid_from_string(menu); return rp; } diff --git a/rooster.c b/rooster.c index 609d07e..aeec8ae 100644 --- a/rooster.c +++ b/rooster.c @@ -1,5 +1,6 @@ #include "rooster.h" +#include #include #include #include @@ -14,11 +15,12 @@ typedef struct rooster_data { toestand state; } rooster; -rooster *rooster_maak(const char* input) { +rooster *grid_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; } @@ -30,6 +32,7 @@ rooster *rooster_maak(const char* input) { 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; } } @@ -92,7 +95,7 @@ static int get_grid_sizes(FILE *fh, rooster *rost) { return 1; } -rooster *rooster_lees(FILE *fh) { +rooster *grid_from_file(FILE *fh) { if (fh == NULL) { return NULL; } diff --git a/rooster.h b/rooster.h index 9ff94ea..565c641 100644 --- a/rooster.h +++ b/rooster.h @@ -39,7 +39,7 @@ typedef enum { NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen achter.) */ -rooster *rooster_lees(FILE *fh); +rooster *grid_from_file(FILE *fh); /* * Maak een rooster op basis van een gegeven string. @@ -54,7 +54,7 @@ rooster *rooster_lees(FILE *fh); * NULL teruggegeven. (In dat geval blijft geen gereserveerd geheugen * achter.) */ -rooster *rooster_maak(const char* input); +rooster *grid_from_string(const char* input); /* * Haal een rij uit het rooster op. diff --git a/snake.c b/snake.c index 93ad071..02f1bc8 100644 --- a/snake.c +++ b/snake.c @@ -6,9 +6,65 @@ #include #include +#include +#include #include "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; + +static direction PREVIOUS_DIRECTION = DIRECTION_DOWN; +static direction CURRENT_DIRECTION = DIRECTION_DOWN; +static coordinate SNAKE_HEAD; +static coordinate SNAKE_TAIL; +static coordinate MESSAGE_LOCATION = {0,0}; +static int MAP_HEIGHT = 20; +static int MAP_WIDTH = 20; + +/* + * Create a snake body part. + * + * Input: + * dir: the direction the body part should point to. + * + * Output: + * a character representing that bodypart. + */ +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. * @@ -19,53 +75,250 @@ * Returns: * A pointer to the grid. */ -static rooster *initialize_snake(void) { - int width = 10; - int height = 10; - const int grid_size = (width + 1) * height + 1; +static rooster *create_grid(void) { + const int grid_size = (MAP_WIDTH + 1) * MAP_HEIGHT + 1; char *map = malloc(grid_size * sizeof(char)); if (map == NULL) { return NULL; } - for (int i = 0; i < (width + 1) * height; i++) { - int top_line = i < width; - int bottom_line = i > grid_size - (width + 2); + for (int i = 1; i <= (MAP_WIDTH + 1) * MAP_HEIGHT; i++) { + // Also subtract the null terminator + int bottom_line = i > grid_size - (MAP_WIDTH + 2); + int top_line = i < MAP_WIDTH + 1; - int line_position = modulo(i, width + 1); + int line_position = modulo(i, MAP_WIDTH + 1); int line_start = line_position == 1; - int line_end = line_position == width; + int line_end = line_position == MAP_WIDTH; int newline = line_position == 0; if (newline) { - map[i] = '\n'; + map[i - 1] = '\n'; } else if (top_line || bottom_line || line_start || line_end) { - map[i] = '#'; - } - else { - map[i] = ' '; + map[i - 1] = CELL_WALL; + } else { + map[i - 1] = CELL_EMPTY; } } map[grid_size - 1] = '\0'; - printf("%s", map); - - rooster *grid = rooster_maak(map); + rooster *grid = grid_from_string(map); free(map); + rooster_plaats(grid, rooster_breedte(grid) / 2, rooster_hoogte(grid) / 2, get_body_part(CURRENT_DIRECTION)); + return grid; +} + +/* + * + */ +static void generate_food(rooster *gp) { + coordinate empty_spots[MAP_HEIGHT * MAP_WIDTH]; + + // Usable as index when initialized like this. + int available_spots = 0; + + for (int x = 0; x < MAP_WIDTH; x++) { + for (int y = 0; y < MAP_HEIGHT; y++) { + if (rooster_kijk(gp, x, y) == CELL_EMPTY) { + coordinate new_spot = {x, y}; + empty_spots[available_spots] = new_spot; + available_spots++; + } + } + } + + // Available spots will now be a counter. + + const coordinate food_location = empty_spots[modulo(rand(), available_spots)]; + + rooster_plaats(gp, 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. + */ +static rooster *initialize(void) { + srand(time(NULL)); + + rooster *grid = create_grid(); + + if (grid == NULL) { + return NULL; + } + + // Set snake head and snake tail. + rooster_zoek(grid, get_body_part(CURRENT_DIRECTION), &SNAKE_HEAD.x, &SNAKE_HEAD.y); + SNAKE_TAIL.x = SNAKE_HEAD.x; + SNAKE_TAIL.y = SNAKE_HEAD.y; + + generate_food(grid); + + if (SNAKE_HEAD.x == -1) { + return NULL; + } + + MESSAGE_LOCATION.y = rooster_hoogte(grid) + OFFSET_Y + 5; + MESSAGE_LOCATION.x = OFFSET_X - 5 >= 0 ? OFFSET_X - 5 : 0; + return grid; } -void snake(void) { - // erase(); - rooster* const gp = initialize_snake(); - if (gp == NULL) { - printf("Help, initializing the grid failed\n"); +/* + * Checks what happens when the snake moves over a given char. + * + * Input: + * c: The char to check. + * + * Returns: + * The snake will move forward: 0 + * The snake will eat an apple: 1 + * The snake will die: 2 + */ +static snake_action collision_check(char c) { + if (c == CELL_EMPTY) { + return SNAKE_MOVE; } - // show_grid(gp); - // graceful_exit(); - // refresh(); + + if (c == CELL_FOOD) { + return SNAKE_EAT; + } + + return SNAKE_DIE; +} + +static void update_snake(rooster *gp, coordinate new_head, snake_action action) { + if (action == SNAKE_DIE) { + rooster_zet_toestand(gp, STATE_VERLOREN); + return; + } + + if (action == SNAKE_MOVE) { + coordinate new_tail = SNAKE_TAIL; + + switch (get_body_part_direction(rooster_kijk(gp, 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; + } + + rooster_plaats(gp, SNAKE_TAIL.x, SNAKE_TAIL.y, CELL_EMPTY); + SNAKE_TAIL = new_tail; + } + + // New head placed after tail moves. It can occupy the empty space created by the tail moving. + rooster_plaats(gp, new_head.x, new_head.y, get_body_part(CURRENT_DIRECTION)); + SNAKE_HEAD = new_head; + PREVIOUS_DIRECTION = CURRENT_DIRECTION; + + if (action == SNAKE_EAT) { + generate_food(gp); + } +} + +static void snake_move(rooster *gp) { + coordinate new_head = SNAKE_HEAD; + switch (CURRENT_DIRECTION) { + case DIRECTION_UP: + new_head.y--; + break; + case DIRECTION_RIGHT: + new_head.x++; + break; + case DIRECTION_DOWN: + new_head.y++; + break; + case DIRECTION_LEFT: + new_head.x--; + break; + } + + snake_action action = collision_check(rooster_kijk(gp, new_head.x, new_head.y)); + + update_snake(gp, new_head, action); + show_grid_on_offset(gp, OFFSET_X, OFFSET_Y); +} + +static void turn_snake(rooster *gp, direction dir) { + if ((direction)modulo(dir + 2, DIRECTION_COUNT) == PREVIOUS_DIRECTION) { + return; + } + rooster_plaats(gp, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir)); + CURRENT_DIRECTION = dir; + show_grid_on_offset(gp, OFFSET_X, OFFSET_Y); +} + +static void play_snake(rooster *gp) { + timeout(500); + + switch (getch()) { + case KEY_UP: // fallthrough + case 'w': + case 'W': + turn_snake(gp, DIRECTION_UP); + break; + case KEY_DOWN: // fallthrough + case 's': + case 'S': + turn_snake(gp, DIRECTION_DOWN); + break; + case KEY_LEFT: // fallthrough + case 'a': + case 'A': + turn_snake(gp, DIRECTION_LEFT); + break; + case KEY_RIGHT: // fallthrough + case 'd': + case 'D': + turn_snake(gp, DIRECTION_RIGHT); + break; + case KEY_BACKSPACE: + rooster_zet_toestand(gp, STATE_QUIT); + break; + case ERR: + snake_move(gp); + } +} + +void snake(void) { + rooster* const gp = initialize(); + if (gp == NULL) { + return; + } + erase(); + + //todo: ?? Win condition? + + show_grid_on_offset(gp, OFFSET_X, OFFSET_Y); + + mvprintw(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, "Press SPACEBAR to start playing."); + while (getch() != ' ') {} + + rooster_zet_toestand(gp, STATE_AAN_HET_SPELEN); + while (rooster_vraag_toestand(gp) == STATE_AAN_HET_SPELEN) { + play_snake(gp); + } + + // game_exit_screen(gp); + + rooster_klaar(gp); + graceful_exit(); } diff --git a/spel b/spel index 6cc0ab9..0c0f4fe 100755 Binary files a/spel and b/spel differ diff --git a/spel.c b/spel.c index 26ee84f..32031a0 100644 --- a/spel.c +++ b/spel.c @@ -13,7 +13,6 @@ #include "grid_game_engine.h" #include "minigame_menu.h" -#include "snake.h" int main(void) { init_engine();