diff --git a/games/snake/Kconfig b/games/snake/Kconfig new file mode 100644 index 000000000..35be60035 --- /dev/null +++ b/games/snake/Kconfig @@ -0,0 +1,90 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config GAMES_SNAKE + bool "Snake Game" + default n + ---help--- + Enable Snake game. + +if GAMES_SNAKE + +config GAMES_SNAKE_PROGNAME + string "Program name" + default "snake" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config GAMES_SNAKE_PRIORITY + int "Snake Game task priority" + default 100 + +config GAMES_SNAKE_STACKSIZE + int "Snake Game stack size" + default DEFAULT_TASK_STACKSIZE + +config DEBUG_SNAKE_GAME + bool "Print board status to the serial console for debugging" + default n + +config GAMES_SNAKE_LED_MATRIX_PATH + string "LED matrix path" + default "/dev/leds0" + ---help--- + Path of the led matrix + +config GAMES_SNAKE_LED_MATRIX_ROWS + int "LED Matrix row count" + default 8 + +config GAMES_SNAKE_LED_MATRIX_COLS + int "LED Matrix column count" + default 8 + +# +# Input Device Selection +# + +choice INPUT_METHOD + prompt "Input Device (Serial Console, GPIO, etc)" + default GAMES_SNAKE_USE_CONSOLEKEY + +config GAMES_SNAKE_USE_CONSOLEKEY + bool "Serial Console as Input" + +config GAMES_SNAKE_USE_GPIO + bool "GPIO pins as Input" +endchoice + +if GAMES_SNAKE_USE_GPIO + +config GAMES_SNAKE_UP_KEY_PATH + string "Up key path" + default "/dev/gpio0" + ---help--- + Path of the up key to read + +config GAMES_SNAKE_DOWN_KEY_PATH + string "Down key path" + default "/dev/gpio1" + ---help--- + Path of the down key to read + +config GAMES_SNAKE_LEFT_KEY_PATH + string "Left key path" + default "/dev/gpio2" + ---help--- + Path of the left key to read + +config GAMES_SNAKE_RIGHT_KEY_PATH + string "Right key path" + default "/dev/gpio3" + ---help--- + Path of the right key to read + +endif #GAMES_SNAKE_USE_GPIO + +endif diff --git a/games/snake/Make.defs b/games/snake/Make.defs new file mode 100644 index 000000000..8656d46ca --- /dev/null +++ b/games/snake/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/games/snake/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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. +# +############################################################################ + +ifneq ($(CONFIG_GAMES_SNAKE),) +CONFIGURED_APPS += $(APPDIR)/games/snake +endif diff --git a/games/snake/Makefile b/games/snake/Makefile new file mode 100644 index 000000000..fcdfd15d3 --- /dev/null +++ b/games/snake/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/games/snake/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Snake game info + +PROGNAME = $(CONFIG_GAMES_SNAKE_PROGNAME) +PRIORITY = $(CONFIG_GAMES_SNAKE_PRIORITY) +STACKSIZE = $(CONFIG_GAMES_SNAKE_STACKSIZE) +MODULE = $(CONFIG_GAMES_SNAKE) + +# Snake game application + +MAINSRC = snake_main.c + +include $(APPDIR)/Application.mk diff --git a/games/snake/snake_input_console.h b/games/snake/snake_input_console.h new file mode 100644 index 000000000..468989d2a --- /dev/null +++ b/games/snake/snake_input_console.h @@ -0,0 +1,203 @@ +/**************************************************************************** + * apps/games/snake/snake_input_console.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "snake_inputs.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/* Termios functions to have getch on Linux/NuttX */ + +static struct termios g_old; +static struct termios g_new; + +/**************************************************************************** + * Name: init_termios + * + * Description: + * Initialize g_new terminal I/O settings. + * + * Parameters: + * echo - Enable echo flag + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void init_termios(int echo) +{ + tcgetattr(0, &g_old); /* grab old terminal i/o settings */ + g_new = g_old; /* use old settings as starting */ + g_new.c_lflag &= ~ICANON; /* disable buffered I/O */ + g_new.c_lflag &= ~ECHO; /* disable ECHO bit */ + g_new.c_lflag |= echo ? ECHO : 0; /* set echo mode if requested */ + tcsetattr(0, TCSANOW, &g_new); /* apply terminal I/O settings */ + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); /* set non-blocking mode */ +} + +/**************************************************************************** + * Name: reset_termios + * + * Description: + * Restore g_old terminal i/o settings. + * + * Parameters: + * None + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void reset_termios(void) +{ + tcsetattr(0, TCSANOW, &g_old); +} + +/**************************************************************************** + * Name: getch_ + * + * Description: + * Read 1 character. + * + * Parameters: + * echo - Enable echo mode flag + * + * Returned Value: + * Read character. + * + ****************************************************************************/ + +char getch_(int echo) +{ + char ch; + + init_termios(echo); + ch = getchar(); + reset_termios(); + + return ch; +} + +/**************************************************************************** + * Name: getch + * + * Description: + * Read 1 character without echo. + * + * Parameters: + * None + * + * Returned Value: + * Read character. + * + ****************************************************************************/ + +char getch(void) +{ + return getch_(0); +} + +/**************************************************************************** + * Name: dev_input_init + * + * Description: + * Initialize input method. + * + * Parameters: + * dev - Input state data + * + * Returned Value: + * Zero (OK) + * + ****************************************************************************/ + +int dev_input_init(FAR struct input_state_s *dev) +{ + init_termios(0); + + return OK; +} + +/**************************************************************************** + * Name: dev_read_input + * + * Description: + * Read inputs and returns result in input state data. + * + * Parameters: + * dev - Input state data + * + * Returned Value: + * Zero (OK) + * + ****************************************************************************/ + +int dev_read_input(FAR struct input_state_s *dev) +{ + char ch; + + /* Arrows keys return three bytes: 27 91 [65-68] */ + + if ((ch = getch()) == 27) + { + if ((ch = getch()) == 91) + { + ch = getch(); + if (ch == 65) + { + dev->dir = DIR_UP; + } + else if (ch == 66) + { + dev->dir = DIR_DOWN; + } + else if (ch == 67) + { + dev->dir = DIR_RIGHT; + } + else if (ch == 68) + { + dev->dir = DIR_LEFT; + } + } + else + { + dev->dir = DIR_NONE; + } + } + else + { + dev->dir = DIR_NONE; + } + + return OK; +} diff --git a/games/snake/snake_input_gpio.h b/games/snake/snake_input_gpio.h new file mode 100644 index 000000000..cb2e7329c --- /dev/null +++ b/games/snake/snake_input_gpio.h @@ -0,0 +1,201 @@ +/**************************************************************************** + * apps/games/snake/snake_input_gpio.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "snake_inputs.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +struct gpio_struct_fd_s +{ + int fd_up; /* File descriptor value to read up arrow key */ + int fd_down; /* File descriptor value to read down arrow key */ + int fd_left; /* File descriptor value to read left arrow key */ + int fd_right; /* File descriptor value to read right arrow key */ +}; + +struct gpio_struct_fd_s fd_list; + +/**************************************************************************** + * Name: dev_input_init + * + * Description: + * Initialize input method. + * + * Parameters: + * dev - Input state data + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int dev_input_init(FAR struct input_state_s *dev) +{ + /* Open the up key gpio device */ + + fd_list.fd_up = open(CONFIG_GAMES_SNAKE_UP_KEY_PATH, O_RDONLY); + if (fd_list.fd_up < 0) + { + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + CONFIG_GAMES_SNAKE_UP_KEY_PATH, errno); + return -ENODEV; + } + + /* Open the down key gpio device */ + + fd_list.fd_down = open(CONFIG_GAMES_SNAKE_DOWN_KEY_PATH, O_RDONLY); + if (fd_list.fd_down < 0) + { + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + CONFIG_GAMES_SNAKE_DOWN_KEY_PATH, errno); + return -ENODEV; + } + + /* Open the left key gpio device */ + + fd_list.fd_left = open(CONFIG_GAMES_SNAKE_LEFT_KEY_PATH, O_RDONLY); + if (fd_list.fd_down < 0) + { + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + CONFIG_GAMES_SNAKE_LEFT_KEY_PATH, errno); + return -ENODEV; + } + + /* Open the right key gpio device */ + + fd_list.fd_right = open(CONFIG_GAMES_SNAKE_RIGHT_KEY_PATH, O_RDONLY); + if (fd_list.fd_down < 0) + { + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + CONFIG_GAMES_SNAKE_RIGHT_KEY_PATH, errno); + return -ENODEV; + } + + dev->fd_gpio = (int)&fd_list; + + return OK; +} + +/**************************************************************************** + * Name: dev_read_input + * + * Description: + * Read inputs and returns result in input state data. + * + * Parameters: + * dev - Input state data + * + * Returned Value: + * Zero (OK) + * + ****************************************************************************/ + +int dev_read_input(FAR struct input_state_s *dev) +{ + struct gpio_struct_fd_s *fd = (struct gpio_struct_fd_s *)dev->fd_gpio; + int invalue = 0; + int ret; + + ret = ioctl(fd->fd_up, GPIOC_READ, (unsigned long)((uintptr_t)&invalue)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to read value from %s: %d\n", + CONFIG_GAMES_SNAKE_UP_KEY_PATH, errcode); + } + else + { + if (invalue != 0) + { + dev->dir = DIR_UP; + } + } + + ret = ioctl(fd->fd_down, GPIOC_READ, (unsigned long)((uintptr_t)&invalue)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to read value from %s: %d\n", + CONFIG_GAMES_SNAKE_DOWN_KEY_PATH, errcode); + } + else + { + if (invalue != 0) + { + dev->dir = DIR_DOWN; + } + } + + ret = ioctl(fd->fd_left, GPIOC_READ, (unsigned long)((uintptr_t)&invalue)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to read value from %s: %d\n", + CONFIG_GAMES_SNAKE_LEFT_KEY_PATH, errcode); + } + else + { + if (invalue != 0) + { + dev->dir = DIR_LEFT; + } + } + + ret = ioctl(fd->fd_right, GPIOC_READ, + (unsigned long)((uintptr_t)&invalue)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to read value from %s: %d\n", + CONFIG_GAMES_SNAKE_RIGHT_KEY_PATH, errcode); + } + else + { + if (invalue != 0) + { + dev->dir = DIR_RIGHT; + } + } + + return OK; +} diff --git a/games/snake/snake_inputs.h b/games/snake/snake_inputs.h new file mode 100644 index 000000000..630349459 --- /dev/null +++ b/games/snake/snake_inputs.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/games/snake/snake_inputs.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#ifndef DIR_NONE +# define DIR_NONE 0 +#endif + +#ifndef DIR_LEFT +# define DIR_LEFT 1 +#endif + +#ifndef DIR_RIGHT +# define DIR_RIGHT 2 +#endif + +#ifndef DIR_UP +# define DIR_UP 3 +#endif + +#ifndef DIR_DOWN +# define DIR_DOWN 4 +#endif + +struct input_state_s +{ +#ifdef CONFIG_GAMES_SNAKE_USE_CONSOLEKEY + int fd_con; +#endif +#ifdef CONFIG_GAMES_SNAKE_USE_GPIO + int fd_gpio; +#endif + + int dir; /* Direction to move the blocks */ +}; + diff --git a/games/snake/snake_main.c b/games/snake/snake_main.c new file mode 100644 index 000000000..8b65d80bd --- /dev/null +++ b/games/snake/snake_main.c @@ -0,0 +1,628 @@ +/**************************************************************************** + * apps/games/snake/snake_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef CONFIG_GAMES_SNAKE_USE_CONSOLEKEY +#include "snake_input_console.h" +#endif + +#ifdef CONFIG_GAMES_SNAKE_USE_GPIO +#include "snake_input_gpio.h" +#endif + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_SNAKE_GAME +# define DEBUG_SNAKE_GAME 1 +#endif + +#define BOARDX_SIZE CONFIG_GAMES_SNAKE_LED_MATRIX_ROWS +#define BOARDY_SIZE CONFIG_GAMES_SNAKE_LED_MATRIX_COLS + +#define N_LEDS BOARDX_SIZE * BOARDY_SIZE + +/* ID numbers of game items */ + +#define BLANK_PLACE 0 +#define SNAKE_TAIL 1 +#define SNAKE_HEAD 2 +#define FOOD 3 +#define BLINK 4 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Game item struct to repesent in board */ + +struct game_item +{ + int pos_x; + int pos_y; +}; + +/* Snake item struct to repesent in board */ + +struct snake_item +{ + struct game_item tail[BOARDX_SIZE * BOARDY_SIZE]; + int tail_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char g_board_path[] = + CONFIG_GAMES_SNAKE_LED_MATRIX_PATH; + +struct snake_item snake = +{ + 0 +}; + +struct game_item food = +{ + 0 +}; + +/* Game board to show */ + +uint32_t board[BOARDX_SIZE][BOARDY_SIZE] = +{ + 0 +}; + +/* Colors used in the game plus Black */ + +static const uint32_t pallete[] = +{ + RGB24_BLACK, + RGB24_BLUE, + RGB24_CYAN, + RGB24_RED, + RGB24_WHITE, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dim_color + * + * Description: + * Dim led color to handle brightness. + * + * Parameters: + * val - RGB24 value of the led + * dim - Percentage of brightness + * + * Returned Value: + * Dimmed RGB24 value. + * + ****************************************************************************/ + +static uint32_t dim_color(uint32_t val, float dim) +{ + uint16_t r = RGB24RED(val); + uint16_t g = RGB24GREEN(val); + uint16_t b = RGB24BLUE(val); + + float sat = dim; + + r *= sat; + g *= sat; + b *= sat; + + return RGBTO24(r, g, b); +} + +/**************************************************************************** + * Name: draw_board + * + * Description: + * Draw the user board. + * + * Parameters: + * fd - File descriptor for screen + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void draw_board(int fd) +{ + uint32_t buffer[N_LEDS] = + { + 0 + }; + + int result; + uint32_t *bp = buffer; + int x; + int y; + int rgb_val; + int tmp; + + for (x = 0; x < BOARDX_SIZE; x++) + { + for (y = 0; y < BOARDY_SIZE; y++) + { + rgb_val = pallete[board[x][y]]; + tmp = dim_color(rgb_val, 0.15); + *bp++ = ws2812_gamma_correct(tmp); + } + } + + lseek(fd, 0, SEEK_SET); + + result = write(fd, buffer, 4 * N_LEDS); + if (result != 4 * N_LEDS) + { + fprintf(stderr, + "ws2812_main: write failed: %d %d\n", + result, + errno); + } +} + +/**************************************************************************** + * Name: print_board + * + * Description: + * Draw the board for debugging. + * + * Parameters: + * None + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef DEBUG_SNAKE_GAME +static void print_board(void) +{ + const char board_icons[] = + { + ' ', + 'o', + 'O', + 'X' + }; + + int row; + int col; + int tmp; + int tmp_idx; + + /* Clear screen */ + + printf("\e[1;1H\e[2J"); + + /* Print board */ + + for (row = 0; row < BOARDY_SIZE; row++) + { + for (tmp = 0; tmp < BOARDX_SIZE; tmp++) + { + printf("+--"); + } + + printf("-+\n"); + printf("|"); + for (col = 0; col < BOARDX_SIZE; col++) + { + tmp_idx = board[row][col]; + printf(" %c ", board_icons[tmp_idx]); + } + + printf("|\n"); + } + + for (tmp = 0; tmp < BOARDX_SIZE; tmp++) + { + printf("+--"); + } + + printf("-+\n\n\n"); +} +#endif + +/**************************************************************************** + * Name: move_snake + * + * Description: + * Move the snake to 'dir' direction (L,R,U,D). + * + * Parameters: + * dir - Direction to move + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void move_snake(int dir) +{ + int prev_x = snake.tail[0].pos_x; + int prev_y = snake.tail[0].pos_y; + int prev_2x; + int prev_2y; + int i; + + board[prev_x][prev_y] = SNAKE_TAIL; + for (i = 1; i < snake.tail_len; i++) + { + prev_2x = snake.tail[i].pos_x; + prev_2y = snake.tail[i].pos_y; + snake.tail[i].pos_x = prev_x; + snake.tail[i].pos_y = prev_y; + prev_x = prev_2x; + prev_y = prev_2y; + } + + board[prev_x][prev_y] = 0; + + if (dir == DIR_LEFT) + { + snake.tail[0].pos_y--; + } + + if (dir == DIR_RIGHT) + { + snake.tail[0].pos_y++; + } + + if (dir == DIR_UP) + { + snake.tail[0].pos_x--; + } + + if (dir == DIR_DOWN) + { + snake.tail[0].pos_x++; + } + + /* Wrap around screen borders */ + + if (snake.tail[0].pos_x >= BOARDX_SIZE) + { + snake.tail[0].pos_x = 0; + } + + if (snake.tail[0].pos_x < 0) + { + snake.tail[0].pos_x = BOARDX_SIZE - 1; + } + + if (snake.tail[0].pos_y >= BOARDY_SIZE) + { + snake.tail[0].pos_y = 0; + } + + if (snake.tail[0].pos_y < 0) + { + snake.tail[0].pos_y = BOARDY_SIZE - 1; + } + + board[snake.tail[0].pos_x][snake.tail[0].pos_y] = SNAKE_HEAD; +} + +/**************************************************************************** + * Name: clear_board + * + * Description: + * Clears the board + * + * Parameters: + * None + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void clear_board(void) +{ + int i; + int j; + + for (i = 0; i < BOARDX_SIZE; i++) + { + for (j = 0; j < BOARDY_SIZE; j++) + { + board[i][j] = 0; + } + } +} + +/**************************************************************************** + * Name: init_game + * + * Description: + * Initializes game to start properly + * + * Parameters: + * None + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void init_game(void) +{ + snake.tail_len = 1; + snake.tail[0].pos_x = BOARDX_SIZE / 2; + snake.tail[0].pos_y = BOARDY_SIZE / 2; + food.pos_x = rand() % BOARDX_SIZE; + food.pos_y = rand() % BOARDY_SIZE; + clear_board(); + + board[BOARDX_SIZE / 2][BOARDY_SIZE / 2] = SNAKE_HEAD; +} + +/**************************************************************************** + * Name: check_food_snake_collision + * + * Description: + * Check if there is any collision between food or snake + * + * Parameters: + * None + * + * Returned Value: + * True(1) if there is collision, False(0) if not. + * + ****************************************************************************/ + +static bool check_food_snake_collision(void) +{ + int i = 0; + + for (i = 0; i < snake.tail_len; i++) + { + if (snake.tail[i].pos_x == food.pos_x && + snake.tail[i].pos_y == food.pos_y) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: check_collisions + * + * Description: + * Check if there is any collision between food or snake itself + * + * Parameters: + * None + * + * Returned Value: + * True(1) if there is collision itself, False(0) if not. + * + ****************************************************************************/ + +static int check_collisions(void) +{ + int i = 0; + bool end_game = false; + bool food_col = false; + + /* Collision with itself (game over) */ + + for (i = 1; i < snake.tail_len; i++) + { + if (snake.tail[i].pos_x == snake.tail[0].pos_x && + snake.tail[i].pos_y == snake.tail[0].pos_y) + { + end_game = true; + } + } + + /* Collision with food (eating food) */ + + food_col = check_food_snake_collision(); + if (food_col) + { + snake.tail_len++; + + while (food_col) + { + food.pos_x = rand() % BOARDX_SIZE; + food.pos_y = rand() % BOARDY_SIZE; + food_col = check_food_snake_collision(); + } + } + + board[food.pos_x][food.pos_y] = FOOD; + return end_game; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * snake_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + bool game_over; + struct input_state_s input_st; + int board_fd = -1; + int last_dir = DIR_NONE; + int ret = ERROR; + int i; + int idx; + int board_x; + int board_y; + struct snake_item prev_snake = + { + 0 + }; + + srand(time(NULL)); + + /* Open the output device driver */ + + board_fd = open(g_board_path, O_RDWR); + if (board_fd < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + g_board_path, errcode); + return EXIT_FAILURE; + } + + dev_input_init(&input_st); + + /* Draw the empty board to user */ + +restart: + init_game(); + draw_board(board_fd); + + input_st.dir = DIR_NONE; + game_over = false; + + while (!game_over) + { +#ifdef DEBUG_SNAKE_GAME + print_board(); +#endif + draw_board(board_fd); + + last_dir = input_st.dir; + ret = ERROR; + + while (ret == ERROR) + { + ret = dev_read_input(&input_st); + } + + /* Check if input creates collision */ + + if ((last_dir == DIR_LEFT && input_st.dir == DIR_RIGHT) || + (last_dir == DIR_RIGHT && input_st.dir == DIR_LEFT) || + (last_dir == DIR_UP && input_st.dir == DIR_DOWN) || + (last_dir == DIR_DOWN && input_st.dir == DIR_UP)) + { + input_st.dir = last_dir; + } + + if (input_st.dir == DIR_NONE) + { + input_st.dir = last_dir; + } + + memcpy(&prev_snake, &snake, sizeof(struct snake_item)); + + move_snake(input_st.dir); + + game_over = check_collisions(); + + if (game_over) + { + /* Lets do a blinking effect */ + + for (i = 0; i < 3; i++) + { + /* Draw the board with the pieces */ + + for (idx = 0; idx < prev_snake.tail_len; idx++) + { + board_x = prev_snake.tail[idx].pos_x; + board_y = prev_snake.tail[idx].pos_y; + board[board_x][board_y] = BLINK; + } + + draw_board(board_fd); + usleep(100000); + + /* Draw the board without the pieces */ + + board[prev_snake.tail[0].pos_x][prev_snake.tail[0].pos_y] = + SNAKE_HEAD; + for (idx = 1; idx < snake.tail_len; idx++) + { + board_x = prev_snake.tail[idx].pos_x; + board_y = prev_snake.tail[idx].pos_y; + board[board_x][board_y] = SNAKE_TAIL; + } + + draw_board(board_fd); + usleep(100000); + } + + usleep(500000); + } + + usleep(85000); + } + + printf("Game Over! Score: %d", (snake.tail_len * 10)); + + printf("Please press left key to exit or other keys to restart\n"); + usleep(1000000); + + input_st.dir = DIR_NONE; + ret = ERROR; + while (ret == ERROR && input_st.dir == DIR_NONE) + { + ret = dev_read_input(&input_st); + } + + if (input_st.dir != DIR_LEFT) + { + goto restart; + } + + return 0; +}