This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 8b121eb13 games/match4: Add new match4 game
8b121eb13 is described below
commit 8b121eb1382c0c66030d7b602c8523532e6ff492
Author: Eren Terzioglu <[email protected]>
AuthorDate: Sat Jul 5 17:48:01 2025 +0200
games/match4: Add new match4 game
Add match4 game support
Signed-off-by: Eren Terzioglu <[email protected]>
---
games/match4/Kconfig | 84 +++
games/match4/Make.defs | 25 +
games/match4/Makefile | 36 ++
games/match4/match4_input_console.h | 239 ++++++++
games/match4/match4_input_gpio.h | 194 +++++++
games/match4/match4_inputs.h | 60 ++
games/match4/match4_main.c | 1024 +++++++++++++++++++++++++++++++++++
7 files changed, 1662 insertions(+)
diff --git a/games/match4/Kconfig b/games/match4/Kconfig
new file mode 100644
index 000000000..cd916fd45
--- /dev/null
+++ b/games/match4/Kconfig
@@ -0,0 +1,84 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config GAMES_MATCH4
+ bool "Match 4 Game"
+ default n
+ ---help---
+ Enable Match 4 game.
+
+if GAMES_MATCH4
+
+config GAMES_MATCH4_PROGNAME
+ string "Program name"
+ default "match"
+ ---help---
+ This is the name of the program that will be used when the NSH
ELF
+ program is installed.
+
+config GAMES_MATCH4_PRIORITY
+ int "Match 4 Game task priority"
+ default 100
+
+config GAMES_MATCH4_STACKSIZE
+ int "Match 4 Game stack size"
+ default DEFAULT_TASK_STACKSIZE
+
+config MATCH4_GAME_DEBUG
+ bool "Print board status to the serial console for debugging"
+ default n
+
+config GAMES_MATCH4_LED_MATRIX_PATH
+ string "LED matrix path"
+ default "/dev/leds0"
+ ---help---
+ Path of the led matrix
+
+config GAMES_MATCH4_LED_MATRIX_ROWS
+ int "LED Matrix row count"
+ default 8
+
+config GAMES_MATCH4_LED_MATRIX_COLS
+ int "LED Matrix column count"
+ default 8
+
+#
+# Input Device Selection
+#
+
+choice
+ prompt "Input Device (Serial Console, GPIO, etc)"
+ default GAMES_MATCH4_USE_CONSOLEKEY
+
+config GAMES_MATCH4_USE_CONSOLEKEY
+ bool "Serial Console as Input"
+
+config GAMES_MATCH4_USE_GPIO
+ bool "GPIO pins as Input"
+endchoice
+
+if GAMES_MATCH4_USE_GPIO
+
+config GAMES_MATCH4_DOWN_KEY_PATH
+ string "Down key path"
+ default "/dev/gpio1"
+ ---help---
+ Path of the down key to read
+
+config GAMES_MATCH4_LEFT_KEY_PATH
+ string "Left key path"
+ default "/dev/gpio2"
+ ---help---
+ Path of the left key to read
+
+config GAMES_MATCH4_RIGHT_KEY_PATH
+ string "Right key path"
+ default "/dev/gpio3"
+ ---help---
+ Path of the right key to read
+
+endif #GAMES_MATCH4_USE_GPIO
+
+endif
diff --git a/games/match4/Make.defs b/games/match4/Make.defs
new file mode 100644
index 000000000..27b4f1352
--- /dev/null
+++ b/games/match4/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/games/match4/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_MATCH4),)
+CONFIGURED_APPS += $(APPDIR)/games/match4
+endif
diff --git a/games/match4/Makefile b/games/match4/Makefile
new file mode 100644
index 000000000..53bc65186
--- /dev/null
+++ b/games/match4/Makefile
@@ -0,0 +1,36 @@
+############################################################################
+# apps/games/match4/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
+
+# Match4 game info
+
+PROGNAME = $(CONFIG_GAMES_MATCH4_PROGNAME)
+PRIORITY = $(CONFIG_GAMES_MATCH4_PRIORITY)
+STACKSIZE = $(CONFIG_GAMES_MATCH4_STACKSIZE)
+MODULE = $(CONFIG_GAMES_MATCH4)
+
+# Match4 game application
+
+MAINSRC = match4_main.c
+
+include $(APPDIR)/Application.mk
diff --git a/games/match4/match4_input_console.h
b/games/match4/match4_input_console.h
new file mode 100644
index 000000000..3e7f0522e
--- /dev/null
+++ b/games/match4/match4_input_console.h
@@ -0,0 +1,239 @@
+/****************************************************************************
+ * apps/games/match4/match4_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 <nuttx/config.h>
+#include <nuttx/ascii.h>
+#include <termios.h>
+
+#include "match4_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 */
+}
+
+/****************************************************************************
+ * Name: deinit_termios
+ *
+ * Description:
+ * Restore back g_old terminal I/O settings.
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+void deinit_termios(void)
+{
+ tcsetattr(0, TCSANOW, &g_old); /* restore old terminal i/o settings */
+}
+
+/****************************************************************************
+ * 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_input_deinit
+ *
+ * Description:
+ * Deinitialize input method.
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * Zero (OK)
+ *
+ ****************************************************************************/
+
+int dev_input_deinit(void)
+{
+ deinit_termios();
+
+ 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()) == ASCII_ESC)
+ {
+ if ((ch = getch()) == ASCII_LBRACKET)
+ {
+ ch = getch();
+ if (ch == ASCII_B)
+ {
+ dev->dir = DIR_DOWN;
+ }
+ else if (ch == ASCII_C)
+ {
+ dev->dir = DIR_RIGHT;
+ }
+ else if (ch == ASCII_D)
+ {
+ dev->dir = DIR_LEFT;
+ }
+ }
+ else
+ {
+ dev->dir = DIR_NONE;
+ }
+ }
+ else
+ {
+ dev->dir = DIR_NONE;
+ }
+
+ return OK;
+}
diff --git a/games/match4/match4_input_gpio.h b/games/match4/match4_input_gpio.h
new file mode 100644
index 000000000..a25ab3930
--- /dev/null
+++ b/games/match4/match4_input_gpio.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+ * apps/games/match4/match4_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 <nuttx/config.h>
+
+#include <sys/ioctl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <nuttx/ioexpander/gpio.h>
+
+#include "match4_inputs.h"
+
+/****************************************************************************
+ * Preprocessor Definitions
+ ****************************************************************************/
+
+struct gpio_struct_fd_s
+{
+ 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 down key gpio device */
+
+ fd_list.fd_down = open(CONFIG_GAMES_MATCH4_DOWN_KEY_PATH, O_RDONLY);
+ if (fd_list.fd_down < 0)
+ {
+ fprintf(stderr, "ERROR: Failed to open %s: %d\n",
+ CONFIG_GAMES_MATCH4_DOWN_KEY_PATH, errno);
+ return -ENODEV;
+ }
+
+ /* Open the left key gpio device */
+
+ fd_list.fd_left = open(CONFIG_GAMES_MATCH4_LEFT_KEY_PATH, O_RDONLY);
+ if (fd_list.fd_down < 0)
+ {
+ fprintf(stderr, "ERROR: Failed to open %s: %d\n",
+ CONFIG_GAMES_MATCH4_LEFT_KEY_PATH, errno);
+ return -ENODEV;
+ }
+
+ /* Open the right key gpio device */
+
+ fd_list.fd_right = open(CONFIG_GAMES_MATCH4_RIGHT_KEY_PATH, O_RDONLY);
+ if (fd_list.fd_down < 0)
+ {
+ fprintf(stderr, "ERROR: Failed to open %s: %d\n",
+ CONFIG_GAMES_MATCH4_RIGHT_KEY_PATH, errno);
+ return -ENODEV;
+ }
+
+ dev->fd_gpio = (int)&fd_list;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: dev_input_deinit
+ *
+ * Description:
+ * Deinitialize input method.
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * Zero (OK)
+ *
+ ****************************************************************************/
+
+int dev_input_deinit(void)
+{
+ 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_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_MATCH4_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_MATCH4_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_MATCH4_RIGHT_KEY_PATH, errcode);
+ }
+ else
+ {
+ if (invalue != 0)
+ {
+ dev->dir = DIR_RIGHT;
+ }
+ }
+
+ return OK;
+}
diff --git a/games/match4/match4_inputs.h b/games/match4/match4_inputs.h
new file mode 100644
index 000000000..e48c1847f
--- /dev/null
+++ b/games/match4/match4_inputs.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+ * apps/games/match4/match4_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 <nuttx/config.h>
+
+/****************************************************************************
+ * 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_DOWN
+# define DIR_DOWN 4
+#endif
+
+struct input_state_s
+{
+#ifdef CONFIG_GAMES_MATCH4_USE_CONSOLEKEY
+ int fd_con;
+#endif
+#ifdef CONFIG_GAMES_MATCH4_USE_GPIO
+ int fd_gpio;
+#endif
+
+ int dir; /* Direction to move the blocks */
+};
+
diff --git a/games/match4/match4_main.c b/games/match4/match4_main.c
new file mode 100644
index 000000000..d60e1dbc6
--- /dev/null
+++ b/games/match4/match4_main.c
@@ -0,0 +1,1024 @@
+/****************************************************************************
+ * apps/games/match4/match4_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 <nuttx/config.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+
+#include <nuttx/video/rgbcolors.h>
+
+#include <nuttx/leds/ws2812.h>
+
+#ifdef CONFIG_GAMES_MATCH4_USE_CONSOLEKEY
+#include "match4_input_console.h"
+#endif
+
+#ifdef CONFIG_GAMES_MATCH4_USE_GPIO
+#include "match4_input_gpio.h"
+#endif
+
+/****************************************************************************
+ * Preprocessor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_MATCH4_GAME_DEBUG
+# define DEBUG_MATCH4_GAME 1
+#endif
+
+#define BOARDX_SIZE CONFIG_GAMES_MATCH4_LED_MATRIX_ROWS
+#define BOARDY_SIZE CONFIG_GAMES_MATCH4_LED_MATRIX_COLS
+
+#define N_LEDS BOARDX_SIZE * BOARDY_SIZE
+
+#define P1_COLOUR RGB24_YELLOW
+#define P1_SELECT_COLOUR RGB24_GOLD
+#define P2_COLOUR RGB24_BLUE
+#define P2_SELECT_COLOUR RGB24_CYAN
+#define BLUE_COLOR RGB24_BLUE
+#define RED_COLOR RGB24_RED
+#define WHITE_COLOR RGB24_WHITE
+
+/* ID numbers of game items */
+
+#define BLANK_PLACE 0
+#define P1_PREVIEW 1
+#define P1_SELECT 2
+#define P2_PREVIEW 3
+#define P2_SELECT 4
+#define BLUE_INDEX 5
+#define RED_INDEX 6
+#define WHITE_INDEX 7
+#define PALETTE_MAX 8
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const char g_board_path[] =
+ CONFIG_GAMES_MATCH4_LED_MATRIX_PATH;
+
+/* Game board to show */
+
+uint32_t board[BOARDX_SIZE][BOARDY_SIZE] =
+{
+ 0
+};
+
+/* Player colours */
+
+uint32_t players[2] =
+{
+ P1_SELECT,
+ P2_SELECT,
+};
+
+/* Player move preview colour */
+
+uint32_t players_select[2] =
+{
+ P1_PREVIEW,
+ P2_PREVIEW,
+};
+
+/* Colors used in the game */
+
+static const uint32_t palette[] =
+{
+ RGB24_BLACK,
+ P1_COLOUR,
+ P1_SELECT_COLOUR,
+ P2_COLOUR,
+ P2_SELECT_COLOUR,
+ BLUE_COLOR,
+ RED_COLOR,
+ WHITE_COLOR,
+};
+
+/* Game entrance screen that shows one player mode */
+
+uint32_t one_player_scr[BOARDX_SIZE][BOARDY_SIZE] =
+{
+ {
+ BLUE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX,
+ BLUE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, BLUE_INDEX, WHITE_INDEX, BLUE_INDEX,
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, BLUE_INDEX, WHITE_INDEX,
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX,
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ }
+};
+
+/* Game entrance screen that shows two players mode */
+
+uint32_t two_player_scr[BOARDX_SIZE][BOARDY_SIZE] =
+{
+ {
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, BLUE_INDEX,
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, BLUE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX,
+ BLUE_INDEX, WHITE_INDEX, BLUE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX,
+ WHITE_INDEX, BLUE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX,
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, WHITE_INDEX, WHITE_INDEX, WHITE_INDEX
+ },
+ {
+ WHITE_INDEX, WHITE_INDEX, RED_INDEX, WHITE_INDEX,
+ RED_INDEX, RED_INDEX, RED_INDEX, WHITE_INDEX
+ }
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#ifdef DEBUG_MATCH4_GAME
+/****************************************************************************
+ * Name: print_board
+ *
+ * Description:
+ * Draw the board for debugging.
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void print_board(void)
+{
+ const char board_icons[] =
+ {
+ ' ', 'x', 'X', 'o', 'O', '-', '#', ' ', ' '
+ };
+
+ int row;
+ int col;
+ int tmp;
+
+ /* Clear screen */
+
+ printf("\e[1;1H\e[2J");
+
+ /* Print board */
+
+ for (int i = 0; i < BOARDY_SIZE; i++)
+ {
+ printf(" %d", i);
+ }
+
+ printf("\n");
+
+ for (row = 0; row < BOARDY_SIZE; row++)
+ {
+ for (col = 0; col < BOARDX_SIZE; col++)
+ {
+ printf("|%c", board_icons[board[row][col]]);
+ }
+
+ printf("|\n");
+ }
+
+ for (tmp = 0; tmp < BOARDX_SIZE * 2 + 1; tmp++)
+ {
+ printf("-");
+ }
+
+ printf("\n");
+}
+#endif /* DEBUG_MATCH4_GAME */
+
+/****************************************************************************
+ * 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;
+
+#ifdef DEBUG_MATCH4_GAME
+ print_board();
+#endif
+
+ for (x = 0; x < BOARDX_SIZE; x++)
+ {
+ for (y = 0; y < BOARDY_SIZE; y++)
+ {
+ rgb_val = palette[board[x][y]];
+ tmp = dim_color(rgb_val, 0.15);
+ *bp++ = ws2812_gamma_correct(tmp);
+ }
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ usleep(200);
+ result = write(fd, buffer, 4 * N_LEDS);
+ if (result != 4 * N_LEDS)
+ {
+ fprintf(stderr,
+ "ws2812_main: write failed: %d %d\n",
+ result,
+ errno);
+ }
+}
+
+/****************************************************************************
+ * Name: fill_board
+ *
+ * Description:
+ * Fill board with given value
+ *
+ * Parameters:
+ * val - Value to fill the board
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void fill_board(int val)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < BOARDX_SIZE; i++)
+ {
+ for (j = 0; j < BOARDY_SIZE; j++)
+ {
+ board[i][j] = val;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: put_mark
+ *
+ * Description:
+ * Drop a piece on the selected column
+ *
+ * Parameters:
+ * col - Column number
+ * piece - Piece to drop
+ *
+ * Returned Value:
+ * OK on success, ERROR if not.
+ *
+ ****************************************************************************/
+
+int put_mark(int col, uint32_t piece)
+{
+ int i = 0;
+ if (col < 0 || col >= BOARDY_SIZE)
+ {
+ return OK;
+ }
+
+ for (i = BOARDX_SIZE - 1; i >= 0; i--)
+ {
+ if (board[i][col] == BLANK_PLACE)
+ {
+ board[i][col] = piece;
+ return OK;
+ }
+ }
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: check_winner
+ *
+ * Description:
+ * Check for 4 in a row in any direction
+ *
+ * Parameters:
+ * piece - Mark number depends on turn
+ * blink - Flag to blink
+ *
+ * Returned Value:
+ * OK on success, ERROR if not.
+ *
+ ****************************************************************************/
+
+int check_winner(uint32_t piece, bool blink, int fd)
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ /* Horizontal 4 piece check */
+
+ for (i = 0; i < BOARDX_SIZE; i++)
+ {
+ for (j = 0; j <= BOARDY_SIZE - 4; j++)
+ {
+ if (board[i][j] == piece &&
+ board[i][j + 1] == piece &&
+ board[i][j + 2] == piece &&
+ board[i][j + 3] == piece)
+ {
+ if (blink)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ board[i][j] = WHITE_INDEX;
+ board[i][j + 1] = WHITE_INDEX;
+ board[i][j + 2] = WHITE_INDEX;
+ board[i][j + 3] = WHITE_INDEX;
+ draw_board(fd);
+ usleep(300000);
+
+ board[i][j] = piece;
+ board[i][j + 1] = piece;
+ board[i][j + 2] = piece;
+ board[i][j + 3] = piece;
+ draw_board(fd);
+ usleep(300000);
+ }
+ }
+
+ return OK;
+ }
+ }
+ }
+
+ /* Vertical 4 piece check */
+
+ for (i = 0; i <= BOARDX_SIZE - 4; i++)
+ {
+ for (j = 0; j < BOARDY_SIZE; j++)
+ {
+ if (board[i][j] == piece &&
+ board[i + 1][j] == piece &&
+ board[i + 2][j] == piece &&
+ board[i + 3][j] == piece)
+ {
+ if (blink)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ board[i][j] = WHITE_INDEX;
+ board[i + 1][j] = WHITE_INDEX;
+ board[i + 2][j] = WHITE_INDEX;
+ board[i + 3][j] = WHITE_INDEX;
+ draw_board(fd);
+ usleep(300000);
+
+ board[i][j] = piece;
+ board[i + 1][j] = piece;
+ board[i + 2][j] = piece;
+ board[i + 3][j] = piece;
+ draw_board(fd);
+ usleep(300000);
+ }
+ }
+
+ return OK;
+ }
+ }
+ }
+
+ /* Diagonal down-right 4 piece check */
+
+ for (i = 0; i <= BOARDX_SIZE - 4; i++)
+ {
+ for (j = 0; j <= BOARDY_SIZE - 4; j++)
+ {
+ if (board[i][j] == piece &&
+ board[i + 1][j + 1] == piece &&
+ board[i + 2][j + 2] == piece &&
+ board[i + 3][j + 3] == piece)
+ {
+ if (blink)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ board[i][j] = WHITE_INDEX;
+ board[i + 1][j + 1] = WHITE_INDEX;
+ board[i + 2][j + 2] = WHITE_INDEX;
+ board[i + 3][j + 3] = WHITE_INDEX;
+ draw_board(fd);
+ usleep(300000);
+
+ board[i][j] = piece;
+ board[i + 1][j + 1] = piece;
+ board[i + 2][j + 2] = piece;
+ board[i + 3][j + 3] = piece;
+ draw_board(fd);
+ usleep(300000);
+ }
+ }
+
+ return OK;
+ }
+ }
+ }
+
+ /* Diagonal up-right 4 piece check */
+
+ for (i = 3; i < BOARDX_SIZE; i++)
+ {
+ for (j = 0; j <= BOARDY_SIZE - 4; j++)
+ {
+ if (board[i][j] == piece &&
+ board[i - 1][j + 1] == piece &&
+ board[i - 2][j + 2] == piece &&
+ board[i - 3][j + 3] == piece)
+ {
+ if (blink)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ board[i][j] = WHITE_INDEX;
+ board[i - 1][j + 1] = WHITE_INDEX;
+ board[i - 2][j + 2] = WHITE_INDEX;
+ board[i - 3][j + 3] = WHITE_INDEX;
+ draw_board(fd);
+ usleep(300000);
+
+ board[i][j] = piece;
+ board[i - 1][j + 1] = piece;
+ board[i - 2][j + 2] = piece;
+ board[i - 3][j + 3] = piece;
+ draw_board(fd);
+ usleep(300000);
+ }
+ }
+
+ return OK;
+ }
+ }
+ }
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: is_board_empty
+ *
+ * Description:
+ * Check if the board is full to decide there is a draw
+ *
+ * Parameters:
+ * None.
+ *
+ * Returned Value:
+ * OK if there is a BLANK_PLACE space, ERROR if not.
+ *
+ ****************************************************************************/
+
+static int is_board_empty(void)
+{
+ for (int j = 0; j < BOARDY_SIZE; j++)
+ {
+ if (board[0][j] == BLANK_PLACE)
+ {
+ return OK;
+ }
+ }
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: ai_move
+ *
+ * Description:
+ * Finds first empty column to fill
+ *
+ * Parameters:
+ * piece - AI (CPU) piece value
+ * opponent_piece - Opponent piece value
+ *
+ * Returned Value:
+ * Column number if there is valid move, ERROR if not.
+ *
+ ****************************************************************************/
+
+int ai_move(uint32_t piece, uint32_t opponent_piece)
+{
+ int row = 0;
+ int col = 0;
+ int valid_boardy_size[BOARDY_SIZE];
+ int count = 0;
+
+ /* Trying to find winner move */
+
+ for (col = 0; col < BOARDY_SIZE; col++)
+ {
+ for (row = BOARDX_SIZE - 1; row >= 0; row--)
+ {
+ if (board[row][col] == BLANK_PLACE)
+ {
+ board[row][col] = piece;
+ if (check_winner(piece, false, 0) == OK)
+ {
+ board[row][col] = 0;
+ return col;
+ }
+
+ board[row][col] = 0;
+ break;
+ }
+ }
+ }
+
+ /* Trying to block opponent let to win */
+
+ for (col = 0; col < BOARDY_SIZE; col++)
+ {
+ for (row = BOARDX_SIZE - 1; row >= 0; row--)
+ {
+ if (board[row][col] == BLANK_PLACE)
+ {
+ board[row][col] = opponent_piece;
+ if (check_winner(opponent_piece, false, 0) == OK)
+ {
+ board[row][col] = 0;
+ return col;
+ }
+
+ board[row][col] = 0;
+ break;
+ }
+ }
+ }
+
+ /* Choose random column */
+
+ for (col = 0; col < BOARDY_SIZE; col++)
+ {
+ if (board[0][col] == BLANK_PLACE)
+ {
+ valid_boardy_size[count++] = col;
+ }
+ }
+
+ if (count > 0)
+ {
+ return valid_boardy_size[rand() % count];
+ }
+
+ /* Return ERROR if no valid move */
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: find_empty_col
+ *
+ * Description:
+ * Finds first empty column to fill
+ *
+ * Parameters:
+ * col - Column number to start
+ * dir - Direction from that column
+ *
+ * Returned Value:
+ * First empty column to fill.
+ *
+ ****************************************************************************/
+
+static int find_empty_col(int col, int dir)
+{
+ int i = 0;
+ while (i < BOARDY_SIZE)
+ {
+ if ((col == 7) && (dir == 1))
+ {
+ col = 0;
+ }
+ else if ((col == BLANK_PLACE) && (dir == -1))
+ {
+ col = 7;
+ }
+ else
+ {
+ col = col + dir;
+ }
+
+ if (board[0][col] == BLANK_PLACE)
+ {
+ break;
+ }
+
+ i++;
+ }
+
+ return col;
+}
+
+/****************************************************************************
+ * Name: init_game
+ *
+ * Description:
+ * Initializes game to start properly
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void init_game(void)
+{
+ fill_board(BLANK_PLACE);
+}
+
+/****************************************************************************
+ * Name: get_col_input
+ *
+ * Description:
+ * Gets column number to put mark into board.
+ *
+ * Parameters:
+ * input_st - Input struct pointer to get input
+ * turn - Turn number
+ * col - Column number pointer to return
+ * fd - File descriptor to print
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void get_col_input(struct input_state_s *input_st,
+ int turn, int *col, int fd)
+{
+ int ret = ERROR;
+ uint8_t column = find_empty_col(-1, 1);
+ bool refresh_board = true;
+
+ printf("Player %d, choose column (0-%d): \n", turn + 1, BOARDX_SIZE - 1);
+ while (input_st->dir != DIR_DOWN)
+ {
+ if (refresh_board)
+ {
+ refresh_board = false;
+ put_mark(column, players_select[turn]);
+ draw_board(fd);
+ int j = 0;
+ while (board[j][column] != players_select[turn])
+ {
+ j++;
+ }
+
+ board[j][column] = 0;
+ }
+
+ while (ret == ERROR || input_st->dir == DIR_NONE)
+ {
+ ret = dev_read_input(input_st);
+ }
+
+ if (input_st->dir == DIR_RIGHT)
+ {
+ column = find_empty_col(column, 1);
+ refresh_board = true;
+ input_st->dir = DIR_NONE;
+ printf("right button pressed: %d\n", column);
+ usleep(7000);
+ }
+ else if (input_st->dir == DIR_LEFT)
+ {
+ column = find_empty_col(column, -1);
+ refresh_board = true;
+ input_st->dir = DIR_NONE;
+ printf("left button pressed: %d\n", column);
+ usleep(7000);
+ }
+
+ usleep(15000);
+ }
+
+ usleep(15000);
+ input_st->dir = DIR_NONE;
+ *col = column;
+}
+
+/****************************************************************************
+ * Name: draw_led_matrix
+ *
+ * Description:
+ * Fills board premade picture
+ *
+ * Parameters:
+ * img - Image to show
+ * fd - File descriptor to print
+ *
+ * Returned Value:
+ * None.
+ *
+ ****************************************************************************/
+
+static void draw_led_matrix(uint32_t img[][BOARDY_SIZE], int fd)
+{
+ int i = 0;
+ int j = 0;
+ for (i = 0; i < BOARDX_SIZE; i++)
+ {
+ for (j = 0; j < BOARDX_SIZE; j++)
+ {
+ board[i][j] = img[i][j];
+ }
+ }
+
+ draw_board(fd);
+}
+
+/****************************************************************************
+ * Name: game_entrance
+ *
+ * Description:
+ * Screen that handles entrance screen to select game mode (1-2 player)
+ *
+ * Parameters:
+ * input_st - Input struct pointer to get input
+ * fd - File descriptor to print
+ *
+ * Returned Value:
+ * True if one player mode, false if two player mode.
+ *
+ ****************************************************************************/
+
+static bool game_entrance(struct input_state_s *input_st, int fd)
+{
+ int ret = ERROR;
+ bool game_mode_one = true;
+
+ draw_led_matrix(one_player_scr, fd);
+ while (input_st->dir != DIR_DOWN)
+ {
+ while (ret == ERROR || input_st->dir == DIR_NONE)
+ {
+ ret = dev_read_input(input_st);
+ }
+
+ if (input_st->dir == DIR_LEFT || input_st->dir == DIR_RIGHT)
+ {
+ game_mode_one = !game_mode_one;
+ if (game_mode_one)
+ {
+ draw_led_matrix(one_player_scr, fd);
+ }
+ else
+ {
+ draw_led_matrix(two_player_scr, fd);
+ }
+
+ input_st->dir = DIR_NONE;
+ }
+
+ usleep(15000);
+ }
+
+ usleep(15000);
+ input_st->dir = DIR_NONE;
+ return game_mode_one;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * match4_main
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+ int col = 0;
+ int turn = 0;
+ bool one_player_mode = true;
+ int ret = ERROR;
+ int board_fd = -1;
+ bool game_over = false;
+ struct input_state_s input_st;
+ char player_symbol[2] =
+ {
+ 'X', 'O'
+ };
+
+ 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);
+
+restart:
+ ret = 0;
+ turn = 0;
+ col = 0;
+ one_player_mode = true;
+ input_st.dir = DIR_NONE;
+ game_over = false;
+
+ one_player_mode = game_entrance(&input_st, board_fd);
+ init_game();
+ while (!game_over)
+ {
+ draw_board(board_fd);
+ if ((one_player_mode && turn == 0) || !one_player_mode)
+ {
+ get_col_input(&input_st, turn, &col, board_fd);
+ }
+ else
+ {
+ col = ai_move(players[turn], players[(turn + 1) % 2]);
+ printf("AI chooses column %d\n", col);
+ }
+
+ ret = put_mark(col, players[turn]);
+ if (ret != 0)
+ {
+ printf("Column full or invalid. Try again.\n");
+ continue;
+ }
+
+ if (check_winner(players[turn], true, board_fd) == OK)
+ {
+ if ((one_player_mode && turn == 0) || !one_player_mode)
+ {
+ printf("\nPlayer %d (%c) wins!\n",
+ turn + 1, player_symbol[turn]);
+ }
+ else
+ {
+ printf("\nAI (%c) wins!\n", player_symbol[turn]);
+ }
+
+ fill_board(players[turn]);
+ draw_board(board_fd);
+ usleep(85000);
+ break;
+ }
+
+ if (is_board_empty() != OK)
+ {
+ printf("It's a draw!\n");
+ break;
+ }
+
+ turn = (turn + 1) % 2;
+ usleep(85000);
+ }
+
+ printf("Please press left key to exit or other keys to restart\n");
+ usleep(2000000);
+
+ 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;
+ }
+
+ input_st.dir = DIR_NONE;
+ dev_input_deinit();
+ return 0;
+}