diff --git a/deel2.tar.gz b/deel2.tar.gz deleted file mode 100644 index 67bd8b7..0000000 Binary files a/deel2.tar.gz and /dev/null differ diff --git a/grid_game_engine.c b/grid_game_engine.c index 5a63a64..b807a66 100644 --- a/grid_game_engine.c +++ b/grid_game_engine.c @@ -12,6 +12,10 @@ #include "rooster.h" +int same_coordinate(coordinate a, 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) { diff --git a/grid_game_engine.h b/grid_game_engine.h index 25fe2b9..2f07c96 100644 --- a/grid_game_engine.h +++ b/grid_game_engine.h @@ -16,6 +16,7 @@ typedef struct { int y; } coordinate; +// Start at 1 since the color 0 is reserved for no_color. typedef enum { BLACK = 1, WHITE = 2, @@ -32,6 +33,19 @@ typedef struct { rooster *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. diff --git a/maze_runner.c b/maze_runner.c index 201d499..6c12962 100644 --- a/maze_runner.c +++ b/maze_runner.c @@ -21,7 +21,7 @@ static rooster *get_maze(void) { FILE *fh = fopen("assets/maze.txt", "r"); if (fh == NULL) { perror("loading maze"); - exit(EXIT_FAILURE); + return NULL; } rooster *rp = grid_from_file(fh); fclose(fh); @@ -29,7 +29,7 @@ static rooster *get_maze(void) { // 3. Bepaal of het lezen van het rooster is gelukt. if (rp == NULL) { fprintf(stderr, "Kan rooster niet maken.\n"); - exit(EXIT_FAILURE); + return NULL; } return rp; @@ -53,7 +53,8 @@ static void maze_runner_beweeg(rooster *rp, int dx, int dy) { if (playerx == -1 || playery == -1) { printf("Player not found!"); - exit(1); + rooster_zet_toestand(rp, STATE_BEGIN); + return; } if (rooster_bevat(rp, playerx + dx, playery + dy) == 1) { @@ -113,11 +114,16 @@ static void speel_maze(rooster *rp) { void maze_runner(void) { // Voorbereiding. rooster *rp = get_maze(); + if (rp == NULL) { + return; + } show_grid(rp); rooster_zet_toestand(rp, STATE_AAN_HET_SPELEN); // Game zelf. - speel_maze(rp); + while (rooster_vraag_toestand(rp) == STATE_AAN_HET_SPELEN) { + speel_maze(rp); + } // Exit game. game_exit_screen(rp); diff --git a/minigame_menu.c b/minigame_menu.c index c886127..405f8f8 100644 --- a/minigame_menu.c +++ b/minigame_menu.c @@ -26,7 +26,7 @@ static int OFFSET_X = 5; * menu: A pointer to the menu grid. * game: The game you want to launch. */ -static void launch_game(rooster *menu, const game game) { +static void launch_game(const game game) { switch (game) { case GAME_MAZE_RUNNER: maze_runner(); @@ -131,12 +131,13 @@ static int navigate_menu(rooster *menu) { menu_move(1); break; case KEY_ENTER: + case '\n': case 'f': case 'F': if (SELECTED_GAME == GAME_QUIT) { return 1; } - launch_game(menu, SELECTED_GAME); + launch_game(SELECTED_GAME); break; case KEY_BACKSPACE: return 1; diff --git a/snake.c b/snake.c index 02f1bc8..017ac3f 100644 --- a/snake.c +++ b/snake.c @@ -6,8 +6,9 @@ #include #include -#include #include +#include +#include #include "grid_game_engine.h" @@ -38,6 +39,8 @@ static coordinate SNAKE_TAIL; static coordinate MESSAGE_LOCATION = {0,0}; static int MAP_HEIGHT = 20; static int MAP_WIDTH = 20; +static rooster *GRID; +static pthread_mutex_t MUTEX; /* * Create a snake body part. @@ -75,11 +78,11 @@ static direction get_body_part_direction(const char body_part) { * Returns: * A pointer to the grid. */ -static rooster *create_grid(void) { +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 NULL; + return; } for (int i = 1; i <= (MAP_WIDTH + 1) * MAP_HEIGHT; i++) { @@ -105,17 +108,16 @@ static rooster *create_grid(void) { map[grid_size - 1] = '\0'; - rooster *grid = grid_from_string(map); + 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; + rooster_plaats(GRID, rooster_breedte(GRID) / 2, rooster_hoogte(GRID) / 2, get_body_part(CURRENT_DIRECTION)); } /* * */ -static void generate_food(rooster *gp) { +static void generate_food() { coordinate empty_spots[MAP_HEIGHT * MAP_WIDTH]; // Usable as index when initialized like this. @@ -123,7 +125,7 @@ static void generate_food(rooster *gp) { for (int x = 0; x < MAP_WIDTH; x++) { for (int y = 0; y < MAP_HEIGHT; y++) { - if (rooster_kijk(gp, x, y) == CELL_EMPTY) { + if (rooster_kijk(GRID, x, y) == CELL_EMPTY) { coordinate new_spot = {x, y}; empty_spots[available_spots] = new_spot; available_spots++; @@ -135,7 +137,7 @@ static void generate_food(rooster *gp) { const coordinate food_location = empty_spots[modulo(rand(), available_spots)]; - rooster_plaats(gp, food_location.x, food_location.y, CELL_FOOD); + rooster_plaats(GRID, food_location.x, food_location.y, CELL_FOOD); } /* @@ -147,30 +149,33 @@ static void generate_food(rooster *gp) { * Side Effects: * Seed random with the current time. */ -static rooster *initialize(void) { +static void initialize(void) { srand(time(NULL)); - rooster *grid = create_grid(); + create_grid(); - if (grid == NULL) { - return NULL; + if (GRID == NULL) { + return; } + CURRENT_DIRECTION = DIRECTION_DOWN; + PREVIOUS_DIRECTION = DIRECTION_DOWN; + // Set snake head and snake tail. - rooster_zoek(grid, get_body_part(CURRENT_DIRECTION), &SNAKE_HEAD.x, &SNAKE_HEAD.y); + 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); + generate_food(); if (SNAKE_HEAD.x == -1) { - return NULL; + free(GRID); + GRID = NULL; + return; } - MESSAGE_LOCATION.y = rooster_hoogte(grid) + OFFSET_Y + 5; + MESSAGE_LOCATION.y = rooster_hoogte(GRID) + OFFSET_Y + 5; MESSAGE_LOCATION.x = OFFSET_X - 5 >= 0 ? OFFSET_X - 5 : 0; - - return grid; } /* @@ -196,16 +201,16 @@ static snake_action collision_check(char c) { return SNAKE_DIE; } -static void update_snake(rooster *gp, coordinate new_head, snake_action action) { +static void update_snake(coordinate new_head, snake_action action) { if (action == SNAKE_DIE) { - rooster_zet_toestand(gp, STATE_VERLOREN); + rooster_zet_toestand(GRID, 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))) { + switch (get_body_part_direction(rooster_kijk(GRID, SNAKE_TAIL.x, SNAKE_TAIL.y))) { case DIRECTION_UP: new_tail.y--; break; @@ -220,105 +225,128 @@ static void update_snake(rooster *gp, coordinate new_head, snake_action action) break; } - rooster_plaats(gp, SNAKE_TAIL.x, SNAKE_TAIL.y, CELL_EMPTY); + rooster_plaats(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. - rooster_plaats(gp, new_head.x, new_head.y, get_body_part(CURRENT_DIRECTION)); + rooster_plaats(GRID, 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); + generate_food(); } } -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; +static void *snake_move(void *arg) { + while (rooster_vraag_toestand(GRID) == STATE_AAN_HET_SPELEN) { + usleep(250000); + pthread_mutex_lock(&MUTEX); + 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(GRID, new_head.x, new_head.y)); + + update_snake(new_head, action); + show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y); + pthread_mutex_unlock(&MUTEX); } - - 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); + return NULL; } -static void turn_snake(rooster *gp, direction dir) { - if ((direction)modulo(dir + 2, DIRECTION_COUNT) == PREVIOUS_DIRECTION) { +static void turn_snake(direction dir) { + if ((direction)modulo(dir + 2, DIRECTION_COUNT) == PREVIOUS_DIRECTION + && !same_coordinate(SNAKE_HEAD, SNAKE_TAIL)) { return; } - rooster_plaats(gp, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir)); + rooster_plaats(GRID, SNAKE_HEAD.x, SNAKE_HEAD.y, get_body_part(dir)); CURRENT_DIRECTION = dir; - show_grid_on_offset(gp, OFFSET_X, OFFSET_Y); + show_grid_on_offset(GRID, 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); +static void *play_snake(void *arg) { + while (rooster_vraag_toestand(GRID) == STATE_AAN_HET_SPELEN) { + timeout(1); + int c = getch(); + pthread_mutex_lock(&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: + rooster_zet_toestand(GRID, STATE_QUIT); + break; + } + pthread_mutex_unlock(&MUTEX); } + return NULL; } +//todo: ?? Win condition? void snake(void) { - rooster* const gp = initialize(); - if (gp == NULL) { + initialize(); + if (GRID == NULL) { return; } + + // Created necessary threads. + pthread_mutex_init(&MUTEX, NULL); + pthread_t user_input; + pthread_t game_tick; + + // Show game. erase(); + show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y); + mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, "Press SPACEBAR to start playing."); - //todo: ?? Win condition? + rooster_zet_toestand(GRID, STATE_AAN_HET_SPELEN); - show_grid_on_offset(gp, OFFSET_X, OFFSET_Y); + // Allow turning before you let the snake move. + pthread_create(&user_input, NULL, play_snake, NULL); - 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); - } + // Cleanup the start playing message. + mvaddstr(MESSAGE_LOCATION.y, MESSAGE_LOCATION.x, " "); - // game_exit_screen(gp); + pthread_create(&game_tick, NULL, snake_move, NULL); - rooster_klaar(gp); + // Cleanup game thread logic. + pthread_join(game_tick, NULL); + pthread_join(user_input, NULL); + pthread_mutex_destroy(&MUTEX); + + rooster_klaar(GRID); graceful_exit(); } diff --git a/spel b/spel index 0c0f4fe..f4d050a 100755 Binary files a/spel and b/spel differ