MULTITHREADED FOR EPICNESSSSSS

This commit is contained in:
2025-10-17 14:29:22 +02:00
parent 0e133aff32
commit f0e9d5c751
7 changed files with 148 additions and 95 deletions

Binary file not shown.

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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.
while (rooster_vraag_toestand(rp) == STATE_AAN_HET_SPELEN) {
speel_maze(rp);
}
// Exit game.
game_exit_screen(rp);

View File

@@ -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;

138
snake.c
View File

@@ -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,21 +225,24 @@ 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) {
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:
@@ -251,74 +259,94 @@ static void snake_move(rooster *gp) {
break;
}
snake_action action = collision_check(rooster_kijk(gp, new_head.x, new_head.y));
snake_action action = collision_check(rooster_kijk(GRID, new_head.x, new_head.y));
update_snake(gp, new_head, action);
show_grid_on_offset(gp, OFFSET_X, OFFSET_Y);
update_snake(new_head, action);
show_grid_on_offset(GRID, OFFSET_X, OFFSET_Y);
pthread_mutex_unlock(&MUTEX);
}
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()) {
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(gp, DIRECTION_UP);
turn_snake(DIRECTION_UP);
break;
case KEY_DOWN: // fallthrough
case 's':
case 'S':
turn_snake(gp, DIRECTION_DOWN);
turn_snake(DIRECTION_DOWN);
break;
case KEY_LEFT: // fallthrough
case 'a':
case 'A':
turn_snake(gp, DIRECTION_LEFT);
turn_snake(DIRECTION_LEFT);
break;
case KEY_RIGHT: // fallthrough
case 'd':
case 'D':
turn_snake(gp, DIRECTION_RIGHT);
turn_snake(DIRECTION_RIGHT);
break;
case KEY_BACKSPACE:
rooster_zet_toestand(gp, STATE_QUIT);
rooster_zet_toestand(GRID, STATE_QUIT);
break;
case ERR:
snake_move(gp);
}
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();
}

BIN
spel

Binary file not shown.