From 8a177cd797d701b3214e71c2f1b995f010542769 Mon Sep 17 00:00:00 2001 From: Alan Carvalho de Assis Date: Thu, 21 Jul 2022 16:10:47 -0300 Subject: [PATCH] apps/games: Add shift game --- games/.gitignore | 1 + games/Make.defs | 21 + games/Makefile | 23 + games/shift/Kconfig | 58 ++ games/shift/Make.defs | 23 + games/shift/Makefile | 34 ++ games/shift/shift_input_console.h | 133 +++++ games/shift/shift_input_gesture.h | 78 +++ games/shift/shift_input_joystick.h | 166 ++++++ games/shift/shift_inputs.h | 64 ++ games/shift/shift_main.c | 927 +++++++++++++++++++++++++++++ 11 files changed, 1528 insertions(+) create mode 100644 games/.gitignore create mode 100644 games/Make.defs create mode 100644 games/Makefile create mode 100644 games/shift/Kconfig create mode 100644 games/shift/Make.defs create mode 100644 games/shift/Makefile create mode 100644 games/shift/shift_input_console.h create mode 100644 games/shift/shift_input_gesture.h create mode 100644 games/shift/shift_input_joystick.h create mode 100644 games/shift/shift_inputs.h create mode 100644 games/shift/shift_main.c diff --git a/games/.gitignore b/games/.gitignore new file mode 100644 index 000000000..9e1d2593e --- /dev/null +++ b/games/.gitignore @@ -0,0 +1 @@ +/Kconfig diff --git a/games/Make.defs b/games/Make.defs new file mode 100644 index 000000000..7152bd5c6 --- /dev/null +++ b/games/Make.defs @@ -0,0 +1,21 @@ +############################################################################ +# apps/games/Make.defs +# +# 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 $(wildcard $(APPDIR)/games/*/Make.defs) diff --git a/games/Makefile b/games/Makefile new file mode 100644 index 000000000..736c18fda --- /dev/null +++ b/games/Makefile @@ -0,0 +1,23 @@ +############################################################################ +# apps/games/Makefile +# +# 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. +# +############################################################################ + +MENUDESC = "Games" + +include $(APPDIR)/Directory.mk diff --git a/games/shift/Kconfig b/games/shift/Kconfig new file mode 100644 index 000000000..0a59395d9 --- /dev/null +++ b/games/shift/Kconfig @@ -0,0 +1,58 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config GAMES_SHIFT + bool "Shift Game" + default n + ---help--- + Enable Shift games. Shift is a brick game, like a mix + between Tetris and Crush Candy. The inspiration came + from a Shift game that was available for Android in the + F-Droid store. The original game source code still here: + https://github.com/boombuler/Shift/ but the game only works + for old Android versions (4.x). + NOTE: The source code here is not based on that code from + above github. + +if GAMES_SHIFT + +config GAMES_SHIFT_PROGNAME + string "Program name" + default "shift" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config GAMES_SHIFT_PRIORITY + int "Shift Game task priority" + default 100 + +config GAMES_SHIFT_STACKSIZE + int "Shift Game stack size" + default DEFAULT_TASK_STACKSIZE + +# +# Input Device Selection +# + +choice + prompt "Input Device (Joystick, Gesture Sensor, etc)" + default GAMES_SHIFT_USE_CONSOLEKEY + +config GAMES_SHIFT_USE_CONSOLEKEY + bool "Serial Console as Input" + depends on !INPUT_DJOYSTICK && !SENSORS_APDS9960 + +config GAMES_SHIFT_USE_DJOYSTICK + bool "Discrete Joystick as Input" + depends on INPUT_DJOYSTICK + +config GAMES_SHIFT_USE_GESTURE + bool "Gesture Sensor APDS-9960 as Input" + depends on SENSORS_APDS9960 + +endchoice + +endif diff --git a/games/shift/Make.defs b/games/shift/Make.defs new file mode 100644 index 000000000..0dc269e74 --- /dev/null +++ b/games/shift/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/games/shift/Make.defs +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_GAMES_SHIFT),y) +CONFIGURED_APPS += $(APPDIR)/games/shift +endif diff --git a/games/shift/Makefile b/games/shift/Makefile new file mode 100644 index 000000000..6bda408e9 --- /dev/null +++ b/games/shift/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/games/shift/Makefile +# +# 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 + +# Shift game info + +PROGNAME = $(CONFIG_GAMES_SHIFT_PROGNAME) +PRIORITY = $(CONFIG_GAMES_SHIFT_PRIORITY) +STACKSIZE = $(CONFIG_GAMES_SHIFT_STACKSIZE) +MODULE = $(CONFIG_GAMES_SHIFT) + +# Shift game application + +MAINSRC = shift_main.c + +include $(APPDIR)/Application.mk diff --git a/games/shift/shift_input_console.h b/games/shift/shift_input_console.h new file mode 100644 index 000000000..d2f974d25 --- /dev/null +++ b/games/shift/shift_input_console.h @@ -0,0 +1,133 @@ +/**************************************************************************** + * apps/games/shift/shift_input_console.h + * + * 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 "shift_inputs.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/* Termios functions to have getch on Linux/NuttX */ + +static struct termios g_old; +static struct termios g_new; + +/* Initialize g_new terminal I/O settings */ + +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 */ +} + +/* Restore g_old terminal i/o settings */ + +void reset_termios(void) +{ + tcsetattr(0, TCSANOW, &g_old); +} + +/* Read 1 character - echo defines echo mode */ + +char getch_(int echo) +{ + char ch; + + init_termios(echo); + ch = getchar(); + reset_termios(); + + return ch; +} + +/* Read 1 character without echo getch() function definition. */ + +char getch(void) +{ + return getch_(0); +} + +/**************************************************************************** + * dev_input_init + ****************************************************************************/ + +int dev_input_init(FAR struct input_state_s *dev) +{ + init_termios(0); + + return OK; +} + +/**************************************************************************** + * dev_read_input + ****************************************************************************/ + +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/shift/shift_input_gesture.h b/games/shift/shift_input_gesture.h new file mode 100644 index 000000000..3547b3cf6 --- /dev/null +++ b/games/shift/shift_input_gesture.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * apps/games/shift/shift_input_gesture.h + * + * 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 "shift_inputs.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define APDS9960_DEVNAME "/dev/gest0" + +/**************************************************************************** + * dev_input_init + ****************************************************************************/ + +int dev_input_init(FAR struct input_state_s *dev) +{ + /* Open the gesture sensor APDS9960 */ + + dev->fd_gest = open(APDS9960_DEVNAME, O_RDONLY | O_NONBLOCK); + if (dev->fd_gest < 0) + { + int errcode = errno; + printf("ERROR: Failed to open %s: %d\n", APDS9960_DEVNAME, errcode); + return -ENODEV; + } + + return OK; +} + +/**************************************************************************** + * dev_read_input + ****************************************************************************/ + +int dev_read_input(FAR struct input_state_s *dev) +{ + int nbytes; + uint8_t gest; + + nbytes = read(dev->fd_gest, (void *)&gest, sizeof(gest)); + if (nbytes == sizeof(gest)) + { + dev->dir = gest; + } + else + { + dev->dir = DIR_NONE; + return -EINVAL; + } + + return OK; +} + diff --git a/games/shift/shift_input_joystick.h b/games/shift/shift_input_joystick.h new file mode 100644 index 000000000..4a58217e6 --- /dev/null +++ b/games/shift/shift_input_joystick.h @@ -0,0 +1,166 @@ +/**************************************************************************** + * apps/games/shift/shift_input_joystick.h + * + * 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 "shift_inputs.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define DJOYSTICK_DEVNAME "/dev/djoy0" +#define DJOYSTICK_SIGNO 13 + +/* The set of supported joystick discretes */ + +static djoy_buttonset_t g_djoysupported; + +/* Last sampled discrete set */ + +static djoy_buttonset_t g_djoylast; + +struct djoy_notify_s notify; + +/**************************************************************************** + * dev_input_init + ****************************************************************************/ + +int dev_input_init(FAR struct input_state_s *dev) +{ + int tmp; + int ret; + + /* Open the djoystick device */ + + dev->fd_joy = open(DJOYSTICK_DEVNAME, O_RDONLY); + if (dev->fd_joy < 0) + { + fprintf(stderr, "ERROR: Failed to open %s: %d\n", + DJOYSTICK_DEVNAME, errno); + return -ENODEV; + } + + /* Get the set of supported discretes */ + + ret = ioctl(dev->fd_joy, DJOYIOC_SUPPORTED, + (unsigned long)((uintptr_t)&tmp)); + if (ret < 0) + { + fprintf(stderr, "ERROR: ioctl(DJOYIOC_SUPPORTED) failed: %d\n", errno); + goto errout_with_fd; + } + + g_djoysupported = (djoy_buttonset_t)tmp; + + /* Register to receive a signal on any change in the joystick discretes. */ + + notify.dn_press = g_djoysupported; + notify.dn_release = g_djoysupported; + + notify.dn_event.sigev_notify = SIGEV_SIGNAL; + notify.dn_event.sigev_signo = DJOYSTICK_SIGNO; + + ret = ioctl(dev->fd_joy, DJOYIOC_REGISTER, + (unsigned long)((uintptr_t)¬ify)); + if (ret < 0) + { + fprintf(stderr, "ERROR: ioctl(DJOYIOC_REGISTER) failed: %d\n", errno); + goto errout_with_fd; + } + + /* Ignore the default signal action */ + + signal(DJOYSTICK_SIGNO, SIG_IGN); + + return OK; + +errout_with_fd: + close(dev->fd_joy); + return -ENODEV; +} + +/**************************************************************************** + * dev_read_input + ****************************************************************************/ + +int dev_read_input(FAR struct input_state_s *dev) +{ + struct siginfo value; + sigset_t set; + djoy_buttonset_t newset; + ssize_t nread; + int ret; + + /* Wait for a signal */ + + sigemptyset(&set); + sigaddset(&set, DJOYSTICK_SIGNO); + ret = sigwaitinfo(&set, &value); + if (ret < 0) + { + fprintf(stderr, "ERROR: sigwaitinfo() failed: %d\n", errno); + } + + newset = (djoy_buttonset_t)value.si_value.sival_int; + + g_djoylast = newset; + + /* Read the signal set and compare */ + + nread = read(dev->fd_joy, &newset, sizeof(djoy_buttonset_t)); + if (nread < 0) + { + fprintf(stderr, "ERROR: read() failed: %d\n", errno); + } + else if (nread != sizeof(djoy_buttonset_t)) + { + fprintf(stderr, "ERROR: read() unexpected size: %ld vs %d\n", + (long)nread, sizeof(djoy_buttonset_t)); + } + + g_djoylast = newset; + + switch (newset) + { + case DJOY_UP_BIT: + dev->dir = DIR_UP; + break; + case DJOY_DOWN_BIT: + dev->dir = DIR_DOWN; + break; + case DJOY_LEFT_BIT: + dev->dir = DIR_LEFT; + break; + case DJOY_RIGHT_BIT: + dev->dir = DIR_RIGHT; + break; + default: + dev->dir = DIR_NONE; + } + + return OK; +} diff --git a/games/shift/shift_inputs.h b/games/shift/shift_inputs.h new file mode 100644 index 000000000..c2141d6d3 --- /dev/null +++ b/games/shift/shift_inputs.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/games/shift/shift_inputs.h + * + * 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_SHIFT_USE_CONSOLEKEY + int fd_con; +#endif +#ifdef CONFIG_GAMES_SHIFT_USE_DJOYSTICK + int fd_joy; +#endif +#ifdef CONFIG_GAMES_SHIFT_USE_GESTURE + int fd_gest; +#endif + int dir; /* Direction to move the blocks */ +}; + diff --git a/games/shift/shift_main.c b/games/shift/shift_main.c new file mode 100644 index 000000000..d0906bf58 --- /dev/null +++ b/games/shift/shift_main.c @@ -0,0 +1,927 @@ +/**************************************************************************** + * apps/games/shift/shift_main.c + * + * 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_SHIFT_USE_CONSOLEKEY +#include "shift_input_console.h" +#endif + +#ifdef CONFIG_GAMES_SHIFT_USE_DJOYSTICK +#include "shift_input_joystick.h" +#endif + +#ifdef CONFIG_GAMES_SHIFT_USE_GESTURE +#include "shift_input_gesture.h" +#endif + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define BOARDX_SIZE 6 +#define BOARDY_SIZE 6 + +#define ROW 8 +#define COL 8 + +/* Difficult mode: 4, 5, 6 */ + +#define GAMEMODE 6 + +#define NCOLORS GAMEMODE + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct screen_state_s +{ + int fd_fb; + struct fb_videoinfo_s vinfo; + struct fb_planeinfo_s pinfo; +#ifdef CONFIG_FB_OVERLAY + struct fb_overlayinfo_s oinfo; +#endif + FAR void *fbmem; +}; + +struct game_screen_s +{ + int xres; /* X display resolution */ + int yres; /* Y display resolution */ + int xoff; /* X offset to start drawing */ + int yoff; /* Y offset to start drawing */ + int ncolors; /* Supported number of colors */ + int blklen; /* Size of side of the blocks */ + int steps; /* Steps to slide a block for a now position */ + int stepinc; /* Step increment during the sliding */ + int dir; /* Direction to move the blocks */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char g_default_fbdev[] = "/dev/fb0"; + +/* The edge of the board is invisible to user */ + +int board[ROW][COL] = +{ + {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 1, 1} +}; + +int prev_board[ROW][COL] = +{ + {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 1, 1} +}; + +/* Colors used in the game plus Black */ + +static const uint16_t pallete[NCOLORS + 1] = +{ + RGB16_BLACK, + RGB16_BLUE, + RGB16_GREEN, + RGB16_RED, + RGB16_CYAN, + RGB16_WHITE, + RGB16_GOLD, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void draw_rect(FAR struct screen_state_s *state, + FAR struct fb_area_s *area, int color) +{ + FAR uint16_t *dest; + FAR uint8_t *row; + int x; + int y; + + row = (FAR uint8_t *)state->fbmem + state->pinfo.stride * area->y; + for (y = 0; y < area->h; y++) + { + dest = ((FAR uint16_t *)row) + area->x; + for (x = 0; x < area->w; x++) + { + *dest++ = pallete[color]; + } + + row += state->pinfo.stride; + } +} + +void update_screen(FAR struct screen_state_s *state, + FAR struct fb_area_s *area) +{ + int ret; + +#ifdef CONFIG_FB_UPDATE + ret = ioctl(state->fd_fb, FBIO_UPDATE, + (unsigned long)((uintptr_t)area)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIO_UPDATE) failed: %d\n", + errcode); + } +#endif +} + +/* Draw the user board without the edges */ + +void draw_board(FAR struct screen_state_s *state, + FAR struct fb_area_s *area, + FAR struct game_screen_s *screen) +{ + int row; + int col; + int x; + int y; + int i; + int color; + + /* Clear the framebuffer, but don't update yet */ + + area->x = 0; + area->y = 0; + area->w = screen->xres; + area->h = screen->yres; + + draw_rect(state, area, 0); + + for (i = screen->steps; i > 0; i--) + { + for (row = 1; row <= BOARDX_SIZE; row++) + { + for (col = 1; col <= BOARDY_SIZE; col++) + { + color = board[row][col]; + + x = (col - 1) * screen->blklen + screen->xoff; + y = (row - 1) * screen->blklen + screen->yoff; + + /* Get original width and height of blocks */ + + area->w = screen->blklen; + area->h = screen->blklen; + + /* Only do the sliding for blocks that changed */ + + if (prev_board[row][col] != board[row][col]) + { + if (screen->dir == DIR_NONE) + { + area->x = x; + area->y = y; + } + + if (screen->dir == DIR_UP) + { + area->x = x; + area->y = y + ((i - 1) * screen->stepinc); + } + + if (screen->dir == DIR_DOWN) + { + area->x = x; + area->y = y - ((i - 1) * screen->stepinc); + } + + if (screen->dir == DIR_LEFT) + { + area->x = x + ((i - 1) * screen->stepinc); + area->y = y; + } + + if (screen->dir == DIR_RIGHT) + { + area->x = x - ((i - 1) * screen->stepinc); + area->y = y; + } + } + else + { + area->x = x; + area->y = y; + } + + /* Drawn only part of block if above margin */ + + if (area->x > (screen->xres - 2 * screen->xoff)) + { + area->x = (screen->xres - 2 * screen->xoff) + 1; + area->w = 1; + } + + if (area->x < screen->xoff) + { + area->x = screen->xoff; + } + + if (area->y > (screen->yres - 2 * screen->yoff)) + { + area->y = (screen->yres - 2 * screen->yoff) + 1; + area->h = 1; + } + + if (area->y < screen->yoff) + { + area->y = screen->yoff; + } + + draw_rect(state, area, color); + } + } + + area->x = 0; + area->y = 0; + area->w = screen->xres; + area->h = screen->yres; + + update_screen(state, area); + usleep(100000); + } +} + +/**************************************************************************** + * Name: print_board + * + * Description: + * Draw the board including the user non-visible border for debugging. + ****************************************************************************/ + +#ifdef DEBUG_SHIFT_GAME +void print_board(void) +{ + int row; + int col; + + for (row = 0; row < ROW; row++) + { + printf("+---+---+---+---+---+---+---+---+\n"); + + for (col = 0; col < COL; col++) + { + if (row == 0 || col == 0 || row == ROW - 1 || col == COL - 1) + { + printf("|>%c<", board[row][col] + 48); + } + else + { + printf("| %c ", board[row][col] + 48); + } + } + + printf("|\n"); + } + + printf("+---+---+---+---+---+---+---+---+\n\n\n"); +} +#endif + +/**************************************************************************** + * Name: move_board + * + * Description: + * Move the blocks (represented by numbers) to 'dir' direction (L,R,U,D). + ****************************************************************************/ + +void move_board(int dir) +{ + int row; + int row_ini; + int col; + int col_ini; + int row_dir; + int row_pos; + int col_dir; + int col_pos; + int row_lim; + int col_lim; + + if (dir == DIR_UP) + { + row_ini = 0; + row_dir = 1; + row_pos = 1; + row_lim = BOARDY_SIZE + 1; + + col_ini = 0; + col_dir = 1; + col_pos = 0; + col_lim = BOARDX_SIZE + 1; + } + + if (dir == DIR_DOWN) + { + row_ini = BOARDY_SIZE + 1; + row_dir = -1; + row_pos = -1; + row_lim = 0; + + col_ini = 0; + col_dir = 1; + col_pos = 0; + col_lim = BOARDX_SIZE + 1; + } + + if (dir == DIR_RIGHT) + { + row_ini = 0; + row_dir = 1; + row_pos = 0; + row_lim = BOARDY_SIZE + 1; + + col_ini = BOARDX_SIZE + 1; + col_dir = -1; + col_pos = -1; + col_lim = 0; + } + + if (dir == DIR_LEFT) + { + row_ini = 0; + row_dir = 1; + row_pos = 0; + row_lim = BOARDY_SIZE + 1; + + col_ini = 0; + col_dir = 1; + col_pos = 1; + col_lim = BOARDX_SIZE + 1; + } + + for (row = row_ini; row != row_lim; row += row_dir) + { + for (col = col_ini; col != col_lim; col += col_dir) + { + /* Never move item if we are in the cache border */ + + if ((row != 0 && col != 0) && + (row != 0 && col != BOARDX_SIZE + 1) && + (row != BOARDY_SIZE + 1 && col != 0) && + (row != BOARDY_SIZE + 1 && col != BOARDX_SIZE + 1)) + { + if (board[row][col] == 0) + { + board[row][col] = board[row + row_pos][col + col_pos]; + board[row + row_pos][col + col_pos] = 0; + } + } + } + } +} + +/**************************************************************************** + * Name: fill_edge + * + * Description: + * Fill the invisible border with "colored" blocks + ****************************************************************************/ + +void fill_edge(void) +{ + int i; + + for (i = 0; i <= BOARDX_SIZE + 1; i++) + { + board[0][i] = (random() % GAMEMODE) + 1; + board[BOARDY_SIZE + 1][i] = (random() % GAMEMODE) + 1; + } + + for (i = 0; i <= BOARDY_SIZE + 1; i++) + { + board[i][0] = (random() % GAMEMODE) + 1; + board[i][BOARDY_SIZE + 1] = (random() % GAMEMODE) + 1; + } +} + +/**************************************************************************** + * Name: fill_border + * + * Description: + * Fill the visible border with "colored" blocks + ****************************************************************************/ + +void fill_border(void) +{ + int i; + + for (i = 1; i < BOARDX_SIZE + 1; i++) + { + board[1][i] = (random() % GAMEMODE) + 1; + board[BOARDY_SIZE][i] = (random() % GAMEMODE) + 1; + } + + for (i = 1; i < BOARDY_SIZE + 1; i++) + { + board[i][1] = (random() % GAMEMODE) + 1; + board[i][BOARDY_SIZE] = (random() % GAMEMODE) + 1; + } +} + +/**************************************************************************** + * Name: search_board_squares + * + * Description: + * Search for squares or triples of same colors. + ****************************************************************************/ + +int search_board_squares(void) +{ + int row; + int col; + int found = 0; + + for (row = 1; row < BOARDY_SIZE; row++) + { + for (col = 1; col < BOARDX_SIZE; col++) + { + if (board[row][col] != 0) + { + if (fabs(board[row][col]) == fabs(board[row][col + 1])) + { + /* Square */ + + if (fabs(board[row][col]) == fabs(board[row + 1][col]) && + fabs(board[row][col + 1]) == + fabs(board[row + 1][col + 1])) + { + found = 1; + board[row][col] = board[row][col] > 0 ? + -board[row][col] : board[row][col]; + board[row][col + 1] = board[row][col + 1] > 0 ? + -board[row][col + 1] : + board[row][col + 1]; + board[row + 1][col] = board[row + 1][col] > 0 ? + -board[row + 1][col] : + board[row + 1][col]; + board[row + 1][col + 1] = board[row + 1][col + 1] > 0 ? + -board[row + 1][col + 1] : + board[row + 1][col + 1]; + } + + /* |- */ + + if (fabs(board[row][col]) == fabs(board[row + 1][col])) + { + found = 1; + board[row][col] = board[row][col] > 0 ? + -board[row][col] : + board[row][col]; + board[row][col + 1] = board[row][col + 1] > 0 ? + -board[row][col + 1] : + board[row][col + 1]; + board[row + 1][col] = board[row + 1][col] > 0 ? + -board[row + 1][col] : + board[row + 1][col]; + } + + /* -| */ + + if (fabs(board[row][col + 1]) == + fabs(board[row + 1][col + 1])) + { + found = 1; + board[row][col] = board[row][col] > 0 ? + -board[row][col] : board[row][col]; + board[row][col + 1] = board[row][col + 1] > 0 ? + -board[row][col + 1] : + board[row][col + 1]; + board[row + 1][col + 1] = board[row + 1][col + 1] > 0 ? + -board[row + 1][col + 1] : + board[row + 1][col + 1]; + } + } + + if (fabs(board[row][col]) == fabs(board[row + 1][col])) + { + /* |_ */ + + if (fabs(board[row + 1][col]) == + fabs(board[row + 1][col + 1])) + { + found = 1; + board[row][col] = board[row][col] > 0 ? + -board[row][col] : board[row][col]; + board[row + 1][col] = board[row + 1][col] > 0 ? + -board[row + 1][col] : + board[row + 1][col]; + board[row + 1][col + 1] = board[row + 1][col + 1] > 0 ? + -board[row + 1][col + 1] : + board[row + 1][col + 1]; + } + } + } + + if (board[row + 1][col] != 0) + { + /* _| */ + + if (fabs(board[row + 1][col]) == fabs(board[row + 1][col + 1])) + { + if (fabs(board[row + 1][col + 1]) == + fabs(board[row][col + 1])) + { + found = 1; + board[row][col + 1] = board[row][col + 1] > 0 ? + -board[row][col + 1] : + board[row][col + 1]; + board[row + 1][col] = board[row + 1][col] > 0 ? + -board[row + 1][col] : + board[row + 1][col]; + board[row + 1][col + 1] = board[row + 1][col + 1] > 0 ? + -board[row + 1][col + 1] : + board[row + 1][col + 1]; + } + } + } + } + } + + return found; +} + +/**************************************************************************** + * Name: search_board_squares + * + * Description: + * Search for vertical/horizontal lines of same colours. + ****************************************************************************/ + +int search_board_lines(void) +{ + int row; + int col; + int h = 0; + int hpos = 0; + int hfound = 0; + int v = 0; + int vpos = 0; + int vfound = 0; + int found = 0; + int prev_sym = 0; + + /* Search horizontal lines */ + + for (row = 1; row < BOARDY_SIZE + 1; row++) + { + prev_sym = board[row][1]; + + for (col = 1; col < BOARDX_SIZE; col++) + { + if (board[row][col] != 0) + { + if (fabs(board[row][col]) == fabs(board[row][col + 1])) + { + if (!hfound) + { + hpos = col; + hfound = 1; + prev_sym = board[row][col]; + } + + if (fabs(board[row][col]) == fabs(prev_sym)) + { + h++; + } + } + else if (h < 2) + { + h = 0; + hpos = 1; + hfound = 0; + } + } + } + + if (h >= 2) + { + for (int i = hpos; i <= hpos + h; i++) + { + board[row][i] = board[row][i] > 0 ? + -board[row][i] : board[row][i]; + found = 1; + } + } + + h = 0; + hpos = 1; + hfound = 0; + } + + /* Search vertical lines */ + + for (col = 1; col < BOARDX_SIZE + 1; col++) + { + prev_sym = board[1][col]; + + for (row = 1; row < BOARDY_SIZE; row++) + { + if (board[row][col] != 0) + { + if (fabs(board[row][col]) == fabs(board[row + 1][col])) + { + if (!vfound) + { + vpos = row; + vfound = 1; + prev_sym = board[row][col]; + } + + if (fabs(board[row][col]) == fabs(prev_sym)) + { + v++; + } + } + else if (v < 2) + { + v = 0; + vpos = 1; + vfound = 0; + } + } + } + + if (v >= 2) + { + for (int i = vpos; i <= vpos + v; i++) + { + board[i][col] = board[i][col] > 0 ? + -board[i][col] : board[i][col]; + found = 1; + } + } + + v = 0; + vpos = 1; + vfound = 0; + } + + return found; +} + +/**************************************************************************** + * Name: check_board + * + * Description: + * Verify if there are match lines/squares and remove them + ****************************************************************************/ + +int check_board(void) +{ + int found = 0; + int row; + int col; + + found = search_board_lines(); + found |= search_board_squares(); + + /* Remove all negative values */ + + for (row = 0; row < ROW; row++) + { + for (col = 0; col < COL; col++) + { + if (board[row][col] < 0) + { + board[row][col] = 0; + } + } + } + + return found; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * shift_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *fbdev = g_default_fbdev; + struct game_screen_s screen; + struct input_state_s input; + struct screen_state_s state; + struct fb_area_s area; + int score = 0; + int ret; + + /* Open the framebuffer driver */ + + state.fd_fb = open(fbdev, O_RDWR); + if (state.fd_fb < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: Failed to open %s: %d\n", fbdev, errcode); + return EXIT_FAILURE; + } + + /* Get the characteristics of the framebuffer */ + + ret = ioctl(state.fd_fb, FBIOGET_VIDEOINFO, + (unsigned long)((uintptr_t)&state.vinfo)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_VIDEOINFO) failed: %d\n", + errcode); + close(state.fd_fb); + return EXIT_FAILURE; + } + + printf("VideoInfo:\n"); + printf(" fmt: %u\n", state.vinfo.fmt); + printf(" xres: %u\n", state.vinfo.xres); + printf(" yres: %u\n", state.vinfo.yres); + printf(" nplanes: %u\n", state.vinfo.nplanes); + + /* Setup the screen information */ + + screen.xres = state.vinfo.xres > state.vinfo.yres ? + state.vinfo.yres : state.vinfo.xres; + screen.yres = state.vinfo.yres > state.vinfo.xres ? + state.vinfo.xres : state.vinfo.yres; + screen.xoff = (screen.xres % NCOLORS) / 2; + screen.yoff = (screen.yres % NCOLORS) / 2; + screen.steps = 2; + screen.stepinc = 1; + screen.blklen = (screen.xres / NCOLORS); + screen.ncolors = NCOLORS; + + /* Get the display planeinfo */ + + ret = ioctl(state.fd_fb, FBIOGET_PLANEINFO, + (unsigned long)((uintptr_t)&state.pinfo)); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", + errcode); + close(state.fd_fb); + return EXIT_FAILURE; + } + + printf("PlaneInfo (plane 0):\n"); + printf(" fbmem: %p\n", state.pinfo.fbmem); + printf(" fblen: %lu\n", (unsigned long)state.pinfo.fblen); + printf(" stride: %u\n", state.pinfo.stride); + printf(" display: %u\n", state.pinfo.display); + printf(" bpp: %u\n", state.pinfo.bpp); + + state.fbmem = mmap(NULL, state.pinfo.fblen, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FILE, state.fd_fb, 0); + if (state.fbmem == MAP_FAILED) + { + int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", + errcode); + close(state.fd_fb); + return EXIT_FAILURE; + } + + printf("Mapped FB: %p\n", state.fbmem); + + /* Fill the edge with random values */ + + fill_edge(); + fill_border(); + + /* Draw the empty board to user */ + + draw_board(&state, &area, &screen); + + dev_input_init(&input); + + while (1) + { + ret = dev_read_input(&input); + + screen.dir = input.dir; + +#ifdef DEBUG_SHIFT_GAME + printf("Before moving:\n"); + print_board(); + usleep(2000000); +#endif + + /* Save a copy of the board before moving to new position */ + + memcpy(prev_board, board, (sizeof(int) * ROW * COL)); + + move_board(screen.dir); + + draw_board(&state, &area, &screen); + +#ifdef DEBUG_SHIFT_GAME + printf("After moving:\n"); + print_board(); + usleep(1000000); +#endif + + /* Save a copy of the board after moving the pieces */ + + memcpy(prev_board, board, (sizeof(int) * ROW * COL)); + + screen.dir = DIR_NONE; + + /* If we found bricks with same colors */ + + if (check_board() == 1) + { + int i; + + /* Lets do a blinking effect */ + + for (i = 0; i < 3; i++) + { + /* Draw the board with the pieces */ + + memcpy(board, prev_board, (sizeof(int) * ROW * COL)); + + draw_board(&state, &area, &screen); + usleep(100000); + + /* Draw the board without the pieces */ + + check_board(); + draw_board(&state, &area, &screen); + usleep(100000); + } + + usleep(500000); + + score += 100; + } + +#ifdef DEBUG_SHIFT_GAME + printf("After checking:\n"); + print_board(); + usleep(1000000); +#endif + + usleep(1000000); + + /* Add random pieces in the not visible border */ + + fill_edge(); + } + + return 0; +}