funguje
This commit is contained in:
commit
adea577dec
15
pvjc19cv9/Makefile
Normal file
15
pvjc19cv9/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
CFLAGS=-std=c99 -Wall -g
|
||||
CC=gcc
|
||||
|
||||
all: game
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm *.o
|
||||
rm game
|
||||
|
||||
game: main.o game.o world.o snake.o
|
||||
$(CC) main.o game.o world.o snake.o -ltermbox -lm -o game
|
||||
|
17
pvjc19cv9/README.md
Normal file
17
pvjc19cv9/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Snake Master
|
||||
|
||||
Implement Snake game.
|
||||
|
||||
Make the game to pass the automatic tests and make the game to be nice.
|
||||
|
||||
## Files
|
||||
|
||||
Please do not change file names.
|
||||
|
||||
- `snake.h`: you implementation should follow this header.
|
||||
- `snake.c`: implemement the game according to the documentation in header file in this file to pass automatic tests.
|
||||
- `Makefile`: rules to build the game.
|
||||
- `game.c`: modify this file to change the appereance of the game and the initial state.
|
||||
- `main.c`: trivial main function that runs the game
|
||||
- `world.c`: world game loop and ASCII graphics library (do not change).
|
||||
- `world.h`: world library interface (do not change).
|
86
pvjc19cv9/game.c
Normal file
86
pvjc19cv9/game.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include <termbox.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "world.h"
|
||||
#include "snake.h"
|
||||
|
||||
// Start is called one in the beginning
|
||||
void start(struct world* world,int argc, char** argv){
|
||||
// Allocate memory for the state
|
||||
struct state* st = calloc(1,(sizeof(struct state)));
|
||||
// Store pointer to the state to the world variable
|
||||
world->state = st;
|
||||
st->snake = NULL;
|
||||
st->sx = 0;
|
||||
st->sy = -1;
|
||||
int cy = world->height/2;
|
||||
int cx = world->width/2;
|
||||
st->snake = add_snake(st->snake,cx + 5 ,cy);
|
||||
st->snake = add_snake(st->snake,cx + 4,cy);
|
||||
st->snake = add_snake(st->snake,cx + 3,cy);
|
||||
st->snake = add_snake(st->snake,cx + 2,cy);
|
||||
st->snake = add_snake(st->snake,cx + 1,cy);
|
||||
st->snake = add_snake(st->snake,cx,cy);
|
||||
|
||||
int h = world->height;
|
||||
int w = world->width;
|
||||
|
||||
st->foodx[0] = rand() % w;
|
||||
st->foody[0] = rand() % h;
|
||||
st->foodx[1] = rand() % w;
|
||||
st->foody[1] = rand() % h;
|
||||
st->foodx[2] = rand() % w;
|
||||
st->foody[2] = rand() % h;
|
||||
st->foodx[3] = rand() % w;
|
||||
st->foody[3] = rand() % h;
|
||||
st->foodx[4] = rand() % w;
|
||||
st->foody[4] = rand() % h;
|
||||
}
|
||||
|
||||
// Step is called in a loop once in interval.
|
||||
// It should modify the state and draw it.
|
||||
int step(struct world* w,int key){
|
||||
// Get state pointer
|
||||
struct state* st = w->state;
|
||||
enum direction dir = DIR_NONE;
|
||||
if (key == TB_KEY_ARROW_LEFT){
|
||||
dir = DIR_LEFT;
|
||||
}
|
||||
else if (key == TB_KEY_ARROW_RIGHT){
|
||||
dir = DIR_RIGHT;
|
||||
}
|
||||
else if (key == TB_KEY_ARROW_UP){
|
||||
dir = DIR_UP;
|
||||
}
|
||||
else if (key == TB_KEY_ARROW_DOWN){
|
||||
dir = DIR_DOWN;
|
||||
}
|
||||
int r = step_state(st,dir,w->width,w->height);
|
||||
struct snake* sn = st->snake;
|
||||
while (sn != NULL){
|
||||
set_character(w,sn->x,sn->y,'x');
|
||||
sn = sn->next;
|
||||
}
|
||||
for (int i = 0 ; i < FOOD_COUNT; i++){
|
||||
if (st->foodx[i] >= 0 && st->foody[i] >= 0){
|
||||
set_character(w,st->foodx[i],st->foody[i],'*');
|
||||
}
|
||||
}
|
||||
if (r){
|
||||
set_message(w,20,20,"Koniec");
|
||||
}
|
||||
if (key == TB_KEY_ESC){
|
||||
// Non zero means finish the loop and stop the game.
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Stop is called after game loop is finished
|
||||
void stop(struct world* world){
|
||||
// Free memory for game state
|
||||
struct state* st = world->state;
|
||||
free_snake(st->snake);
|
||||
free(st);
|
||||
}
|
5
pvjc19cv9/main.c
Normal file
5
pvjc19cv9/main.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "world.h"
|
||||
|
||||
int main(int argc, char** argv){
|
||||
game(argc,argv);
|
||||
};
|
151
pvjc19cv9/snake.c
Normal file
151
pvjc19cv9/snake.c
Normal file
@ -0,0 +1,151 @@
|
||||
#include "snake.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
struct snake* add_snake(struct snake* snake,int x,int y){
|
||||
|
||||
struct snake* new=calloc(sizeof(struct snake),1);
|
||||
|
||||
new->x=x;
|
||||
|
||||
new->y=y;
|
||||
|
||||
new->next=snake;
|
||||
|
||||
return new;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct snake* remove_snake(struct snake* snake){
|
||||
|
||||
if(snake==NULL)return NULL;
|
||||
|
||||
if(snake->next==NULL){
|
||||
|
||||
free(snake);
|
||||
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
struct snake* temp=snake;
|
||||
|
||||
while(temp->next->next!=NULL){
|
||||
|
||||
temp=snake->next;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
free(temp->next);
|
||||
|
||||
temp->next=NULL;
|
||||
|
||||
|
||||
return snake;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void free_snake(struct snake* sn){
|
||||
|
||||
if(sn==NULL)return;
|
||||
|
||||
if(sn->next!=NULL)free_snake(sn->next);
|
||||
|
||||
free(sn);
|
||||
|
||||
sn=NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int step_state(struct state* st,enum direction dir,int width,int height){
|
||||
|
||||
|
||||
if(st==NULL)return 0;
|
||||
|
||||
switch(dir){
|
||||
|
||||
case DIR_DOWN:st->sx=0;st->sy=1;break;
|
||||
|
||||
case DIR_UP:st->sx=0;st->sy=-1;break;
|
||||
|
||||
case DIR_LEFT:st->sx=-1;st->sy=0;break;
|
||||
|
||||
case DIR_RIGHT:st->sx=1;st->sy=0;break;
|
||||
|
||||
default:break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
int newx=st->snake->x+st->sx;
|
||||
|
||||
if(newx>width)return END_WALL;
|
||||
|
||||
if(newx<0)return END_WALL;
|
||||
|
||||
int newy=st->snake->y+st->sy;
|
||||
|
||||
if(newy>height)return END_WALL;
|
||||
|
||||
if(newy<0)return END_WALL;
|
||||
|
||||
struct snake* pos=st->snake;
|
||||
|
||||
while(pos!=NULL){
|
||||
|
||||
if(pos->x==newx && pos->y==newy){
|
||||
|
||||
return END_SNAKE;
|
||||
|
||||
}
|
||||
|
||||
pos=pos->next;
|
||||
|
||||
}
|
||||
|
||||
for(int i=0;i<FOOD_COUNT;i++){
|
||||
|
||||
if(st->foodx[i]==newx && st->foody[i]==newy){
|
||||
|
||||
st->foodx[i]=-1;
|
||||
|
||||
st->foody[i]=-1;
|
||||
|
||||
add_snake(st->snake,newx,newy);
|
||||
|
||||
return END_CONTINUE;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
for(int i=0;i<=FOOD_COUNT;i++){
|
||||
|
||||
if(i==FOOD_COUNT)return END_FOOD;
|
||||
|
||||
if(st->foodx>=0)break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
st->snake=add_snake(st->snake,newx,newy);
|
||||
|
||||
remove_snake(st->snake);
|
||||
|
||||
return END_CONTINUE;
|
||||
|
||||
}
|
128
pvjc19cv9/snake.h
Normal file
128
pvjc19cv9/snake.h
Normal file
@ -0,0 +1,128 @@
|
||||
#ifndef snake_h_INCLUDED
|
||||
#define snake_h_INCLUDED
|
||||
|
||||
|
||||
// Number of food items on the plane
|
||||
#define FOOD_COUNT 5
|
||||
|
||||
/**
|
||||
* One part of the snake;
|
||||
*
|
||||
* The snake is a linked list;
|
||||
*/
|
||||
|
||||
struct snake {
|
||||
// x position of the snake part
|
||||
int x;
|
||||
// y position of the snake part
|
||||
int y;
|
||||
// Pointer to the next snake part.
|
||||
// The last part of the snake has NULL pointer to the next part.
|
||||
struct snake* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Direction of the snake
|
||||
*/
|
||||
|
||||
enum direction {
|
||||
// No direction command was given
|
||||
DIR_NONE,
|
||||
// Up arrow, direction to north
|
||||
DIR_UP,
|
||||
// Down arrow, direction to south
|
||||
DIR_DOWN,
|
||||
// Left arow, direction to west
|
||||
DIR_LEFT,
|
||||
// Right arrow, direction east.
|
||||
DIR_RIGHT
|
||||
};
|
||||
|
||||
enum endgame {
|
||||
// Continue the game
|
||||
END_CONTINUE = 0,
|
||||
// Snake hit a wall
|
||||
END_WALL,
|
||||
// Snake hit itself
|
||||
END_SNAKE,
|
||||
// No food left
|
||||
END_FOOD,
|
||||
// Other reason to end
|
||||
END_USER
|
||||
};
|
||||
|
||||
/**
|
||||
* State of the game.
|
||||
*
|
||||
* The state consists of the snake, its speed and food on the plane.
|
||||
*
|
||||
* The snake is a linked list of snake parts.
|
||||
*
|
||||
* Speed vector is a vector added to the last head position to create a new head.
|
||||
*
|
||||
* Food are points on the plane. Food with negative coordinates meads food is already eaten.
|
||||
*/
|
||||
|
||||
struct state {
|
||||
// Snake as a linked list
|
||||
struct snake* snake;
|
||||
// X of the speed vector
|
||||
int sx;
|
||||
// Y of the speed vector
|
||||
int sy;
|
||||
// X of the food positions
|
||||
int foodx[FOOD_COUNT];
|
||||
// Y of the food positions
|
||||
int foody[FOOD_COUNT];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new snake part with given position. The new snake part becomes the new head.
|
||||
* @param head of the snake.
|
||||
* @param x coordinate of the new head;
|
||||
* @param y coordinate of the new head.
|
||||
* @return new head of the snake.
|
||||
*/
|
||||
struct snake* add_snake(struct snake* snake,int x,int y);
|
||||
/**
|
||||
* Remove the last snake part.
|
||||
* The last snake part should always have NULL next pointer.
|
||||
* @param head of the snake.
|
||||
* @return new head of the snake.
|
||||
*/
|
||||
struct snake* remove_snake(struct snake* snake);
|
||||
/**
|
||||
* Remove each snake part;
|
||||
* @param head of the snake.
|
||||
*/
|
||||
void free_snake(struct snake* sn);
|
||||
/**
|
||||
* Change game state.
|
||||
*
|
||||
* The function should first set the speed vector according to the direction.
|
||||
* If direction is:
|
||||
* - DIR_NONE, speed vector does not change
|
||||
* - DIR_UP, speed vector is 0,-1
|
||||
* - DIR_DOWN, speed vector is 0,1
|
||||
* - DIR_LEFT, speed vector is -1, 0
|
||||
* - DIR_RIGHT, speed vector is 1, 0
|
||||
*
|
||||
* Then it calculates the new position of the head according to the old position
|
||||
* of the snake head and the speed vector.
|
||||
*
|
||||
* - If the new position is on the snake, end the game, return END_SNAKE.
|
||||
* - If the new position is on the plane, add new snake part on the new position and remove the last part of the snake, return END_CONTINUE.
|
||||
* - If the new position is on the food, mark food as eaten and add new snake part on the position of the food. Return END_CONTINUE.
|
||||
* - If there is no food left, end the game, return END_FOOD.
|
||||
*
|
||||
* @param old state of the game.
|
||||
* @param direction command, one of "enum direction".
|
||||
* @param width of the plane.
|
||||
* @param height of the plane.
|
||||
* @return reason to end the game according to enum endgame.
|
||||
*/
|
||||
int step_state(struct state* state,enum direction dir,int width,int height);
|
||||
|
||||
|
||||
#endif // snake_h_INCLUDED
|
||||
|
117
pvjc19cv9/world.c
Normal file
117
pvjc19cv9/world.c
Normal file
@ -0,0 +1,117 @@
|
||||
#include "world.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termbox.h>
|
||||
#include <time.h>
|
||||
|
||||
void destroy_world(struct world* world){
|
||||
stop(world);
|
||||
tb_shutdown();
|
||||
}
|
||||
|
||||
void end_message(struct world* world, const char* message){
|
||||
destroy_world(world);
|
||||
puts(message);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void assert_message(int event, struct world* world, const char* message){
|
||||
if (event == 0){
|
||||
end_message(world,message);
|
||||
}
|
||||
}
|
||||
|
||||
void init_world(struct world* w) {
|
||||
assert_message(w != NULL,w,"init_world:: world is NULL");
|
||||
w->height = tb_height();
|
||||
w->width = tb_width();
|
||||
if (w->interval <= 0){
|
||||
w->interval = 100;
|
||||
}
|
||||
}
|
||||
|
||||
void set_color_character(struct world* w,int x,int y,int character,uint16_t foreground,uint16_t background) {
|
||||
assert_message(w != NULL,w,"set_character:: world is NULL");
|
||||
if (x < 0 || x >= w->width){
|
||||
char msg[100];
|
||||
sprintf(msg,"set_character:: width %d is out of bounds (0,%d)",x,w->width);
|
||||
end_message(w,msg);
|
||||
}
|
||||
if (y < 0 || y >= w->height){
|
||||
char msg[100];
|
||||
sprintf(msg,"set_character:: height %d is out of bounds (0,%d)",y,w->height);
|
||||
end_message(w,msg);
|
||||
}
|
||||
tb_change_cell(x,y,character,foreground,background);
|
||||
}
|
||||
|
||||
void set_character(struct world* w,int x,int y,int character) {
|
||||
set_color_character(w,x,y,character,TB_WHITE,TB_BLACK);
|
||||
}
|
||||
|
||||
void set_message(struct world* w,int x,int y,const char* message) {
|
||||
int l = strlen(message);
|
||||
for (int i = 0; i < l; i++){
|
||||
set_character(w,x+i,y,message[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void set_color_message(struct world* w,int x,int y,const char* message,int character,uint16_t foreground,uint16_t background) {
|
||||
int l = strlen(message);
|
||||
for (int i = 0; i < l; i++){
|
||||
set_color_character(w,x+i,y,message[i],foreground,background);
|
||||
}
|
||||
}
|
||||
|
||||
int step_world(struct world* world,int eventkey){
|
||||
init_world(world);
|
||||
tb_clear();
|
||||
int r = step(world,eventkey);
|
||||
tb_present();
|
||||
return r;
|
||||
}
|
||||
|
||||
void game(int argc, char** argv){
|
||||
srand(time(NULL));
|
||||
int r = tb_init();
|
||||
if (r < 0){
|
||||
puts("Termbox Error.");
|
||||
return;
|
||||
}
|
||||
struct tb_event event;
|
||||
struct world world;
|
||||
memset(&world,0,sizeof(struct world));
|
||||
init_world(&world);
|
||||
start(&world,argc,argv);
|
||||
r = step_world(&world,WORLD_START_EVENT);
|
||||
while (!r) {
|
||||
int t = tb_peek_event(&event,world.interval);
|
||||
if (t == -1){
|
||||
end_message(&world,"termox poll error");
|
||||
}
|
||||
int eventkey = WORLD_TIMEOUT_EVENT;
|
||||
if (event.type == TB_EVENT_KEY){
|
||||
eventkey = event.key;
|
||||
if (event.ch > 0){
|
||||
eventkey = event.ch;
|
||||
}
|
||||
}
|
||||
else if (event.type == TB_EVENT_RESIZE){
|
||||
eventkey = WORLD_RESIZE_EVENT;
|
||||
}
|
||||
else if (event.type == TB_EVENT_MOUSE){
|
||||
// Ignore mouse events
|
||||
continue;
|
||||
}
|
||||
r = step_world(&world,eventkey);
|
||||
if (eventkey == TB_KEY_CTRL_C){
|
||||
r = 1;
|
||||
}
|
||||
else if (eventkey == TB_KEY_CTRL_D){
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
destroy_world(&world);
|
||||
};
|
82
pvjc19cv9/world.h
Normal file
82
pvjc19cv9/world.h
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef _GAME_H_
|
||||
#define _GAME_H_
|
||||
|
||||
// Signalizes game next loop
|
||||
// (abuses ctrl tilda keypress code)
|
||||
#define WORLD_TIMEOUT_EVENT 0
|
||||
// Signlizes the first step of the game loop
|
||||
// (abuses ctrl a keypress)
|
||||
#define WORLD_START_EVENT 1
|
||||
// Signalizes resize of the screen (width and height have changed)
|
||||
// (abuses ctrl b keypress)
|
||||
#define WORLD_RESIZE_EVENT 2
|
||||
|
||||
#include <termbox.h>
|
||||
/**
|
||||
* Game world represented as a rectangular matrix of colorful characters.
|
||||
*
|
||||
* Point [0,0] is displayed the upper left corner of the screen.
|
||||
*
|
||||
*/
|
||||
|
||||
struct world {
|
||||
/**
|
||||
* Width of the screen.
|
||||
*/
|
||||
int width;
|
||||
/**
|
||||
* Height of the screen.
|
||||
*/
|
||||
int height;
|
||||
/**
|
||||
* State of the game managed by the programmer.
|
||||
*/
|
||||
void* state;
|
||||
/**
|
||||
* Interval to wait for next step.
|
||||
*/
|
||||
int interval;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets cell to a state.
|
||||
* @param world
|
||||
* @param x coordinate of cell
|
||||
* @param y coordinate of cell
|
||||
* @param new state of the cell
|
||||
*/
|
||||
void set_character(struct world* w,int x,int y,int value);
|
||||
|
||||
void set_message(struct world* w,int x,int y,const char* message);
|
||||
|
||||
/**
|
||||
* Runs the game loop.
|
||||
* @param number of commandline arguments
|
||||
* @param commandline arguments
|
||||
*/
|
||||
void game(int argc,char** argv);
|
||||
|
||||
/**
|
||||
* Initializes user state.
|
||||
* @param world
|
||||
* @param number of commandline arguments
|
||||
* @param commandline arguments
|
||||
*
|
||||
*/
|
||||
void start(struct world*,int argc,char** argv);
|
||||
|
||||
/**
|
||||
* Free user state.
|
||||
* @param world
|
||||
*/
|
||||
void stop(struct world* world);
|
||||
|
||||
/**
|
||||
* Changes state of the world according to pressed keys or events.
|
||||
* @param world
|
||||
* @param code of the event. Event is WORLD_START_EVENT for the first step, WORLD_TIMEOUT_EVENT for regular step or Termbox key code (TB_KEY_*) for key press event. Mouse events are ignored.
|
||||
*/
|
||||
int step(struct world* world,int key);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user