MULTITHREADED FOR EPICNESSSSSS
This commit is contained in:
BIN
deel2.tar.gz
BIN
deel2.tar.gz
Binary file not shown.
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
206
snake.c
206
snake.c
@@ -6,8 +6,9 @@
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user