forked from nakidai/csnake
1
0
Fork 0

Compare commits

...

44 Commits

Author SHA1 Message Date
Nakidai 06f05e7412
Version 1.2 2024-03-07 00:37:15 +03:00
Nakidai cd0649a375
Return MinGW to tested 2024-03-07 00:33:17 +03:00
Nakidai 351c45b1c1
It's 2024 2024-03-07 00:32:37 +03:00
Nakidai a735950c67
Move config.mk to include/ 2024-03-07 00:31:36 +03:00
Nakidai ad871be017
Some work with templates
- Now configure script also reads templates (files which end by .in)
  - So added template for config.mk
- Moved templates to templates/ folder
2024-03-07 00:27:37 +03:00
Nakidai 8c6395592d
Merge branch 'fix-mingw' 2024-03-07 00:17:20 +03:00
Nakidai 9d6264d100
Also register troubles between MinGW and MSVC 2024-03-07 00:16:19 +03:00
Nakidai bb55e690d0
Completely fix #3 2024-03-07 00:16:19 +03:00
Nakidai 1c7a0ef6b2
Probably fix #3 2024-03-07 00:16:18 +03:00
Nakidai 71229a9205 Remove MinGW, because it can't compile project now 2024-02-23 17:53:07 +03:00
Nakidai a67704ebff Add flag about c standard 2024-02-23 17:13:02 +03:00
Nakidai Perumenei eb82ad3173 Remove getchInit from input.c 2023-12-17 03:10:52 +03:00
Nakidai Perumenei 71cfceee4b Now game clears cursor at exit on ^C 2023-12-17 03:07:48 +03:00
Nakidai Perumenei 22d3cdeb43 Repair cursor on exit for posix 2023-12-17 00:41:42 +03:00
Nakidai cf71b4d57d Some movements 2023-12-09 00:49:36 +03:00
Nakidai e2d01db144 Refactor
- Fix bug when Make cannot create obj/platform
- Remove some mallocs
2023-12-09 00:35:49 +03:00
Nakidai bfaf0f0e02 Fix Makefile 2023-12-02 12:29:29 +03:00
Nakidai f70e3d949a Decrease lines count 2023-12-01 23:11:01 +03:00
Nakidai 1685dc927b Remove functions for free screen and player as they're unused 2023-12-01 23:07:02 +03:00
Nakidai 517e57cfc5 I like semicolons 2023-12-01 22:46:43 +03:00
Nakidai eda7badf47 Finally separate platform-specific code from independent 2023-12-01 22:39:38 +03:00
Nakidai 50c7f9d632 Now you can configurate sleep time 2023-11-30 18:05:51 +03:00
Nakidai 4b208ab1c4 Move some functions
- Remove platform.c, instead use inlines
- Some small functions in screen.c are now inline too
- Make functions in sleep.c and input.c not specified in header static
2023-11-30 17:52:46 +03:00
Nakidai be37715af7 Automatically link pthread on FreeBSD 2023-11-25 20:55:57 +03:00
Nakidai cf8fe181a2 Change way to handle arguments in configure 2023-11-25 20:27:09 +03:00
Nakidai 49bd6ee329 Move platform specific code & make sleep platform independent 2023-11-25 13:24:01 +03:00
Nakidai 9933de02ac Edit year of license ._. 2023-11-25 01:29:19 +03:00
Nakidai 7911484f02 Add license 2023-11-25 01:28:45 +03:00
Nakidai 8acdec21a7 Rename target default to all 2023-11-25 00:55:36 +03:00
Nakidai 123760b56a Add csnake to gitignore 2023-11-25 00:53:51 +03:00
Nakidai e6995af34a Prepare for release 2023-11-25 00:52:02 +03:00
Nakidai 8e8a1c5abd Add config.h and config.mk to .gitignore 2023-11-25 00:17:55 +03:00
Nakidai 9c54a483bd Remove some warnings 2023-11-25 00:11:34 +03:00
Nakidai 970fb66db8 Move pthread_t declaration 2023-11-25 00:00:44 +03:00
Nakidai 1ff14f7cbc Add windows support in threads and sleep 2023-11-24 23:58:37 +03:00
Nakidai ce1e91d90f Switch back to pthread.h 2023-11-24 23:32:42 +03:00
Nakidai 0b74ef8618 Add windows support 2023-11-24 22:58:33 +03:00
Nakidai 793db5f00a Fix allocation bug and some other
- Add *.core (core dumps) to .gitignore
- Change defines of directions to enum
2023-11-24 22:14:53 +03:00
Nakidai 1d63892d67
Add README 2023-11-07 04:49:55 +03:00
Nakidai 98089c4351
Add pause key
Also moved render with sleep to start of loop, so you can see first
frame.
2023-11-01 19:29:12 +03:00
Nakidai b64ffdb82a
Fix condition 2023-11-01 19:21:51 +03:00
Nakidai c31de323f0
Fix bug when game freezes on max possible score 2023-11-01 18:34:19 +03:00
Nakidai bcf6465f4e
Optimize game over condition 2023-11-01 13:27:42 +03:00
nakidai 460e4e33ed Merge pull request 'Add cmake, remove non-standard random' (#2) from neko/csnake:cmake into master
Reviewed-on: https://nakidai.ddns.net/nakidai/csnake/pulls/2
Size of CMake is over 70M :O
2023-10-31 06:40:45 +03:00
26 changed files with 371 additions and 185 deletions

6
.gitignore vendored
View File

@ -1,2 +1,6 @@
obj/ obj/
game build/
csnake
*.core
include/config.h
include/config.mk

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(csnake VERSION 0.1.0 LANGUAGES C) project(csnake VERSION 1.2 LANGUAGES C)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed.") message(FATAL_ERROR "In-source builds are not allowed.")
@ -10,14 +10,29 @@ add_executable(csnake
src/screen.c src/screen.c
src/input.c src/input.c
src/player.c src/player.c
src/sleep.c
src/platform/getch.c
src/platform/game.c
) )
set_target_properties(csnake PROPERTIES C_STANDARD 11) set_target_properties(csnake PROPERTIES C_STANDARD 11)
set_target_properties(csnake PROPERTIES C_EXTENSIONS FALSE) set_target_properties(csnake PROPERTIES C_EXTENSIONS FALSE)
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
target_link_libraries(csnake pthread)
elseif(${MINGW})
set(CMAKE_C_FLAGS "-D_UCRT")
target_link_libraries(csnake ucrt)
endif()
target_include_directories(csnake PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(csnake PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(SIZE 10 CACHE STRING "Size of game field") set(FIELD_SIZE 10 CACHE STRING "Size of game field")
set(DEFX 0 CACHE STRING "Start x") set(DEFX 0 CACHE STRING "Start x")
set(DEFY 0 CACHE STRING "Start y") set(DEFY 0 CACHE STRING "Start y")
set(SLEEP 1000 CACHE STRING "Sleep between frames (ms)")
configure_file(include/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/config.h) configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/templates/config.h.in
${CMAKE_CURRENT_SOURCE_DIR}/include/config.h
)

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
Copyright 2024 nakidai
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,24 +1,30 @@
include config.mk include include/config.mk
INCLUDE = -Iinclude INCLUDE = -Iinclude
RM = rm -f RM = rm -f
SRCDIR = src SRCDIR = src
OBJDIR = obj OBJDIR = obj
SRC = main.c screen.c input.c player.c SRC = main.c screen.c input.c player.c sleep.c platform/getch.c platform/game.c
OBJ = $(addprefix $(OBJDIR)/,$(SRC:.c=.o)) OBJ = $(addprefix $(OBJDIR)/,$(SRC:.c=.o))
default: $(OUT) # Link pthread on FreeBSD
DEFLDFLAGS += $(shell if echo "" | cc -E -dM -xc - | grep __FreeBSD__ > /dev/null 2>&1; then echo "-lpthread"; fi)
# Use ucrt on MinGW
DEFLDFLAGS += $(shell if echo "" | cc -E -dM -xc - | grep __MINGW32__ > /dev/null 2>&1; then echo "-lucrt"; fi)
DEFCFLAGS += $(shell if echo "" | cc -E -dM -xc - | grep __MINGW32__ > /dev/null 2>&1; then echo "-D_UCRT"; fi)
obj: all: $(OUT)
mkdir obj
$(OBJDIR)/platform:
mkdir -p $(OBJDIR)/platform
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) -c -o $@ $< $(CFLAGS) $(INCLUDE) $(CC) -c -std=c11 -o $@ $< $(CFLAGS) $(DEFCFLAGS) $(INCLUDE)
$(OUT): obj $(OBJ) $(OUT): $(OBJDIR)/platform $(OBJ)
$(CC) -o $@ $(OBJ) $(LDFLAGS) $(CC) -o $@ $(OBJ) $(LDFLAGS) $(DEFLDFLAGS)
clean: clean:
$(RM) $(OUT) $(OBJDIR)/* $(RM) $(OUT) $(OBJDIR)/*.o $(OBJDIR)/platform/*.o
.PHONY: default clean .PHONY: default clean

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# csnake
Snakegame written in C.
Controls
--
- WASD - movement
- P - Pause
Supported platforms
--
Was tested on:
- FreeBSD 13.2
- Windows 11 (both MSVC and MinGW)
- Linux 6.5.8 (glibc 2.38-7)
Building
--
- On \*nix-like systems you can use GNU Make with configure script providing arguments through environment variables or CMake
- On Windows you can use CMake

View File

@ -1,4 +0,0 @@
CC = cc
CFLAGS =
LDFLAGS =
OUT = game

49
configure vendored
View File

@ -1,25 +1,37 @@
#!/bin/sh #!/bin/sh
if [[ "$*" == *"--help"* ]] || [[ "$*" == *"-h"* ]] usage()
then {
echo "Use environment variables to pass values: echo "Use environment variables to pass values:
CC - compiler (default: cc) CC - compiler (default: cc)
CFLAGS - flags for compiler CFLAGS - flags for compiler
LDFLAGS - flags for linker LDFLAGS - flags for linker
OUT - out file (default: game OUT - out file (default: csnake
SIZE - size of game field FIELD_SIZE - size of game field
DEFX - start x DEFX - start x
DEFY - start y" DEFY - start y
SLEEP - sleep between frames (ms)"
exit 1 exit 1
fi }
while test $# -gt 0; do
case "$1" in
-h) usage
;;
--help) usage
;;
esac
shift
done
CC=${CC:-cc} CC=${CC:-cc}
CFLAGS=${CFLAGS:-} CFLAGS=${CFLAGS:-}
LDFLAGS=${LDFLAGS:-} LDFLAGS=${LDFLAGS:-}
OUT=${OUT:-game} OUT=${OUT:-csnake}
SIZE=${SIZE:-10} FIELD_SIZE=${FIELD_SIZE:-10}
DEFX=${DEFX:-0} DEFX=${DEFX:-0}
DEFY=${DEFY:-0} DEFY=${DEFY:-0}
SLEEP=${SLEEP:-1000}
echo "Makefile configuration:" echo "Makefile configuration:"
echo "Compiler: $CC" echo "Compiler: $CC"
@ -28,15 +40,10 @@ echo "LDFLAGS: $LDFLAGS"
echo "Out file: $OUT" echo "Out file: $OUT"
echo echo
echo "Code configuration:" echo "Code configuration:"
echo "Size: $SIZE" echo "Field size: $FIELD_SIZE"
echo "Start x: $DEFX" echo "Start x: $DEFX"
echo "Start y: $DEFY" echo "Start y: $DEFY"
echo "Sleep: $SLEEP"
echo "CC = $CC eval "echo \"$(cat templates/config.mk.in)\"" > include/config.mk
CFLAGS = $CFLAGS eval "echo \"$(cat templates/config.h.in)\"" > include/config.h
LDFLAGS = $LDFLAGS
OUT = $OUT" > config.mk
echo "#define SIZE $SIZE
#define DEFX $DEFX
#define DEFY $DEFY" > include/config.h

View File

@ -1,3 +0,0 @@
#define SIZE 10
#define DEFX 0
#define DEFY 0

View File

@ -1,3 +0,0 @@
#define SIZE ${SIZE}
#define DEFX ${DEFX}
#define DEFY ${DEFY}

View File

@ -3,12 +3,14 @@
#include <stdbool.h> #include <stdbool.h>
#include "platform/thread.h"
typedef struct input_args_t typedef struct input_args_t
{ {
char *out; int *out;
bool *alive; bool *alive;
} InputArgs; } InputArgs;
int input(void *vargp); ThreadR input(void *vargp);
#endif /* __INPUT_H__ */ #endif /* __INPUT_H__ */

6
include/platform/game.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __PLATFORM_GAME_H__
#define __PLATFORM_GAME_H__
void platformGameInit(void);
#endif /* __PLATFORM_GAME_H__ */

15
include/platform/getch.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __GETCH_H__
#define __GETCH_H__
#ifdef _WIN32
#include <conio.h>
#define getch _getch
inline int getchInit(void) { return 0; }
#else
int getch(void);
void getchInit(void);
void getchResetTerminalStateHandler(int sig);
void getchResetTerminalState(void);
#endif /* _WIN32 */
#endif /* __GETCH_H__ */

29
include/platform/screen.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __PLATFORM_SCREEN_H__
#define __PLATFORM_SCREEN_H__
#ifdef _WIN32
#ifdef __MINGW32__
#include <windows.h>
#else
#include <Windows.h>
#endif /* __MINGW32__ */
#else
#include <stdio.h>
#endif /* _WIN32 */
#ifdef _WIN32
static inline void resetCoordinates(void)
{
HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(output, (COORD){0});
}
#else
static inline void resetCoordinates(void)
{
printf("\e[1;1H\e[2J");
}
#endif /* _WIN32 */
#endif /* __PLATFORM_SCREEN_H__ */

29
include/platform/thread.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __THREAD_H__
#define __THREAD_H__
#ifdef _WIN32
#include <process.h>
#else
#include <pthread.h>
#include <stddef.h>
#endif /* _WIN32 */
#ifdef _WIN32
typedef void ThreadR;
#define ThreadReturn return
#else
typedef void* ThreadR;
#define ThreadReturn return NULL
#endif /* _WIN32 */
typedef ThreadR (*Thread)(void *);
static inline void threadCreate(Thread function, void *args)
{
#ifdef _WIN32
_beginthread(function, 0, args);
#else
pthread_create(&(pthread_t){0}, 0, function, args);
#endif /* _WIN32 */
}
#endif /* __THREAD_H__ */

View File

@ -4,12 +4,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "food.h" #include "food.h"
#define UP 0 typedef enum { UP, RIGHT, DOWN, LEFT} Direction;
#define RIGHT 1
#define DOWN 2
#define LEFT 3
typedef int Direction;
typedef struct player_node_t PlayerNode; typedef struct player_node_t PlayerNode;
typedef struct player_t Player; typedef struct player_t Player;
@ -26,11 +21,10 @@ struct player_t
int score; int score;
}; };
Player *playerCreate(Direction direction, int x, int y, int score); void playerCreate(Player *buffer, Direction direction, int x, int y, int score);
void playerFree(Player *player);
bool playerCheckSelfCollision(Player *player); bool playerCheckSelfCollision(Player player);
bool playerCheckFoodCollision(Player *player, Food food); bool playerCheckFoodCollision(Player player, Food food);
bool playerDoTick(Player *player, Food food); bool playerDoTick(Player *player, Food food);
#endif /* __PLAYER_H__ */ #endif /* __PLAYER_H__ */

View File

@ -1,6 +1,8 @@
#ifndef __SCREEN_H__ #ifndef __SCREEN_H__
#define __SCREEN_H__ #define __SCREEN_H__
#include <string.h>
typedef char Point; typedef char Point;
typedef struct screen_t typedef struct screen_t
{ {
@ -9,11 +11,18 @@ typedef struct screen_t
Point *screen; Point *screen;
} Screen; } Screen;
Screen *screenCreate(int width, int height, Point fill_value); void screenCreate(Screen *buffer, int width, int height, Point fill_value);
void screenFree(Screen *screen); void screenShow(Screen screen);
static inline Point *screenGetPoint(Screen screen, int x, int y)
{
return screen.screen + x + (y * screen.width);
}
static inline void screenSet(Screen screen, Point fill_value)
{
memset(screen.screen, fill_value, screen.width * screen.height * sizeof(char));
}
Point *screenGetPoint(Screen *screen, int x, int y);
void screenShow(Screen *screen);
void screenSet(Screen *screen, char fill_value);
#endif /* __SCREEN_H__ */ #endif /* __SCREEN_H__ */

6
include/sleep.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __SLEEP_H__
#define __SLEEP_H__
void sleepMS(int msec);
#endif /* __SLEEP_H__ */

View File

@ -1,35 +1,18 @@
#include "input.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <termios.h>
char getch(void) #include "input.h"
{ #include "platform/thread.h"
char buf = 0; #include "platform/getch.h"
struct termios old = { 0 };
fflush(stdout);
if (tcgetattr(0, &old) < 0) perror("tcsetattr()");
old.c_lflag &= ~ICANON; // local modes = Non Canonical mode
old.c_lflag &= ~ECHO; // local modes = Disable echo.
old.c_cc[VMIN] = 1; // control chars (MIN value) = 1
old.c_cc[VTIME] = 0; // control chars (TIME value) = 0 (No time)
if (tcsetattr(0, TCSANOW, &old) < 0) perror("tcsetattr ICANON");
if (read(0, &buf, 1) < 0) perror("read()");
old.c_lflag |= ICANON; // local modes = Canonical mode
old.c_lflag |= ECHO; // local modes = Enable echo.
if (tcsetattr(0, TCSADRAIN, &old) < 0) perror ("tcsetattr ~ICANON");
return buf;
}
int input(void *vargp) ThreadR input(void *vargp)
{ {
char *out = ((InputArgs *)vargp)->out; int *out = ((InputArgs *)vargp)->out;
bool *alive = ((InputArgs *)vargp)->alive; bool *alive = ((InputArgs *)vargp)->alive;
while (*alive) while (*alive)
{ {
*out = getch(); *out = getch();
} }
return 0; ThreadReturn;
} }

View File

@ -1,7 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <threads.h>
#include <time.h> #include <time.h>
#include "input.h" #include "input.h"
@ -9,89 +8,87 @@
#include "player.h" #include "player.h"
#include "food.h" #include "food.h"
#include "config.h" #include "config.h"
#include "sleep.h"
#include "platform/thread.h"
#include "platform/screen.h"
#include "platform/game.h"
void drawPlayer(Player *player, Screen *screen) void drawPlayer(Player player, Screen screen)
{ {
PlayerNode *node; PlayerNode *node;
for (node = player->tail; node != NULL; node = node->next) for (node = player.tail; node != NULL; node = node->next)
*screenGetPoint(screen, node->x, node->y) = '#'; *screenGetPoint(screen, node->x, node->y) = '#';
} }
Food generateFood(Player *player) Food generateFood(Player player)
{ {
Food food; Food food;
do do
{ {
food = (Food){rand() % SIZE, rand() % SIZE}; food = (Food){rand() % FIELD_SIZE, rand() % FIELD_SIZE};
} while (playerCheckFoodCollision(player, food)); } while (playerCheckFoodCollision(player, food));
return food; return food;
} }
void resetCoordinates(void)
{
printf("\e[1;1H\e[2J");
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
srand(time(NULL)); srand((unsigned int)time(NULL));
Player *player = playerCreate(DOWN, DEFX, DEFY, 0); platformGameInit();
Screen *screen = screenCreate(SIZE, SIZE, ' ');
PlayerNode *node;
thrd_t input_thread;
int i;
int head_x, head_y;
Food food = generateFood(player);
bool *running = malloc(sizeof(bool)); *running = true; Player player; playerCreate(&player, DOWN, DEFX, DEFY, 0);
char *key = malloc(sizeof(char)); *key = 0; Screen screen; screenCreate(&screen, FIELD_SIZE, FIELD_SIZE, ' ');
InputArgs input_args = (InputArgs){ key, running }; Food food = generateFood(player);
thrd_create(&input_thread, input, &input_args); int key = 0;
while (*running) bool running = true;
bool stopped = false;
int head_x, head_y;
threadCreate(input, &(InputArgs){ &key, &running });
while (running)
{ {
switch (*key)
{
case 'q':
*running = false; return 0;
case 'w':
if (player->direction == DOWN) break;
player->direction = UP; break;
case 'd':
if (player->direction == LEFT) break;
player->direction = RIGHT; break;
case 's':
if (player->direction == UP) break;
player->direction = DOWN; break;
case 'a':
if (player->direction == RIGHT) break;
player->direction = LEFT; break;
}
if (playerDoTick(player, food))
food = generateFood(player);
if (playerCheckSelfCollision(player))
{
*running = false;
break;
}
head_x = player->head->x;
head_y = player->head->y;
if (head_x >= SIZE || head_x < 0 || head_y >= SIZE || head_y < 0)
{
*running = false;
break;
}
screenSet(screen, ' '); screenSet(screen, ' ');
drawPlayer(player, screen); drawPlayer(player, screen);
*screenGetPoint(screen, food.x, food.y) = '@'; *screenGetPoint(screen, food.x, food.y) = '@';
resetCoordinates(); resetCoordinates();
screenShow(screen); screenShow(screen);
for (i = 0; i < SIZE*2; ++i) putchar('-'); for (int i = 0; i < FIELD_SIZE*2; ++i) putchar('-');
printf("\nScore: %d\n", player->score); printf("\nScore: %d\n", player.score);
thrd_sleep(&(struct timespec){.tv_sec=1}, NULL); sleepMS(SLEEP);
switch (key)
{
case 'q':
running = false; return 0;
case 'p':
stopped = !stopped; break;
case 'w':
if (player.direction == DOWN) break;
player.direction = UP; break;
case 'd':
if (player.direction == LEFT) break;
player.direction = RIGHT; break;
case 's':
if (player.direction == UP) break;
player.direction = DOWN; break;
case 'a':
if (player.direction == RIGHT) break;
player.direction = LEFT; break;
} key = 0;
if (stopped) continue;
if (playerDoTick(&player, food) && player.score < FIELD_SIZE*FIELD_SIZE - 1)
food = generateFood(player);
head_x = player.head->x;
head_y = player.head->y;
if (head_x >= FIELD_SIZE || head_x < 0 || head_y >= FIELD_SIZE || head_y < 0 || playerCheckSelfCollision(player))
{
running = false;
break;
}
} }
return 0; return 0;
} }

24
src/platform/game.c Normal file
View File

@ -0,0 +1,24 @@
#ifdef _WIN32
void platformGameInit(void) {}
#else
#include <signal.h>
#include <stdlib.h>
#include "platform/getch.h"
void platformGameInit(void)
{
getchInit();
atexit(getchResetTerminalState);
signal(SIGINT, getchResetTerminalStateHandler);
signal(SIGABRT, getchResetTerminalStateHandler);
signal(SIGFPE, getchResetTerminalStateHandler);
signal(SIGILL, getchResetTerminalStateHandler);
signal(SIGSEGV, getchResetTerminalStateHandler);
signal(SIGTERM, getchResetTerminalStateHandler);
}
#endif /* _WIN32 */

43
src/platform/getch.c Normal file
View File

@ -0,0 +1,43 @@
#ifndef _WIN32
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
static struct termios DefaultState = {0};
int getch(void)
{
char buf = 0;
struct termios old = {0};
fflush(stdout);
if (tcgetattr(0, &old) < 0) perror("tcsetattr()");
old.c_lflag &= ~ICANON; // local modes = Non Canonical mode
old.c_lflag &= ~ECHO; // local modes = Disable echo.
old.c_cc[VMIN] = 1; // control chars (MIN value) = 1
old.c_cc[VTIME] = 0; // control chars (TIME value) = 0 (No time)
if (tcsetattr(0, TCSANOW, &old) < 0) perror("tcsetattr ICANON");
if (read(0, &buf, 1) < 0) perror("read()");
old.c_lflag |= ICANON; // local modes = Canonical mode
old.c_lflag |= ECHO; // local modes = Enable echo.
if (tcsetattr(0, TCSADRAIN, &old) < 0) perror ("tcsetattr ~ICANON");
return (int)buf;
}
void getchResetTerminalState(void)
{
tcsetattr(0, TCSANOW, &DefaultState);
}
void getchResetTerminalStateHandler(int sig)
{
getchResetTerminalState();
_Exit(sig);
}
void getchInit(void)
{
tcgetattr(0, &DefaultState);
}
#endif /* !_WIN32 */

View File

@ -2,39 +2,32 @@
#include "player.h" #include "player.h"
Player *playerCreate(Direction direction, int x, int y, int score) void playerCreate(Player *buffer, Direction direction, int x, int y, int score)
{ {
Player *player = (Player *)malloc(sizeof(Player));
PlayerNode *head = (PlayerNode *)malloc(sizeof(PlayerNode)); PlayerNode *head = (PlayerNode *)malloc(sizeof(PlayerNode));
head->x = x; head->x = x;
head->y = y; head->y = y;
head->next = NULL; head->next = NULL;
player->tail = head; buffer->tail = head;
player->head = head; buffer->head = head;
player->score = score; buffer->score = score;
player->direction = direction; buffer->direction = direction;
return player;
} }
void playerFree(Player *player) bool playerCheckFoodCollision(Player player, Food food)
{ {
} for (PlayerNode *node = player.tail; node != NULL; node = node->next)
bool playerCheckFoodCollision(Player *player, Food food)
{
for (PlayerNode *node = player->tail; node != NULL; node = node->next)
if (node->x == food.x && node->y == food.y) if (node->x == food.x && node->y == food.y)
return true; return true;
return false; return false;
} }
bool playerCheckSelfCollision(Player *player) bool playerCheckSelfCollision(Player player)
{ {
PlayerNode *nodei, *nodej; PlayerNode *nodei, *nodej;
for (nodei = player->tail; nodei != NULL; nodei = nodei->next) for (nodei = player.tail; nodei != NULL; nodei = nodei->next)
for (nodej = nodei->next; nodej != NULL; nodej = nodej->next) for (nodej = nodei->next; nodej != NULL; nodej = nodej->next)
if (nodei->x == nodej->x && nodei->y == nodej->y) if (nodei->x == nodej->x && nodei->y == nodej->y)
return true; return true;
@ -45,6 +38,7 @@ bool playerDoTick(Player *player, Food food)
{ {
bool food_collision; bool food_collision;
PlayerNode *new_head = (PlayerNode *)malloc(sizeof(PlayerNode)); PlayerNode *new_head = (PlayerNode *)malloc(sizeof(PlayerNode));
new_head->next = NULL;
int head_x = player->head->x; int head_x = player->head->x;
int head_y = player->head->y; int head_y = player->head->y;

View File

@ -1,36 +1,24 @@
#include "screen.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
Screen *screenCreate(int width, int height, Point fill_value) #include "screen.h"
void screenCreate(Screen *buffer, int width, int height, Point fill_value)
{ {
Point *screen = malloc(width * height * sizeof(Point)); Point *screen = malloc(width * height * sizeof(Point));
memset(screen, fill_value, width * height * sizeof(Point)); memset(screen, fill_value, width * height * sizeof(Point));
Screen *out = malloc(sizeof(Screen)); buffer->width = width;
out->width = width; buffer->height = height;
out->height = height; buffer->screen = screen;
out->screen = screen;
return out;
} }
void screenFree(Screen *screen) void screenShow(Screen screen)
{
free(screen->screen);
free(screen);
}
Point *screenGetPoint(Screen *screen, int x, int y)
{
return screen->screen + x + (y * screen->width);
}
void screenShow(Screen *screen)
{ {
int x, y, i; int x, y, i;
int width = screen->width; int width = screen.width;
int height = screen->height; int height = screen.height;
Point point; Point point;
for (y = 0; y < height; ++y) for (y = 0; y < height; ++y)
@ -43,8 +31,3 @@ void screenShow(Screen *screen)
putchar('\n'); putchar('\n');
} }
} }
void screenSet(Screen *screen, Point fill_value)
{
memset(screen->screen, fill_value, screen->width * screen->height * sizeof(char));
}

14
src/sleep.c Normal file
View File

@ -0,0 +1,14 @@
#include <time.h>
static long long int getMS()
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
void sleepMS(int msec)
{
long long int end = getMS() + msec;
while (getMS() < end);
}

4
templates/config.h.in Normal file
View File

@ -0,0 +1,4 @@
#define FIELD_SIZE ${FIELD_SIZE}
#define DEFX ${DEFX}
#define DEFY ${DEFY}
#define SLEEP ${SLEEP}

4
templates/config.mk.in Normal file
View File

@ -0,0 +1,4 @@
CC = ${CC}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}
OUT = ${OUT}