SNAKEEEEEEEEEEEE
This commit is contained in:
BIN
deel2.tar.gz
Normal file
BIN
deel2.tar.gz
Normal file
Binary file not shown.
@@ -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':
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "rooster.h"
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
303
snake.c
303
snake.c
@@ -6,9 +6,65 @@
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user