Initial commit.

This commit is contained in:
yzrh 2020-10-10 17:16:14 +00:00
commit b9b74e8802
17 changed files with 3156 additions and 0 deletions

83
CHANGE.md Normal file
View file

@ -0,0 +1,83 @@
0.6.2_3 (2020-10-10)
====================
* Update contrib/IBM-Plex-Sans to 5.1.3
0.6.2_2 (2019-10-31)
====================
* Make sound support optional
0.6.2_1 (2019-10-30)
====================
* Make GPIO support optional
0.6.2 (2019-03-27)
==================
* Fix GPIO interrupt
0.6.1 (2019-03-25)
==================
* Fix GPIO mapping
0.6.0 (2019-03-18)
==================
* Change timer resolution to nanosecond
* Add colour selection to renderer
* Add GPIO support
0.5.1 (2019-03-12)
==================
* Add a pause at the start of flappy mode
0.5.0 (2019-03-09)
==================
* Change keyboard key map
* Change maximum speed in classic mode to a lower value
* Change snake initial position to be centred
* Add flappy bird like game mode
0.4.1 (2019-03-06)
==================
* Fix swipe threshold
0.4.0 (2019-03-04)
==================
* Change game loop to be threaded
* Change menu to be event driven
* Change font size
* Add font texture caching
* Add option to toggle music
* Add condition for clearing game
* Add touchscreen support
0.3.1 (2019-02-22)
==================
* Fix pause logic
0.3.0 (2019-02-22)
==================
* Change memory allocation method
* Change difficult scaling
* Add pause function
0.2.0 (2019-02-21)
==================
* Add multiplayer mode
* Add key press driven menu
0.1.0 (2019-02-20)
==================
* Initial release

202
COPYING Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

42
README.md Normal file
View file

@ -0,0 +1,42 @@
Snake
=====
Dependency
----------
1. sdl2 with OpenGL support
2. sdl2\_ttf
3. sdl2\_mixer with FLAC support (optional)
Build
-----
Download contrib resources and extract
to contrib/
`cd src`
`make`
With GPIO support:
`make -DGPIO`
With SOUND support:
`make -DSOUND`
Run
---
`./snake`
Installation
------------
`make install`
To-do
-----
* Leaderboard (SQLite)

50
src/Makefile Normal file
View file

@ -0,0 +1,50 @@
# Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
#
# SPDX-License-Identifier: Apache-2.0
src = main.c collision.c game.c input.c renderer.c
inc = extern.h game.h input.h renderer.h screen.h version.h
.ifdef GPIO
src += gpio.c
inc += gpio.h
.endif
obj = ${src:.c=.o}
PREFIX = /usr/local
CFLAGS = -O3 -march=native -pipe -flto=thin -Wall -Wno-unused-result
LDFLAGS = -Wl,-O3 -lpthread -lSDL2 -lSDL2_ttf
.ifdef SOUND
LDFLAGS += -lSDL2_mixer
.endif
LDFLAGS += -Wl,--as-needed
CFLAGS += -I/usr/local/include
LDFLAGS += -L/usr/local/lib
all: ${obj} ${inc}
${CC} ${LDFLAGS} -o snake ${obj}
clean:
rm -f snake ${obj}
install:
install -d ${PREFIX}/bin
install -d ${PREFIX}/share/snake
install snake ${PREFIX}/bin
install -m644 ../contrib/font.ttf ${PREFIX}/share/snake
install -m644 ../contrib/music.flac ${PREFIX}/share/snake
deinstall:
rm -f ${PREFIX}/bin/snake
rm -rf ${PREFIX}/share/snake
.PHONY: all clean install deinstall

42
src/collision.c Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <SDL2/SDL.h>
#include "screen.h"
#include "renderer.h"
bool
collision(int *a, int *b)
{
if (a[0] < b[0] + b[2] &&
a[0] + a[2] > b[0] &&
a[1] < b[1] + b[3] &&
a[1] + a[3] > b[1])
return 1;
return 0;
}
bool
collision_block(int *a, int *b)
{
return collision(
(int[]) {a[0], a[1], SCREEN_UNIT, SCREEN_UNIT},
(int[]) {b[0], b[1], SCREEN_UNIT, SCREEN_UNIT});
}
bool
collision_bound(float x, float y, Snake_Text *message)
{
if (x > message->pos.x &&
x < message->pos.x + message->pos.w &&
y > message->pos.y &&
y < message->pos.y + message->pos.h)
return 1;
return 0;
}

54
src/extern.h Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "renderer.h"
#include "game.h"
#include "input.h"
bool collision(int *a, int *b);
bool collision_block(int *a, int *b);
bool collision_bound(float x, float y, Snake_Text *message);
void draw_clear(SDL_Renderer *renderer);
void draw_colour(SDL_Renderer *renderer, Snake_Colour colour);
void draw_text(SDL_Renderer *renderer, TTF_Font *font,
int text_x, int text_y, char *text,
bool centre, bool select, SDL_Color fg, SDL_Color bg,
Snake_Text *message);
void draw_wall(SDL_Renderer *renderer);
void draw_snake(SDL_Renderer *renderer, Snake_Pos **snake);
void draw_fruit(SDL_Renderer *renderer, int *fruit);
void draw_pipe(SDL_Renderer *renderer, Snake_Pos **pipe);
void game_snake_init(Snake_Pos **snake, int dx);
void game_snake_move(Snake_Pos **snake0, Snake_Pos **snake1,
Snake_Input *head, bool lawi, int *legal);
void game_snake_grow(Snake_Pos **snake);
void game_fruit(int *fruit, Snake_Pos **snake);
void game_snake_fall(Snake_Pos **snake, bool norm, int speed);
void game_pipe_init(Snake_Pos **pipe);
void game_pipe_move(Snake_Pos **pipe);
void game_pipe_add(Snake_Pos **pipe);
void game_pipe_del(Snake_Pos **pipe);
void game_pipe_pos(Snake_Pos **pipe, int *state);
void game_pipe_bound(Snake_Pos **pipe, int *pos);
void input_keyboard(SDL_Keycode sym, Snake_Input *code);
void input_controller(uint8_t button, Snake_Input *code);
void input_gpio(int gpio, uint8_t *button);
#ifdef GPIO
int gpio_export(int pin);
int gpio_unexport(int pin);
int gpio_config(int pin, int direction, int interrupt);
int gpio_write(int pin, int value);
int gpio_read(int pin);
#endif /* GPIO */

317
src/game.c Normal file
View file

@ -0,0 +1,317 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "extern.h"
#include "screen.h"
static bool
_collision(int *fruit, Snake_Pos **snake)
{
Snake_Pos *ptr = *snake;
while (ptr != NULL) {
if (collision_block(fruit, (int[]) {ptr->x, ptr->y}))
return 1;
ptr = ptr->next;
}
return 0;
}
static bool
_bound(Snake_Pos **snake)
{
if ((*snake)->x + SCREEN_UNIT / 2 > SCREEN_HEIGHT)
return 1;
else if ((*snake)->y + SCREEN_UNIT / 2 > SCREEN_HEIGHT)
return 1;
else if ((*snake)->x - SCREEN_UNIT / 2 < -SCREEN_UNIT)
return 1;
else if ((*snake)->y - SCREEN_UNIT / 2 < -SCREEN_UNIT)
return 1;
return 0;
}
static int
_overlap(Snake_Pos **snake0, Snake_Pos **snake1)
{
Snake_Pos *ptr0 = *snake0;
Snake_Pos *ptr1;
int is_overlap[2] = {0, 0};
while (ptr0->next->next != NULL) {
if (collision_block(
(int[]) {(*snake0)->x, (*snake0)->y},
(int[]) {ptr0->next->x, ptr0->next->y})) {
is_overlap[0] = 1;
break;
}
ptr0 = ptr0->next;
}
if (snake1 != NULL) {
ptr0 = *snake0;
ptr1 = *snake1;
while (ptr0->next->next != NULL &&
ptr1->next->next != NULL &&
(is_overlap[0] != 1 || is_overlap[1] != 2)) {
if (collision_block(
(int[]) {(*snake0)->x, (*snake0)->y},
(int[]) {ptr1->next->x, ptr1->next->y}))
is_overlap[0] = 1;
if (collision_block(
(int[]) {(*snake1)->x, (*snake1)->y},
(int[]) {ptr0->next->x, ptr0->next->y}))
is_overlap[1] = 2;
if (collision_block(
(int[]) {(*snake1)->x, (*snake1)->y},
(int[]) {ptr1->next->x, ptr1->next->y}))
is_overlap[1] = 2;
ptr0 = ptr0->next;
ptr1 = ptr1->next;
}
}
return is_overlap[0] + is_overlap[1];
}
void
game_snake_init(Snake_Pos **snake, int dx)
{
Snake_Pos *ptr;
while ((ptr = *snake) != NULL) {
*snake = (*snake)->next;
free(ptr);
}
*snake = malloc(sizeof(Snake_Pos));
(*snake)->next = malloc(sizeof(Snake_Pos));
(*snake)->x = (*snake)->next->x = SCREEN_HEIGHT / 2 + dx;
(*snake)->y = SCREEN_HEIGHT / 2;
(*snake)->next->y = SCREEN_HEIGHT / 2 + SCREEN_UNIT;
(*snake)->next->next = NULL;
}
void
game_snake_move(Snake_Pos **snake0, Snake_Pos **snake1,
Snake_Input *head, bool law, int *legal)
{
bool bound_snake0;
bool bound_snake1;
int overlap;
Snake_Pos *ptr0;
Snake_Pos *ptr1;
ptr0 = malloc(sizeof(Snake_Pos));
ptr0->x = (*snake0)->x;
ptr0->y = (*snake0)->y;
ptr0->next = *snake0;
switch (head[0]) {
case SNAKE_LEFT:
ptr0->x -= SCREEN_UNIT;
break;
case SNAKE_UP:
ptr0->y -= SCREEN_UNIT;
break;
case SNAKE_DOWN:
ptr0->y += SCREEN_UNIT;
break;
case SNAKE_RIGHT:
ptr0->x += SCREEN_UNIT;
break;
default:
break;
}
bound_snake0 = _bound(&ptr0);
if (snake1 == NULL) {
overlap = _overlap(&ptr0, NULL);
if (bound_snake0 || overlap == 1)
*legal = 1;
else
*legal = 0;
} else {
ptr1 = malloc(sizeof(Snake_Pos));
ptr1->x = (*snake1)->x;
ptr1->y = (*snake1)->y;
ptr1->next = *snake1;
switch (head[1]) {
case SNAKE_LEFT:
ptr1->x -= SCREEN_UNIT;
break;
case SNAKE_UP:
ptr1->y -= SCREEN_UNIT;
break;
case SNAKE_DOWN:
ptr1->y += SCREEN_UNIT;
break;
case SNAKE_RIGHT:
ptr1->x += SCREEN_UNIT;
break;
default:
break;
}
bound_snake1 = _bound(&ptr1);
overlap = _overlap(&ptr0, &ptr1);
if ((bound_snake0 && bound_snake1) || overlap == 3)
*legal = 3;
else if (bound_snake1 || overlap == 2)
*legal = 2;
else if (bound_snake0 || overlap == 1)
*legal = 1;
else
*legal = 0;
}
if (*legal == 0 || !law) {
*snake0 = ptr0;
while (ptr0->next->next != NULL)
ptr0 = ptr0->next;
free(ptr0->next);
ptr0->next = NULL;
if (snake1 != NULL) {
*snake1 = ptr1;
while (ptr1->next->next != NULL)
ptr1 = ptr1->next;
free(ptr1->next);
ptr1->next = NULL;
}
} else {
free(ptr0);
if (snake1 != NULL)
free(ptr1);
}
}
void
game_snake_grow(Snake_Pos **snake)
{
Snake_Pos *ptr = *snake;
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = malloc(sizeof(Snake_Pos));
ptr->next->x = ptr->x;
ptr->next->y = ptr->y;
ptr->next->next = NULL;
}
void
game_fruit(int *fruit, Snake_Pos **snake)
{
do {
fruit[0] = rand() % SCREEN_SIDE * SCREEN_UNIT;
fruit[1] = rand() % SCREEN_SIDE * SCREEN_UNIT;
} while (_collision(fruit, snake));
}
void
game_snake_fall(Snake_Pos **snake, bool norm, int speed)
{
Snake_Pos *ptr = *snake;
while(ptr != NULL) {
ptr->y += norm ? speed : -speed;
ptr = ptr->next;
}
}
void
game_pipe_init(Snake_Pos **pipe)
{
Snake_Pos *ptr;
while ((ptr = *pipe) != NULL) {
*pipe = (*pipe)->next;
free(ptr);
}
*pipe = malloc(sizeof(Snake_Pos));
(*pipe)->x = SCREEN_HEIGHT;
(*pipe)->y = (rand() % (SCREEN_SIDE - PIPE_GAP) + PIPE_GAP) *
SCREEN_UNIT;
(*pipe)->next = NULL;
}
void
game_pipe_move(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
while(ptr != NULL) {
ptr->x -= 1;
ptr = ptr->next;
}
}
void
game_pipe_add(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
while(ptr->next != NULL)
ptr = ptr->next;
ptr->next = malloc(sizeof(Snake_Pos));
ptr->next->x = SCREEN_HEIGHT;
ptr->next->y = (rand() % (SCREEN_SIDE - PIPE_GAP) + PIPE_GAP) *
SCREEN_UNIT;
ptr->next->next = NULL;
}
void
game_pipe_del(Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
*pipe = (*pipe)->next;
free(ptr);
}
void
game_pipe_pos(Snake_Pos **pipe, int *state)
{
Snake_Pos *ptr = *pipe;
*state = 0;
*state += ptr->x + SCREEN_UNIT < 0 ? 1 : 0;
while(ptr->next != NULL)
ptr = ptr->next;
*state += ptr->x < SCREEN_HEIGHT - PIPE_DISTANCE * SCREEN_UNIT ? 2 : 0;
}
/*
* This function returns the dimension of pipes
* that snake is currently passing through,
* so that we can avoid checking all pipes for
* collision
*
* Array elements: x, w, y0, h0, y1, h1
*/
void
game_pipe_bound(Snake_Pos **pipe, int *pos)
{
Snake_Pos *ptr = *pipe;
while(ptr != NULL) {
if (ptr->x < SCREEN_HEIGHT / 2 &&
ptr->x + SCREEN_UNIT > SCREEN_HEIGHT / 2) {
pos[0] = ptr->x;
pos[1] = SCREEN_UNIT;
pos[2] = 0;
pos[3] = ptr->y - PIPE_GAP / 2 * SCREEN_UNIT;
pos[4] = ptr->y + PIPE_GAP / 2 * SCREEN_UNIT;
pos[5] = SCREEN_HEIGHT - ptr->y - SCREEN_UNIT;
return;
}
ptr = ptr->next;
}
pos[0] = 0;
}

18
src/game.h Normal file
View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define TICK_TIME_INIT 250000000
#define TICK_TIME_DEC 10000000
#define TICK_TIME_MIN 100000000
#define PIPE_GAP 4
#define PIPE_DISTANCE 10
typedef struct _Snake_Pos {
int x;
int y;
struct _Snake_Pos *next;
} Snake_Pos;

194
src/gpio.c Normal file
View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "gpio.h"
#ifdef __FreeBSD__
int
gpio_config(int pin, int direction, int interrupt)
{
int ret;
int fd = open("/dev/gpioc0", O_RDWR);
if (fd == -1) {
fprintf(stderr, "GPIO configuration failure\n");
return 1;
}
struct gpio_pin pin_req = {
.gp_pin = pin,
.gp_caps = interrupt,
.gp_flags = direction
};
ret = ioctl(fd, GPIOSETCONFIG, &pin_req) == 0 ? 0 : 1;
close(fd);
return ret;
}
int
gpio_write(int pin, int value)
{
int ret;
int fd = open("/dev/gpioc0", O_RDWR);
if (fd == -1) {
fprintf(stderr, "GPIO output failure\n");
return 1;
}
struct gpio_req req = {
.gp_pin = pin,
.gp_value = value
};
ret = ioctl(fd, GPIOSET, &req) == 0 ? 0 : 1;
close(fd);
return ret;
}
int
gpio_read(int pin)
{
int fd = open("/dev/gpioc0", O_RDWR);
if (fd == -1) {
fprintf(stderr, "GPIO input failure\n");
return 1;
}
struct gpio_req req = {
.gp_pin = pin
};
ioctl(fd, GPIOGET, &req);
close(fd);
return req.gp_value;
}
#elif __linux__
int
gpio_export(int pin)
{
int export = open("/sys/class/gpio/export", O_RDWR);
if (export == -1) {
fprintf(stderr, "GPIO export failure\n");
return 1;
}
char buf[3];
int buf_size = snprintf(buf, 3, "%d", pin);
write(export, buf, buf_size);
close(export);
return 0;
}
int
gpio_unexport(int pin)
{
int unexport = open("/sys/class/gpio/unexport", O_RDWR);
if (unexport == -1) {
fprintf(stderr, "GPIO unexport failure\n");
return 1;
}
char buf[3];
int buf_size = snprintf(buf, 3, "%d", pin);
write(unexport, buf, buf_size);
close(unexport);
return 0;
}
int
gpio_config(int pin, int direction, int interrupt)
{
char path[33];
snprintf(path, 33, "/sys/class/gpio/gpio%d/direction", pin);
int config = open(path, O_RDWR);
if (config == -1) {
fprintf(stderr, "GPIO configuration failure\n");
return 1;
}
char buf[8];
int buf_size = snprintf(buf, 8, "%s", direction == 0 ? "in" : "out");
write(config, buf, buf_size);
close(config);
snprintf(path, 33, "/sys/class/gpio/gpio%d/edge", pin);
config = open(path, O_RDWR);
if (config == -1) {
fprintf(stderr, "GPIO configuration failure\n");
return 1;
}
buf_size = snprintf(buf, 8, "%s",
interrupt == 0 ? "falling" : "rising");
write(config, buf, buf_size);
close(config);
return 0;
}
int
gpio_write(int pin, int value)
{
char path[29];
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", pin);
int config = open(path, O_RDWR);
if (config == -1) {
fprintf(stderr, "GPIO output failure\n");
return 1;
}
char buf[2];
int buf_size = snprintf(buf, 2, "%d", value);
write(config, buf, buf_size);
close(config);
return 0;
}
int
gpio_read(int pin)
{
char path[29];
snprintf(path, 29, "/sys/class/gpio/gpio%d/value", pin);
int config = open(path, O_RDONLY);
if (config == -1) {
fprintf(stderr, "GPIO input failure\n");
return 1;
}
char buf[2];
read(config, buf, 2);
close(config);
return atoi(buf);
}
#endif /* __FreeBSD__ */

66
src/gpio.h Normal file
View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define GPIO_LOW 0x00
#define GPIO_HIGH 0x01
#ifdef __FreeBSD__
#include <stdint.h>
#include <sys/gpio.h>
#define GPIO_IN GPIO_PIN_INPUT
#define GPIO_OUT GPIO_PIN_OUTPUT
#define GPIO_INT GPIO_INTR_EDGE_FALLING
#elif __linux__
#define GPIO_IN 0x00
#define GPIO_OUT 0x01
/*
* 0: falling
* 1: rising
*/
#define GPIO_INT 0
#endif /* __FreeBSD__ */
/* Raspberry Pi 3 */
#define GPIO_PIN_IN0 4 /* 7: DPAD_LEFT */
#define GPIO_PIN_IN1 14 /* 8: DPAD_UP */
#define GPIO_PIN_IN2 15 /* 10: DPAD_DOWN */
#define GPIO_PIN_IN3 17 /* 11: DPAD_RIGHT */
#define GPIO_PIN_IN4 18 /* 12: BACK */
#define GPIO_PIN_IN5 27 /* 13: GUIDE */
#define GPIO_PIN_IN6 22 /* 15: START */
#define GPIO_PIN_IN7 23 /* 16: A */
#define GPIO_PIN_IN8 24 /* 18: B */
#define GPIO_PIN_IN9 10 /* 19: X */
#define GPIO_PIN_IN10 9 /* 21: Y */
#define GPIO_PIN_IN11 25 /* 22: LEFTSHOULDER */
#define GPIO_PIN_IN12 11 /* 23: RIGHTSHOULDER */
#define GPIO_PIN_IN13 8 /* 24: LEFTSTICK */
#define GPIO_PIN_IN14 7 /* 26: RIGHTSTICK */
/* Second controller */
#define GPIO_PIN_IN15 0 /* 27: DPAD_LEFT */
#define GPIO_PIN_IN16 1 /* 28: DPAD_UP */
#define GPIO_PIN_IN17 5 /* 29: DPAD_DOWN */
#define GPIO_PIN_IN18 6 /* 31: DPAD_RIGHT */
/* Unused */
#define GPIO_PIN_IN19 12 /* 32: NULL */
#define GPIO_PIN_IN20 13 /* 33: NULL */
#define GPIO_PIN_IN21 19 /* 35: NULL */
#define GPIO_PIN_IN22 16 /* 36: NULL */
#define GPIO_PIN_IN23 26 /* 37: NULL */
#define GPIO_PIN_IN24 20 /* 38: NULL */
#define GPIO_PIN_IN25 21 /* 40: NULL */

186
src/input.c Normal file
View file

@ -0,0 +1,186 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <SDL2/SDL.h>
#include "input.h"
void
input_keyboard(SDL_Keycode sym, Snake_Input *code)
{
switch (sym) {
case SDLK_LEFT:
*code = SNAKE_LEFT;
break;
case SDLK_UP:
*code = SNAKE_UP;
break;
case SDLK_DOWN:
*code = SNAKE_DOWN;
break;
case SDLK_RIGHT:
*code = SNAKE_RIGHT;
break;
case SDLK_SPACE:
*code = SNAKE_JUMP;
break;
case SDLK_RETURN:
*code = SNAKE_SELECT;
break;
case SDLK_ESCAPE:
*code = SNAKE_PAUSE;
break;
case SDLK_r:
*code = SNAKE_RESET;
break;
case SDLK_g:
*code = SNAKE_GOD;
break;
case SDLK_h:
*code = SNAKE_GROW;
break;
case SDLK_t:
*code = SNAKE_TELEPORT;
break;
case SDLK_m:
*code = SNAKE_MUSIC;
break;
case SDLK_COMMA:
*code = SNAKE_TICK_DECREASE;
break;
case SDLK_PERIOD:
*code = SNAKE_TICK_INCREASE;
break;
case SDLK_s:
*code = SNAKE_LEFT;
break;
case SDLK_e:
*code = SNAKE_UP;
break;
case SDLK_d:
*code = SNAKE_DOWN;
break;
case SDLK_f:
*code = SNAKE_RIGHT;
break;
default:
*code = SNAKE_EMPTY;
break;
}
}
void
input_controller(uint8_t button, Snake_Input *code)
{
switch (button) {
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
*code = SNAKE_LEFT;
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
*code = SNAKE_UP;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
*code = SNAKE_DOWN;
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
*code = SNAKE_RIGHT;
break;
case SDL_CONTROLLER_BUTTON_START:
*code = SNAKE_SELECT;
break;
case SDL_CONTROLLER_BUTTON_BACK:
*code = SNAKE_PAUSE;
break;
case SDL_CONTROLLER_BUTTON_GUIDE:
*code = SNAKE_GOD;
break;
case SDL_CONTROLLER_BUTTON_A:
*code = SNAKE_JUMP;
break;
case SDL_CONTROLLER_BUTTON_B:
break;
case SDL_CONTROLLER_BUTTON_X:
*code = SNAKE_RESET;
break;
case SDL_CONTROLLER_BUTTON_Y:
*code = SNAKE_MUSIC;
break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
*code = SNAKE_TELEPORT;
break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
*code = SNAKE_GROW;
break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
*code = SNAKE_TICK_DECREASE;
break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
*code = SNAKE_TICK_INCREASE;
break;
default:
*code = SNAKE_EMPTY;
break;
}
}
#ifdef GPIO
void
input_gpio(int gpio, uint8_t *button)
{
switch (gpio) {
case 0:
*button = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
break;
case 1:
*button = SDL_CONTROLLER_BUTTON_DPAD_UP;
break;
case 2:
*button = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
break;
case 3:
*button = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
break;
case 4:
*button = SDL_CONTROLLER_BUTTON_BACK;
break;
case 5:
*button = SDL_CONTROLLER_BUTTON_GUIDE;
break;
case 6:
*button = SDL_CONTROLLER_BUTTON_START;
break;
case 7:
*button = SDL_CONTROLLER_BUTTON_A;
break;
case 8:
*button = SDL_CONTROLLER_BUTTON_B;
break;
case 9:
*button = SDL_CONTROLLER_BUTTON_X;
break;
case 10:
*button = SDL_CONTROLLER_BUTTON_Y;
break;
case 11:
*button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
break;
case 12:
*button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
break;
case 13:
*button = SDL_CONTROLLER_BUTTON_LEFTSTICK;
break;
case 14:
*button = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
break;
default:
*button = SDL_CONTROLLER_BUTTON_INVALID;
break;
}
}
#endif /* GPIO */

29
src/input.h Normal file
View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
typedef enum {
SNAKE_EMPTY,
SNAKE_LEFT,
SNAKE_UP,
SNAKE_DOWN,
SNAKE_RIGHT,
SNAKE_JUMP,
SNAKE_SELECT,
SNAKE_PAUSE,
SNAKE_RESET,
SNAKE_GOD,
SNAKE_GROW,
SNAKE_TELEPORT,
SNAKE_MUSIC,
SNAKE_TICK_DECREASE,
SNAKE_TICK_INCREASE
} Snake_Input;
typedef enum {
SNAKE_KEYBOARD,
SNAKE_TOUCHSCREEN,
SNAKE_CONTROLLER
} Snake_Device;

1542
src/main.c Normal file

File diff suppressed because it is too large Load diff

244
src/renderer.c Normal file
View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "renderer.h"
#include "screen.h"
#include "game.h"
void
draw_clear(SDL_Renderer *renderer)
{
SDL_SetRenderDrawColor(renderer,
COLOUR_BACKGROUND_R,
COLOUR_BACKGROUND_G,
COLOUR_BACKGROUND_B,
255);
SDL_RenderClear(renderer);
}
void
draw_colour(SDL_Renderer *renderer, Snake_Colour colour)
{
int r;
int g;
int b;
switch (colour) {
case SNAKE_FOREGROUND:
r = COLOUR_FOREGROUND_R;
g = COLOUR_FOREGROUND_G;
b = COLOUR_FOREGROUND_B;
break;
case SNAKE_BACKGROUND:
r = COLOUR_BACKGROUND_R;
g = COLOUR_BACKGROUND_G;
b = COLOUR_BACKGROUND_B;
break;
case SNAKE_BACKGROUND_SHADE:
r = COLOUR_BACKGROUND_SHADE_R;
g = COLOUR_BACKGROUND_SHADE_G;
b = COLOUR_BACKGROUND_SHADE_B;
break;
case SNAKE_RED:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_GREEN:
r = COLOUR_GREEN_R;
g = COLOUR_GREEN_G;
b = COLOUR_GREEN_B;
break;
case SNAKE_BLUE:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_YELLOW:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_MAGENTA:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_CYAN:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_WHITE:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
case SNAKE_BLACK:
r = COLOUR_RED_R;
g = COLOUR_RED_G;
b = COLOUR_RED_B;
break;
default:
break;
}
SDL_SetRenderDrawColor(renderer,
r,
g,
b,
255);
}
void
draw_text(SDL_Renderer *renderer, TTF_Font *font,
int text_x, int text_y, char *text,
bool centre, bool select, SDL_Color fg, SDL_Color bg,
Snake_Text *message)
{
if (!message->cache) {
if (message->texture != NULL)
SDL_DestroyTexture(message->texture);
SDL_Surface *surface =
TTF_RenderUTF8_Shaded(font, text, fg, bg);
message->texture =
SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
message->pos.x = text_x;
message->pos.y = text_y;
TTF_SizeText(font, text, &message->pos.w, &message->pos.h);
if (centre)
message->pos.x -= message->pos.w / 2;
message->pos.y -= message->pos.h;
message->cache = 1;
}
SDL_RenderCopy(renderer, message->texture, NULL, &message->pos);
if (select) {
SDL_SetRenderDrawColor(renderer,
COLOUR_FOREGROUND_R,
COLOUR_FOREGROUND_G,
COLOUR_FOREGROUND_B,
255);
SDL_RenderDrawLine(renderer,
message->pos.x, message->pos.y,
message->pos.x, message->pos.y + message->pos.h);
SDL_RenderDrawLine(renderer,
message->pos.x, message->pos.y,
message->pos.x + message->pos.w, message->pos.y);
SDL_RenderDrawLine(renderer,
message->pos.x, message->pos.y + message->pos.h,
message->pos.x + message->pos.w,
message->pos.y + message->pos.h);
SDL_RenderDrawLine(renderer,
message->pos.x + message->pos.w, message->pos.y,
message->pos.x + message->pos.w,
message->pos.y + message->pos.h);
}
}
void
draw_wall(SDL_Renderer *renderer)
{
SDL_RenderDrawLine(renderer,
0, 0,
0, SCREEN_HEIGHT);
SDL_RenderDrawLine(renderer,
0, 0,
SCREEN_HEIGHT, 0);
SDL_RenderDrawLine(renderer,
0, SCREEN_HEIGHT - 1,
SCREEN_HEIGHT, SCREEN_HEIGHT - 1);
SDL_RenderDrawLine(renderer,
SCREEN_HEIGHT, 0,
SCREEN_HEIGHT, SCREEN_HEIGHT);
}
void
draw_snake(SDL_Renderer *renderer, Snake_Pos **snake)
{
Snake_Pos *ptr = *snake;
SDL_Rect pos;
while (ptr != NULL) {
pos.x = ptr->x;
pos.y = ptr->y;
pos.w = SCREEN_UNIT;
pos.h = SCREEN_UNIT;
SDL_RenderFillRect(renderer, &pos);
ptr = ptr->next;
}
}
void
draw_fruit(SDL_Renderer *renderer, int *fruit)
{
int rx = fruit[0] + SCREEN_UNIT / 2;
int ry = fruit[1] + SCREEN_UNIT / 2;
/* Midpoint circle algorithm */
int x = SCREEN_UNIT / 2;
int y = 0;
int dx = 1;
int dy = 1;
int err = dx - ((SCREEN_UNIT / 2) << 1);
while (x >= y) {
SDL_RenderDrawLine(renderer,
rx - y, ry + x,
rx + y, ry + x);
SDL_RenderDrawLine(renderer,
rx - x, ry + y,
rx + x, ry + y);
SDL_RenderDrawLine(renderer,
rx - x, ry - y,
rx + x, ry - y);
SDL_RenderDrawLine(renderer,
rx - y, ry - x,
rx + y, ry - x);
if (err <= 0) {
y++;
err += dy;
dy += 2;
}
if (err > 0) {
x--;
dx += 2;
err += dx - ((SCREEN_UNIT / 2) << 1);
}
}
}
void
draw_pipe(SDL_Renderer *renderer, Snake_Pos **pipe)
{
Snake_Pos *ptr = *pipe;
SDL_Rect pos;
while (ptr != NULL) {
pos.x = ptr->x;
pos.y = 0;
pos.w = SCREEN_UNIT;
if (ptr->x + SCREEN_UNIT > SCREEN_HEIGHT)
pos.w -= ptr->x + SCREEN_UNIT - SCREEN_HEIGHT;
pos.h = ptr->y - PIPE_GAP / 2 * SCREEN_UNIT;
SDL_RenderFillRect(renderer, &pos);
pos.y = ptr->y + PIPE_GAP / 2 * SCREEN_UNIT;
pos.h = SCREEN_HEIGHT - ptr->y - SCREEN_UNIT;
SDL_RenderFillRect(renderer, &pos);
ptr = ptr->next;
}
}

25
src/renderer.h Normal file
View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
typedef struct {
SDL_Texture *texture;
SDL_Rect pos;
bool cache;
} Snake_Text;
typedef enum {
SNAKE_FOREGROUND,
SNAKE_BACKGROUND,
SNAKE_BACKGROUND_SHADE,
SNAKE_RED,
SNAKE_GREEN,
SNAKE_BLUE,
SNAKE_YELLOW,
SNAKE_MAGENTA,
SNAKE_CYAN,
SNAKE_WHITE,
SNAKE_BLACK
} Snake_Colour;

52
src/screen.h Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 480
#define SCREEN_UNIT 20
#define COLOUR_FOREGROUND_R 51
#define COLOUR_FOREGROUND_G 255
#define COLOUR_FOREGROUND_B 51
#define COLOUR_BACKGROUND_R 40
#define COLOUR_BACKGROUND_G 40
#define COLOUR_BACKGROUND_B 40
#define COLOUR_BACKGROUND_SHADE_R 80
#define COLOUR_BACKGROUND_SHADE_G 80
#define COLOUR_BACKGROUND_SHADE_B 80
#define COLOUR_RED_R 255
#define COLOUR_RED_G 0
#define COLOUR_RED_B 0
#define COLOUR_GREEN_R 0
#define COLOUR_GREEN_G 255
#define COLOUR_GREEN_B 0
#define COLOUR_BLUE_R 0
#define COLOUR_BLUE_G 0
#define COLOUR_BLUE_B 255
#define COLOUR_YELLOW_R 255
#define COLOUR_YELLOW_G 255
#define COLOUR_YELLOW_B 0
#define COLOUR_MAGENTA_R 255
#define COLOUR_MAGENTA_G 0
#define COLOUR_MAGENTA_B 255
#define COLOUR_CYAN_R 0
#define COLOUR_CYAN_G 255
#define COLOUR_CYAN_B 255
#define COLOUR_WHITE_R 255
#define COLOUR_WHITE_G 255
#define COLOUR_WHITE_B 255
#define COLOUR_BLACK_R 0
#define COLOUR_BLACK_G 0
#define COLOUR_BLACK_B 0
#define SCREEN_UNIT_X ((SCREEN_WIDTH - SCREEN_HEIGHT) / 8)
#define SCREEN_UNIT_Y (SCREEN_HEIGHT / 8)
#define SCREEN_SIDE (SCREEN_HEIGHT / SCREEN_UNIT - 1)
#define SCREEN_BLOCK (SCREEN_SIDE * SCREEN_SIDE)

10
src/version.h Normal file
View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define VERSION "0"
#define RELEASE "6"
#define PATCH "2"
#define EXTRA "_3"