forked from nakidai/csnake
Compare commits
45 Commits
Author | SHA1 | Date |
---|---|---|
Nakidai | 06f05e7412 | |
Nakidai | cd0649a375 | |
Nakidai | 351c45b1c1 | |
Nakidai | a735950c67 | |
Nakidai | ad871be017 | |
Nakidai | 8c6395592d | |
Nakidai | 9d6264d100 | |
Nakidai | bb55e690d0 | |
Nakidai | 1c7a0ef6b2 | |
Nakidai | 71229a9205 | |
Nakidai | a67704ebff | |
Nakidai Perumenei | eb82ad3173 | |
Nakidai Perumenei | 71cfceee4b | |
Nakidai Perumenei | 22d3cdeb43 | |
Nakidai | cf71b4d57d | |
Nakidai | e2d01db144 | |
Nakidai | bfaf0f0e02 | |
Nakidai | f70e3d949a | |
Nakidai | 1685dc927b | |
Nakidai | 517e57cfc5 | |
Nakidai | eda7badf47 | |
Nakidai | 50c7f9d632 | |
Nakidai | 4b208ab1c4 | |
Nakidai | be37715af7 | |
Nakidai | cf8fe181a2 | |
Nakidai | 49bd6ee329 | |
Nakidai | 9933de02ac | |
Nakidai | 7911484f02 | |
Nakidai | 8acdec21a7 | |
Nakidai | 123760b56a | |
Nakidai | e6995af34a | |
Nakidai | 8e8a1c5abd | |
Nakidai | 9c54a483bd | |
Nakidai | 970fb66db8 | |
Nakidai | 1ff14f7cbc | |
Nakidai | ce1e91d90f | |
Nakidai | 0b74ef8618 | |
Nakidai | 793db5f00a | |
Nakidai | 1d63892d67 | |
Nakidai | 98089c4351 | |
Nakidai | b64ffdb82a | |
Nakidai | c31de323f0 | |
Nakidai | bcf6465f4e | |
nakidai | 460e4e33ed | |
Neirokan | 29e63d64ee |
|
@ -1,2 +1,6 @@
|
||||||
obj/
|
obj/
|
||||||
game
|
build/
|
||||||
|
csnake
|
||||||
|
*.core
|
||||||
|
include/config.h
|
||||||
|
include/config.mk
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(csnake VERSION 1.2 LANGUAGES C)
|
||||||
|
|
||||||
|
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||||
|
message(FATAL_ERROR "In-source builds are not allowed.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(csnake
|
||||||
|
src/main.c
|
||||||
|
src/screen.c
|
||||||
|
src/input.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_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)
|
||||||
|
|
||||||
|
set(FIELD_SIZE 10 CACHE STRING "Size of game field")
|
||||||
|
set(DEFX 0 CACHE STRING "Start x")
|
||||||
|
set(DEFY 0 CACHE STRING "Start y")
|
||||||
|
set(SLEEP 1000 CACHE STRING "Sleep between frames (ms)")
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/templates/config.h.in
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/config.h
|
||||||
|
)
|
|
@ -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.
|
24
Makefile
24
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#define SIZE 10
|
|
||||||
#define DEFX 0
|
|
||||||
#define DEFY 0
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef __PLATFORM_GAME_H__
|
||||||
|
#define __PLATFORM_GAME_H__
|
||||||
|
|
||||||
|
void platformGameInit(void);
|
||||||
|
|
||||||
|
#endif /* __PLATFORM_GAME_H__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef __SLEEP_H__
|
||||||
|
#define __SLEEP_H__
|
||||||
|
|
||||||
|
void sleepMS(int msec);
|
||||||
|
|
||||||
|
#endif /* __SLEEP_H__ */
|
29
src/input.c
29
src/input.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
113
src/main.c
113
src/main.c
|
@ -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){random() % SIZE, random() % 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)
|
||||||
{
|
{
|
||||||
srandom(time(NULL));
|
srand((unsigned int)time(NULL));
|
||||||
Player *player = playerCreate(DOWN, DEFX, DEFY, 0);
|
platformGameInit();
|
||||||
Screen *screen = screenCreate(SIZE, SIZE, ' ');
|
|
||||||
PlayerNode *node;
|
Player player; playerCreate(&player, DOWN, DEFX, DEFY, 0);
|
||||||
thrd_t input_thread;
|
Screen screen; screenCreate(&screen, FIELD_SIZE, FIELD_SIZE, ' ');
|
||||||
int i;
|
|
||||||
int head_x, head_y;
|
|
||||||
Food food = generateFood(player);
|
Food food = generateFood(player);
|
||||||
|
|
||||||
bool *running = malloc(sizeof(bool)); *running = true;
|
int key = 0;
|
||||||
char *key = malloc(sizeof(char)); *key = 0;
|
bool running = true;
|
||||||
InputArgs input_args = (InputArgs){ key, running };
|
bool stopped = false;
|
||||||
|
int head_x, head_y;
|
||||||
|
|
||||||
thrd_create(&input_thread, input, &input_args);
|
threadCreate(input, &(InputArgs){ &key, &running });
|
||||||
while (*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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
|
@ -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 */
|
26
src/player.c
26
src/player.c
|
@ -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;
|
||||||
|
|
||||||
|
|
35
src/screen.c
35
src/screen.c
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#define FIELD_SIZE ${FIELD_SIZE}
|
||||||
|
#define DEFX ${DEFX}
|
||||||
|
#define DEFY ${DEFY}
|
||||||
|
#define SLEEP ${SLEEP}
|
|
@ -0,0 +1,4 @@
|
||||||
|
CC = ${CC}
|
||||||
|
CFLAGS = ${CFLAGS}
|
||||||
|
LDFLAGS = ${LDFLAGS}
|
||||||
|
OUT = ${OUT}
|
Loading…
Reference in New Issue