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 <eren.terzio...@espressif.com>
AuthorDate: Sat Jul 5 17:48:01 2025 +0200

    games/match4: Add new match4 game
    
    Add match4 game support
    
    Signed-off-by: Eren Terzioglu <eren.terzio...@espressif.com>
---
 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;
+}


Reply via email to