summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorAntoine Cellerier <dionoea@videolan.org>2007-06-30 20:04:42 +0000
committerAntoine Cellerier <dionoea@videolan.org>2007-06-30 20:04:42 +0000
commitb4b34f0c0723c29f368d7793889ef92bc6404a52 (patch)
tree0dfeb4a5b0a2a54f85a0deb227baafed92ae6549 /apps
parent9789acc3e04a8cd23fe0a2359a0fb8801ee7255e (diff)
downloadrockbox-b4b34f0c0723c29f368d7793889ef92bc6404a52.zip
rockbox-b4b34f0c0723c29f368d7793889ef92bc6404a52.tar.gz
rockbox-b4b34f0c0723c29f368d7793889ef92bc6404a52.tar.bz2
rockbox-b4b34f0c0723c29f368d7793889ef92bc6404a52.tar.xz
FS #6509 - "Plugin for playing reversi game" by Alexander Levin + changes by me to make it compile due to the menu api change and compile for e200.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13745 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/reversi/Makefile111
-rw-r--r--apps/plugins/reversi/SOURCES3
-rw-r--r--apps/plugins/reversi/reversi-game.c370
-rw-r--r--apps/plugins/reversi/reversi-game.h75
-rw-r--r--apps/plugins/reversi/reversi-gui.c669
-rw-r--r--apps/plugins/reversi/reversi-gui.h117
-rw-r--r--apps/plugins/reversi/reversi-strategy.c59
-rw-r--r--apps/plugins/reversi/reversi-strategy.h44
9 files changed, 1449 insertions, 0 deletions
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 5ac85a3..6665f41 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -12,6 +12,7 @@ rockboy
#ifdef HAVE_LCD_BITMAP
chessbox
sudoku
+reversi
#endif
/* For all 2bpp and colour targets */
diff --git a/apps/plugins/reversi/Makefile b/apps/plugins/reversi/Makefile
new file mode 100644
index 0000000..b008738
--- /dev/null
+++ b/apps/plugins/reversi/Makefile
@@ -0,0 +1,111 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $$Id: $$
+#
+
+INCLUDES = -I$(APPSDIR) -I.. -I. $(TARGET_INC) -I$(FIRMDIR)/include -I$(FIRMDIR)/export \
+ -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(OUTDIR) -I$(BUILDDIR) \
+ -I$(BUILDDIR)/pluginbitmaps
+CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \
+ -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN
+
+ifdef APPEXTRA
+ INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA)))
+endif
+
+LINKFILE := $(OBJDIR)/link.lds
+DEPFILE = $(OBJDIR)/dep-reversi
+
+# This sets up 'SRC' based on the files mentioned in SOURCES
+include $(TOOLSDIR)/makesrc.inc
+
+SOURCES = $(SRC)
+OBJS := $(SRC:%.c=$(OBJDIR)/%.o)
+DIRS = .
+
+ifndef SIMVER
+ LDS := ../plugin.lds
+ OUTPUT = $(OUTDIR)/reversi.rock
+else ## simulators
+ OUTPUT = $(OUTDIR)/reversi.rock
+endif
+
+all: $(OUTPUT)
+
+ifndef SIMVER
+$(OBJDIR)/reversi.elf: $(OBJS) $(LINKFILE) $(BITMAPLIBS)
+ $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lgcc \
+ $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/reversi.map
+
+$(OUTPUT): $(OBJDIR)/reversi.elf
+ $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@
+else
+
+ifeq ($(SIMVER), x11)
+###################################################
+# This is the X11 simulator version
+
+$(OUTPUT): $(OBJS)
+ $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@
+ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
+# 'x' must be kept or you'll have "Win32 error 5"
+# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
+# #define ERROR_ACCESS_DENIED 5L
+else
+ @chmod -x $@
+endif
+
+else # end of x11-simulator
+ifeq ($(SIMVER), sdl)
+###################################################
+# This is the SDL simulator version
+
+$(OUTPUT): $(OBJS)
+ $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@
+ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
+# 'x' must be kept or you'll have "Win32 error 5"
+# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
+# #define ERROR_ACCESS_DENIED 5L
+else
+ @chmod -x $@
+endif
+
+else # end of sdl-simulator
+###################################################
+# This is the win32 simulator version
+DLLTOOLFLAGS = --export-all
+DLLWRAPFLAGS = -s --entry _DllMain@12 --target=i386-mingw32 -mno-cygwin
+
+$(OUTPUT): $(OBJS)
+ $(call PRINTS,DLL $(@F))$(DLLTOOL) $(DLLTOOLFLAGS) -z $(OBJDIR)/$*.def $(OBJS)
+ $(SILENT)$(DLLWRAP) $(DLLWRAPFLAGS) --def $(OBJDIR)/$*.def $(OBJS) \
+ $(BUILDDIR)/libplugin.a $(BITMAPLIBS) -o $@
+ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
+# 'x' must be kept or you'll have "Win32 error 5"
+# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
+# #define ERROR_ACCESS_DENIED 5L
+else
+ @chmod -x $@
+endif
+endif # end of win32-simulator
+endif
+endif # end of simulator section
+
+
+include $(TOOLSDIR)/make.inc
+
+# MEMORYSIZE should be passed on to this makefile with the chosen memory size
+# given in number of MB
+$(LINKFILE): $(LDS)
+ $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \
+ $(DEFINES) -E -P - >$@
+
+clean:
+ $(call PRINTS,cleaning reversi)rm -rf $(OBJDIR)/reversi
+ $(SILENT)rm -f $(OBJDIR)/reversi.* $(DEPFILE)
+
+-include $(DEPFILE)
diff --git a/apps/plugins/reversi/SOURCES b/apps/plugins/reversi/SOURCES
new file mode 100644
index 0000000..342e4d0
--- /dev/null
+++ b/apps/plugins/reversi/SOURCES
@@ -0,0 +1,3 @@
+reversi-game.c
+reversi-strategy.c
+reversi-gui.c
diff --git a/apps/plugins/reversi/reversi-game.c b/apps/plugins/reversi/reversi-game.c
new file mode 100644
index 0000000..80893b7
--- /dev/null
+++ b/apps/plugins/reversi/reversi-game.c
@@ -0,0 +1,370 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+ * Reversi. Code is heavily based on othello code by Claudio Clemens which is
+ * copyright (c) 2003-2006 Claudio Clemens <asturio at gmx dot net> and is
+ * released under the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include "reversi-game.h"
+
+/*
+ * Constants for directions. The values are chosen so that
+ * they can be bit combined.
+ */
+#define DIR_UP 1 /* UP */
+#define DIR_DO 2 /* DOWN */
+#define DIR_LE 4 /* LEFT */
+#define DIR_RI 8 /* RIGHT */
+#define DIR_UL 16 /* UP LEFT */
+#define DIR_UR 32 /* UP RIGHT */
+#define DIR_DL 64 /* DOWN LEFT */
+#define DIR_DR 128 /* DOWN RIGHT */
+
+/* Array of directions for easy iteration through all of them */
+static int DIRECTIONS[] =
+ {DIR_UP, DIR_DO, DIR_LE, DIR_RI, DIR_UL, DIR_UR, DIR_DL, DIR_DR};
+#define NUM_OF_DIRECTIONS 8
+
+
+/* Initializes a reversi game */
+void reversi_init_game(reversi_board_t *game) {
+ int r, c;
+ for (r = 0; r < BOARD_SIZE; r++) {
+ for (c = 0; c < BOARD_SIZE; c++) {
+ game->board[r][c] = FREE;
+ }
+ }
+ game->board[3][3] = WHITE;
+ game->board[4][4] = WHITE;
+ game->board[3][4] = BLACK;
+ game->board[4][3] = BLACK;
+
+ /* Invalidate the history */
+ c = sizeof(game->history) / sizeof(game->history[0]);
+ for (r = 0; r < c; r++) {
+ game->history[r] = MOVE_INVALID;
+ }
+}
+
+
+/* Returns the 'flipped' color, e.g. WHITE for BLACK and vice versa */
+int reversi_flipped_color(const int color) {
+ switch (color) {
+ case WHITE:
+ return BLACK;
+
+ case BLACK:
+ return WHITE;
+
+ default:
+ return FREE;
+ }
+}
+
+
+/* Counts and returns the number of occupied cells on the board.
+ * If white_count and/or black_count is not null, the number of
+ * white/black stones is placed there. */
+int reversi_count_occupied_cells(const reversi_board_t *game,
+ int *white_count, int *black_count) {
+ int w_cnt, b_cnt, r, c;
+ w_cnt = b_cnt = 0;
+ for (r = 0; r < BOARD_SIZE; r++) {
+ for (c = 0; c < BOARD_SIZE; c++) {
+ if (game->board[r][c] == WHITE) {
+ w_cnt++;
+ } else if (game->board[r][c] == BLACK) {
+ b_cnt++;
+ }
+ }
+ }
+ if (white_count != NULL) {
+ *white_count = w_cnt;
+ }
+ if (black_count != NULL) {
+ *black_count = b_cnt;
+ }
+ return w_cnt + b_cnt;
+}
+
+
+/* Returns the number of free cells on the board */
+static int reversi_count_free_cells(const reversi_board_t *game) {
+ int cnt;
+ cnt = reversi_count_occupied_cells(game, NULL, NULL);
+ return BOARD_SIZE*BOARD_SIZE - cnt;
+}
+
+
+/* Checks whether the game is finished. That means that nobody
+ * can make a move. Note that the implementation is not correct
+ * as a game may be finished even if there are free cells
+ */
+bool reversi_game_is_finished(const reversi_board_t *game) {
+ return (reversi_count_free_cells(game) == 0);
+}
+
+
+/* Finds out who should place the next stone. It's the partner
+ * of the last move or WHITE if the game is just started.
+ *
+ * Returns WHITE or BLACK.
+ */
+int reversi_get_turn(const reversi_board_t *game) {
+ int moves;
+ moves = reversi_count_moves(game);
+ if (moves == 0) {
+ return WHITE;
+ } else {
+ return reversi_flipped_color(MOVE_PLAYER(game->history[moves-1]));
+ }
+}
+
+
+/* Returns the total number of moves made so far */
+int reversi_count_moves(const reversi_board_t *game) {
+ int cnt;
+ cnt = reversi_count_occupied_cells(game, NULL, NULL);
+ return cnt - INIT_STONES;
+}
+
+
+/* Returns the number of moves made by the specified
+ * player (WHITE or BLACK) so far
+ */
+static int reversi_count_player_moves(const reversi_board_t *game,
+ const int player) {
+ int moves, cnt, i;
+ moves = reversi_count_moves(game);
+ cnt = 0;
+ for (i = 0; i < moves; i++) {
+ if (MOVE_PLAYER(game->history[i]) == player) {
+ cnt++;
+ }
+ }
+ return cnt;
+}
+
+
+/* Returns the number of moves made by WHITE so far */
+int reversi_count_white_moves(const reversi_board_t *game) {
+ return reversi_count_player_moves(game, WHITE);
+}
+
+
+/* Returns the number of moves made by BLACK so far */
+int reversi_count_black_moves(const reversi_board_t *game) {
+ return reversi_count_player_moves(game, BLACK);
+}
+
+
+/* Checks whether the specified position is on the board
+ * (and not beyond)
+ */
+static bool reversi_is_position_on_board(const int row, const int col) {
+ return (row >= 0) && (row < BOARD_SIZE) &&
+ (col >= 0) && (col < BOARD_SIZE);
+}
+
+
+/* Returns the delta for row to move in the specified direction */
+static int reversi_row_delta(const int direction) {
+ switch (direction) {
+ case DIR_UP:
+ case DIR_UL:
+ case DIR_UR:
+ return -1;
+
+ case DIR_DO:
+ case DIR_DL:
+ case DIR_DR:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+/* Returns the delta for column to move in the specified direction */
+static int reversi_column_delta(const int direction) {
+ switch (direction) {
+ case DIR_LE:
+ case DIR_UL:
+ case DIR_DL:
+ return -1;
+
+ case DIR_RI:
+ case DIR_UR:
+ case DIR_DR:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+/* Checks if some stones would be captured in the specified direction
+ * if a stone were placed in the specified cell by the specified player.
+ *
+ * Returns 0 if no stones would be captured or 'direction' otherwise
+ */
+static int reversi_is_valid_direction(const reversi_board_t *game,
+ const int row, const int col, const int player, const int direction) {
+ int row_delta, col_delta, r, c;
+ int other_color;
+ int flip_cnt; /* Number of stones that would be flipped */
+
+ row_delta = reversi_row_delta(direction);
+ col_delta = reversi_column_delta(direction);
+ other_color = reversi_flipped_color(player);
+
+ r = row + row_delta;
+ c = col + col_delta;
+
+ flip_cnt = 0;
+ while (reversi_is_position_on_board(r, c) &&
+ (game->board[r][c] == other_color)) {
+ r += row_delta;
+ c += col_delta;
+ flip_cnt++;
+ }
+
+ if ((flip_cnt > 0) && reversi_is_position_on_board(r, c) &&
+ (game->board[r][c] == player)) {
+ return direction;
+ } else {
+ return 0;
+ }
+}
+
+
+/* Checks whether the move at the specified position would be valid.
+ * Params:
+ * - game: current state of the game
+ * - row: 0-based row number of the move in question
+ * - col: 0-based column number of the move in question
+ * - player: who is about to make the move (WHITE/BLACK)
+ *
+ * Checks if the place is empty, the coordinates are legal,
+ * and some stones can be captured.
+ *
+ * Returns 0 if the move is not valid or, otherwise, the or'd
+ * directions in which stones would be captured.
+ */
+static int reversi_is_valid_move(const reversi_board_t *game,
+ const int row, const int col, const int player) {
+ int dirs, i;
+ dirs = 0;
+
+ /* Check if coordinates are legal */
+ if (!reversi_is_position_on_board(row, col)) {
+ return dirs;
+ }
+
+ /* Check if the place is free */
+ if (game->board[row][col] != FREE) {
+ return dirs;
+ }
+
+ /* Check the directions of capture */
+ for (i = 0; i < NUM_OF_DIRECTIONS; i++) {
+ dirs |= reversi_is_valid_direction(game, row, col, player, DIRECTIONS[i]);
+ }
+
+ return dirs;
+}
+
+
+/* Flips the stones in the specified direction after the specified
+ * player has placed a stone in the specified cell. The move is
+ * assumed to be valid.
+ *
+ * Returns the number of flipped stones in that direction
+ */
+static int reversi_flip_stones(reversi_board_t *game,
+ const int row, const int col, const int player, const int direction) {
+ int row_delta, col_delta, r, c;
+ int other_color;
+ int flip_cnt; /* Number of stones flipped */
+
+ row_delta = reversi_row_delta(direction);
+ col_delta = reversi_column_delta(direction);
+ other_color = reversi_flipped_color(player);
+
+ r = row + row_delta;
+ c = col + col_delta;
+
+ flip_cnt = 0;
+ while (reversi_is_position_on_board(r, c) &&
+ (game->board[r][c] == other_color)) {
+ game->board[r][c] = player;
+ r += row_delta;
+ c += col_delta;
+ flip_cnt++;
+ }
+
+ return flip_cnt;
+}
+
+
+/* Tries to make a move (place a stone) at the specified position.
+ * If the move is valid the board is changed. Otherwise nothing happens.
+ *
+ * Params:
+ * - game: current state of the game
+ * - row: 0-based row number of the move to make
+ * - col: 0-based column number of the move to make
+ * - player: who makes the move (WHITE/BLACK)
+ *
+ * Returns the number of flipped (captured) stones (>0) iff the move
+ * was valid or 0 if the move was not valid. Note that in the case of
+ * a valid move, the stone itself is not counted.
+ */
+int reversi_make_move(reversi_board_t *game,
+ const int row, const int col, const int player) {
+ int dirs, cnt, i;
+
+ dirs = reversi_is_valid_move(game, row, col, player);
+ if (dirs == 0) {
+ return 0;
+ }
+
+ /* Place the stone into the cell */
+ game->board[row][col] = player;
+
+ /* Capture stones in all possible directions */
+ cnt = 0;
+ for (i = 0; i < NUM_OF_DIRECTIONS; i++) {
+ if (dirs & DIRECTIONS[i]) {
+ cnt += reversi_flip_stones(game, row, col, player, DIRECTIONS[i]);
+ }
+ }
+
+ /* Remember the move */
+ i = reversi_count_moves(game);
+ game->history[i-1] = MAKE_MOVE(row, col, player);
+
+ return cnt;
+}
diff --git a/apps/plugins/reversi/reversi-game.h b/apps/plugins/reversi/reversi-game.h
new file mode 100644
index 0000000..a7d0329
--- /dev/null
+++ b/apps/plugins/reversi/reversi-game.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _REVERSI_GAME_H
+#define _REVERSI_GAME_H
+
+#include <stdbool.h>
+
+#define WHITE 1 /* WHITE constant, it always plays first (as in chess) */
+#define BLACK 2 /* BLACK constant */
+#define FREE 0 /* Free place constant */
+
+#define BOARD_SIZE 8
+#define INIT_STONES 4
+
+/* Description of a move. A move is stored as a byte in the following format:
+ * - bit 7 : 0 for valid entries (i.e. those containing a move info,
+ * 1 for invalid entries
+ * - bits 6..4: row
+ * - bit 3 : 0 if it's white move, 1 if it's black move
+ * - bits 2..0: column
+ */
+typedef unsigned char move_t;
+
+#define MOVE_ROW(h) (((h) >> 4) & 0x7)
+#define MOVE_COL(h) ((h) & 0x7)
+#define MOVE_PLAYER(h) (((h) & 0x8) ? BLACK : WHITE)
+#define MAKE_MOVE(r,c,player) ( ((r)<<4) | ((c)&0x7) | \
+ ((player) == WHITE ? 0 : 0x8) )
+#define MOVE_INVALID 0x80
+
+
+/* State of a board */
+typedef struct _reversi_board_t {
+ /* The current state of the game (BLACK/WHITE/FREE) */
+ char board[BOARD_SIZE][BOARD_SIZE];
+
+ /* Game history. First move (mostly, but not necessarily, black) is stored
+ * in history[0], second move (mostly, but not necessarily, white) is
+ * stored in history[1] etc.
+ */
+ move_t history[BOARD_SIZE*BOARD_SIZE - INIT_STONES];
+} reversi_board_t;
+
+
+void reversi_init_game(reversi_board_t *game);
+int reversi_flipped_color(const int color);
+bool reversi_game_is_finished(const reversi_board_t *game);
+int reversi_get_turn(const reversi_board_t *game);
+int reversi_count_occupied_cells(const reversi_board_t *game,
+ int *white_count, int *black_count);
+int reversi_count_moves(const reversi_board_t *game);
+int reversi_count_white_moves(const reversi_board_t *game);
+int reversi_count_black_moves(const reversi_board_t *game);
+int reversi_make_move(reversi_board_t *game, const int row,
+ const int col, const int player);
+
+
+#endif
diff --git a/apps/plugins/reversi/reversi-gui.c b/apps/plugins/reversi/reversi-gui.c
new file mode 100644
index 0000000..e543563
--- /dev/null
+++ b/apps/plugins/reversi/reversi-gui.c
@@ -0,0 +1,669 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+GUI part of reversi. Code is inspired by sudoku code by Dave Chapman
+which is copyright (c) 2005 Dave Chapman and is released under the
+GNU General Public License.
+
+
+User instructions
+-----------------
+
+Use the arrow keys to move cursor, and press TOGGLE to place a stone.
+
+At any time during the game, press MENU to bring up the game menu with
+further options:
+
+ - Save
+ - Reload
+ - Clear
+
+*/
+
+#include "plugin.h"
+
+#ifdef HAVE_LCD_BITMAP
+
+#include "reversi-game.h"
+#include "reversi-strategy.h"
+#include "reversi-gui.h"
+
+#include "../lib/oldmenuapi.h"
+
+PLUGIN_HEADER
+
+/* The global api struct pointer. While not strictly necessary,
+ it's nice not to have to pass the api pointer in all function
+ calls in the plugin */
+static struct plugin_api* rb;
+
+/* Thickness of the grid lines */
+#define LINE_THCK 1
+
+#if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout */
+
+#if (LCD_HEIGHT==64) && (LCD_WIDTH==112)
+/* Archos Recorders and Ondios - 112x64, 8 cells @ 8x6 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 8
+#define CELL_HEIGHT 6
+#define SMALL_BOARD
+
+#elif (LCD_HEIGHT==110) && (LCD_WIDTH==138)
+/* iPod Mini - 138x110, 8 cells @ 10x10 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 10
+#define CELL_HEIGHT 10
+
+#elif (LCD_HEIGHT==128) && (LCD_WIDTH==128)
+/* iriver H10 5-6GB - 128x128, 8 cells @ 10x10 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 10
+#define CELL_HEIGHT 10
+
+#elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \
+ ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
+/* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
+/* iPod Nano - 176x132, 8 cells @ 12x12 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 12
+#define CELL_HEIGHT 12
+
+#elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220)) || \
+ ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
+/* Iriver h300, iPod Color/Photo - 220x176, 8 cells @ 16x16 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 16
+#define CELL_HEIGHT 16
+
+#elif (LCD_HEIGHT>=240) && (LCD_WIDTH>=320)
+/* iPod Video - 320x240, 8 cells @ 24x24 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 24
+#define CELL_HEIGHT 24
+
+#else
+ #error REVERSI: Unsupported LCD size
+#endif
+
+#else /* Vertical layout */
+#define VERTICAL_LAYOUT
+
+#if (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
+/* Gigabeat - 240x320, 8 cells @ 24x24 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 24
+#define CELL_HEIGHT 24
+
+#elif (LCD_HEIGHT>=220) && (LCD_WIDTH>=176)
+/* e200 - 176x220, 8 cells @ 12x12 with 9 border lines */
+
+/* Internal dimensions of a cell */
+#define CELL_WIDTH 18
+#define CELL_HEIGHT 18
+
+#else
+ #error REVERSI: Unsupported LCD size
+#endif
+
+#endif /* Layout */
+
+
+/* Where the board begins */
+#define XOFS 4
+#define YOFS 4
+
+/* Total width and height of the board without enclosing box */
+#define BOARD_WIDTH (CELL_WIDTH*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1))
+#define BOARD_HEIGHT (CELL_HEIGHT*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1))
+
+/* Thickness of the white cells' lines */
+#if (CELL_WIDTH >= 15) && (CELL_HEIGHT >= 15)
+#define CELL_LINE_THICKNESS 2
+#else
+#define CELL_LINE_THICKNESS 1
+#endif
+
+/* Margins within a cell */
+#if (CELL_WIDTH >= 10) && (CELL_HEIGHT >= 10)
+#define STONE_MARGIN 2
+#else
+#define STONE_MARGIN 1
+#endif
+
+#define CURSOR_MARGIN (STONE_MARGIN + CELL_LINE_THICKNESS)
+
+/* Upper left corner of a cell */
+#define CELL_X(c) (XOFS + (c)*CELL_WIDTH + ((c)+1)*LINE_THCK)
+#define CELL_Y(r) (YOFS + (r)*CELL_HEIGHT + ((r)+1)*LINE_THCK)
+
+
+#ifdef VERTICAL_LAYOUT
+#define LEGEND_X(lc) (CELL_X(lc))
+#define LEGEND_Y(lr) (CELL_Y(BOARD_SIZE+(lr)) + CELL_HEIGHT/2)
+#else
+#define LEGEND_X(lc) (CELL_X(BOARD_SIZE+(lc)) + CELL_WIDTH/2)
+#define LEGEND_Y(lr) (CELL_Y(lr))
+#endif
+
+
+/* Board state */
+static reversi_board_t game;
+
+/* --- Setting values --- */
+
+/* Playing strategies used by white and black players */
+const game_strategy_t *white_strategy;
+const game_strategy_t *black_strategy;
+
+/* Cursor position */
+static int cur_row, cur_col;
+
+/* Color for the next move (BLACK/WHITE) */
+static int cur_player;
+
+/* Active cursor wrapping mode */
+static cursor_wrap_mode_t cursor_wrap_mode;
+
+static bool quit_plugin;
+
+
+/* Initialises the state of the game (starts a new game) */
+static void reversi_gui_init(void) {
+ reversi_init_game(&game);
+ white_strategy = &strategy_human;
+ black_strategy = &strategy_human;
+ cur_player = BLACK;
+
+ /* Place the cursor so that WHITE can make a move */
+ cur_row = 2;
+ cur_col = 3;
+}
+
+
+/* Draws the cursor in the specified cell. Cursor is drawn in the complement
+ * mode, i.e. drawing it twice will result in no changes on the screen.
+ */
+static void reversi_gui_display_cursor(int row, int col) {
+ int old_mode, x, y;
+ old_mode = rb->lcd_get_drawmode();
+ x = CELL_X(col);
+ y = CELL_Y(row);
+
+ rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
+ rb->lcd_drawline(x+CURSOR_MARGIN, y+CURSOR_MARGIN,
+ x+CELL_WIDTH-CURSOR_MARGIN-1, y+CELL_HEIGHT-CURSOR_MARGIN-1);
+ rb->lcd_drawline(x+CURSOR_MARGIN, y+CELL_HEIGHT-CURSOR_MARGIN-1,
+ x+CELL_WIDTH-CURSOR_MARGIN-1, y+CURSOR_MARGIN);
+
+ /* Draw the shadows */
+ rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS-3);
+ rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS+BOARD_HEIGHT+2);
+ rb->lcd_vline(XOFS-3, y, y+CELL_HEIGHT-1);
+ rb->lcd_vline(XOFS+BOARD_WIDTH+2, y, y+CELL_HEIGHT-1);
+
+ rb->lcd_set_drawmode(old_mode);
+ rb->lcd_update();
+}
+
+
+/* Draws the cell of the specified color (WHITE/BLACK) assuming that
+ * the upper left corner of the cell is at (x, y) */
+static void reversi_gui_draw_cell(int x, int y, int color) {
+ int i;
+ if (color == WHITE) {
+ for (i = 0; i < CELL_LINE_THICKNESS; i++) {
+ rb->lcd_drawrect(x+STONE_MARGIN+i, y+STONE_MARGIN+i,
+ CELL_WIDTH-2*(STONE_MARGIN+i), CELL_HEIGHT-2*(STONE_MARGIN+i));
+ }
+ } else if (color == BLACK) {
+ rb->lcd_fillrect(x+STONE_MARGIN, y+STONE_MARGIN,
+ CELL_WIDTH-2*STONE_MARGIN, CELL_HEIGHT-2*STONE_MARGIN);
+ } else {
+ /* Cell is free -> nothing to do */
+ }
+}
+
+
+/* Draws the complete screen */
+static void reversi_gui_display_board(void) {
+ int x, y, r, c, x_width, x_height;
+ char buf[8];
+
+ /* Clear the display buffer */
+ rb->lcd_clear_display();
+ rb->lcd_set_drawmode(DRMODE_FG);
+
+ /* Thicker board box */
+ rb->lcd_drawrect(XOFS-1, YOFS-1, BOARD_WIDTH+2, BOARD_HEIGHT+2);
+
+ /* Draw the gridlines */
+ for (r=0, x=XOFS, y=YOFS; r<=BOARD_SIZE;
+ r++, x+=CELL_WIDTH+LINE_THCK, y+=CELL_HEIGHT+LINE_THCK) {
+ rb->lcd_hline(XOFS, XOFS+BOARD_WIDTH-1, y);
+ rb->lcd_vline(x, YOFS, YOFS+BOARD_HEIGHT-1);
+ }
+
+ /* Draw the stones. This is not the most efficient way but more readable */
+ for (r=0; r<BOARD_SIZE; r++) {
+ y = CELL_Y(r);
+ for (c=0; c<BOARD_SIZE; c++) {
+ x = CELL_X(c);
+ reversi_gui_draw_cell(x, y, game.board[r][c]);
+ }
+ }
+
+ /* Draw the cursor */
+ reversi_gui_display_cursor(cur_row, cur_col);
+
+ /* Draw the current score */
+ reversi_count_occupied_cells(&game, &r, &c);
+ rb->lcd_getstringsize("x", &x_width, &x_height);
+
+ x = LEGEND_X(0);
+ y = LEGEND_Y(0);
+ reversi_gui_draw_cell(x, y, BLACK);
+ rb->snprintf(buf, sizeof(buf), "%d", c);
+ y += (CELL_HEIGHT-x_height) / 2;
+ rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf);
+
+ y = LEGEND_Y(1);
+ reversi_gui_draw_cell(x, y, WHITE);
+ rb->snprintf(buf, sizeof(buf), "%d", r);
+ y += (CELL_HEIGHT-x_height) / 2;
+ rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf);
+
+ /* Draw the box around the current player */
+ r = (cur_player == BLACK ? 0 : 1);
+ y = LEGEND_Y(r);
+ rb->lcd_drawrect(x-1, y-1, CELL_WIDTH+2, CELL_HEIGHT+2);
+
+ /* Update the screen */
+ rb->lcd_update();
+}
+
+
+/*
+ * Menu related stuff
+ */
+
+/* Menu entries and the corresponding values for cursor wrap mode */
+#define MENU_TEXT_WRAP_MODE "Cursor wrap mode"
+static const struct opt_items cursor_wrap_mode_settings[] = {
+ { "Flat board", NULL },
+ { "Sphere", NULL },
+ { "Torus", NULL },
+};
+static const cursor_wrap_mode_t cursor_wrap_mode_values[3] = {
+ WRAP_FLAT, WRAP_SPHERE, WRAP_TORUS };
+
+
+/* Menu entries and the corresponding values for available strategies */
+#define MENU_TEXT_STRAT_WHITE "Strategy for white"
+#define MENU_TEXT_STRAT_BLACK "Strategy for black"
+
+static struct opt_items strategy_settings[] = {
+ { "Human", NULL },
+ { "Silly robot", NULL },
+ { "Smart robot", NULL },
+};
+static const game_strategy_t * const strategy_values[] = {
+ &strategy_human, &strategy_novice, &strategy_expert };
+
+
+/* Sets the strategy for the specified player. 'player' is the
+ pointer to the player to set the strategy for (actually,
+ either white_strategy or black_strategy). propmpt is the
+ text to show as the prompt in the menu */
+static bool reversi_gui_choose_strategy(
+ const game_strategy_t **player, const char *prompt) {
+ int index = 0, i;
+ int num_items = sizeof(strategy_settings)/sizeof(strategy_settings[0]);
+ bool result;
+
+ for (i = 0; i < num_items; i++) {
+ if ((*player) == strategy_values[i]) {
+ index = i;
+ break;
+ }
+ }
+ result = rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
+ (*player) = strategy_values[index];
+
+ return result;
+}
+
+
+/* Returns true iff USB ws connected while in the menu */
+static bool reversi_gui_menu(void) {
+ int m, index, num_items, i;
+ int result;
+
+ static const struct menu_item items[] = {
+ { "Start new game", NULL },
+ { "Pass the move", NULL },
+ { MENU_TEXT_STRAT_BLACK, NULL },
+ { MENU_TEXT_STRAT_WHITE, NULL },
+ { MENU_TEXT_WRAP_MODE, NULL },
+ { "Quit", NULL },
+ };
+
+ m = menu_init(rb, items, sizeof(items) / sizeof(*items),
+ NULL, NULL, NULL, NULL);
+
+ result = menu_show(m);
+
+ switch (result) {
+ case 0: /* Start a new game */
+ reversi_gui_init();
+ break;
+
+ case 1: /* Pass the move to the partner */
+ cur_player = reversi_flipped_color(cur_player);
+ break;
+
+ case 2: /* Strategy for black */
+ reversi_gui_choose_strategy(&black_strategy, MENU_TEXT_STRAT_BLACK);
+ break;
+
+ case 3: /* Strategy for white */
+ reversi_gui_choose_strategy(&white_strategy, MENU_TEXT_STRAT_WHITE);
+ break;
+
+ case 4: /* Cursor wrap mode */
+ num_items = sizeof(cursor_wrap_mode_values)/sizeof(cursor_wrap_mode_values[0]);
+ index = 0;
+ for (i = 0; i < num_items; i++) {
+ if (cursor_wrap_mode == cursor_wrap_mode_values[i]) {
+ index = i;
+ break;
+ }
+ }
+ rb->set_option(MENU_TEXT_WRAP_MODE, &index, INT,
+ cursor_wrap_mode_settings, 3, NULL);
+ cursor_wrap_mode = cursor_wrap_mode_values[index];
+ break;
+
+ case 5: /* Quit */
+ quit_plugin = true;
+ break;
+ }
+
+ menu_exit(m);
+
+ return (result == MENU_ATTACHED_USB);
+}
+
+
+/* Calculates the new cursor position if the user wants to move it
+ * vertically as specified by delta. Current wrap mode is respected.
+ * The cursor is not actually moved.
+ *
+ * Returns true iff the cursor would be really moved. In any case, the
+ * new cursor position is stored in (new_row, new_col).
+ */
+static bool reversi_gui_cursor_pos_vmove(int row_delta, int *new_row, int *new_col) {
+ *new_row = cur_row + row_delta;
+ *new_col = cur_col;
+
+ if (*new_row < 0) {
+ switch (cursor_wrap_mode) {
+ case WRAP_FLAT:
+ *new_row = cur_row;
+ break;
+ case WRAP_SPHERE:
+ *new_row = BOARD_SIZE - 1;
+ break;
+ case WRAP_TORUS:
+ *new_row = BOARD_SIZE - 1;
+ (*new_col)--;
+ if (*new_col < 0) {
+ *new_col = BOARD_SIZE - 1;
+ }
+ break;
+ }
+ } else if (*new_row >= BOARD_SIZE) {
+ switch (cursor_wrap_mode) {
+ case WRAP_FLAT:
+ *new_row = cur_row;
+ break;
+ case WRAP_SPHERE:
+ *new_row = 0;
+ break;
+ case WRAP_TORUS:
+ *new_row = 0;
+ (*new_col)++;
+ if (*new_col >= BOARD_SIZE) {
+ *new_col = 0;
+ }
+ break;
+ }
+ }
+
+ return (cur_row != (*new_row)) || (cur_col != (*new_col));
+}
+
+
+/* Calculates the new cursor position if the user wants to move it
+ * horisontally as specified by delta. Current wrap mode is respected.
+ * The cursor is not actually moved.
+ *
+ * Returns true iff the cursor would be really moved. In any case, the
+ * new cursor position is stored in (new_row, new_col).
+ */
+static bool reversi_gui_cursor_pos_hmove(int col_delta, int *new_row, int *new_col) {
+ *new_row = cur_row;
+ *new_col = cur_col + col_delta;
+
+ if (*new_col < 0) {
+ switch (cursor_wrap_mode) {
+ case WRAP_FLAT:
+ *new_col = cur_col;
+ break;
+ case WRAP_SPHERE:
+ *new_col = BOARD_SIZE - 1;
+ break;
+ case WRAP_TORUS:
+ *new_col = BOARD_SIZE - 1;
+ (*new_row)--;
+ if (*new_row < 0) {
+ *new_row = BOARD_SIZE - 1;
+ }
+ break;
+ }
+ } else if (*new_col >= BOARD_SIZE) {
+ switch (cursor_wrap_mode) {
+ case WRAP_FLAT:
+ *new_col = cur_col;
+ break;
+ case WRAP_SPHERE:
+ *new_col = 0;
+ break;
+ case WRAP_TORUS:
+ *new_col = 0;
+ (*new_row)++;
+ if (*new_row >= BOARD_SIZE) {
+ *new_row = 0;
+ }
+ break;
+ }
+ }
+
+ return (cur_row != (*new_row)) || (cur_col != (*new_col));
+}
+
+
+/* Actually moves the cursor to the new position and updates the screen */
+static void reversi_gui_move_cursor(int new_row, int new_col) {
+ int old_row, old_col;
+
+ old_row = cur_row;
+ old_col = cur_col;
+
+ cur_row = new_row;
+ cur_col = new_col;
+
+ /* Only update the changed cells since there are no global changes */
+ reversi_gui_display_cursor(old_row, old_col);
+ reversi_gui_display_cursor(new_row, new_col);
+}
+
+
+/* plugin entry point */
+enum plugin_status plugin_start(struct plugin_api *api, void *parameter) {
+ bool exit, draw_screen;
+ int button;
+ int lastbutton = BUTTON_NONE;
+ int row, col;
+ int w_cnt, b_cnt;
+ char msg_buf[30];
+
+ /* plugin init */
+ rb = api;
+ /* end of plugin init */
+
+#if LCD_DEPTH > 1
+ rb->lcd_set_backdrop(NULL);
+ rb->lcd_set_foreground(LCD_BLACK);
+ rb->lcd_set_background(LCD_WHITE);
+#endif
+
+ /* Avoid compiler warnings */
+ (void)parameter;
+
+ reversi_gui_init();
+ cursor_wrap_mode = WRAP_FLAT;
+
+ /* The main game loop */
+ exit = false;
+ quit_plugin = false;
+ draw_screen = true;
+ while (!exit && !quit_plugin) {
+ if (draw_screen) {
+ reversi_gui_display_board();
+ draw_screen = false;
+ }
+ button = rb->button_get(true);
+
+ switch (button) {
+#ifdef REVERSI_BUTTON_QUIT
+ /* Exit game */
+ case REVERSI_BUTTON_QUIT:
+ exit = true;
+ break;
+#endif
+
+#ifdef REVERSI_BUTTON_ALT_MAKE_MOVE
+ case REVERSI_BUTTON_ALT_MAKE_MOVE:
+#endif
+ case REVERSI_BUTTON_MAKE_MOVE:
+#ifdef REVERSI_BUTTON_MAKE_MOVE_PRE
+ if ((button == REVERSI_BUTTON_MAKE_MOVE)
+ && (lastbutton != REVERSI_BUTTON_MAKE_MOVE_PRE))
+ break;
+#endif
+ if (reversi_make_move(&game, cur_row, cur_col, cur_player) > 0) {
+ /* Move was made. Global changes on the board are possible */
+ draw_screen = true; /* Redraw the screen next time */
+ cur_player = reversi_flipped_color(cur_player);
+ if (reversi_game_is_finished(&game)) {
+ reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
+ rb->snprintf(msg_buf, sizeof(msg_buf),
+ "Game over. %s have won.",
+ (w_cnt>b_cnt?"WHITE":"BLACK"));
+ rb->splash(HZ*2, msg_buf);
+ draw_screen = true; /* Must update screen after splash */
+ }
+ } else {
+ /* An attempt to make an invalid move */
+ rb->splash(HZ/2, "Illegal move!");
+ draw_screen = true;
+ /* Ignore any button presses during the splash */
+ rb->button_clear_queue();
+ }
+ break;
+
+ /* Move cursor left */
+ case REVERSI_BUTTON_LEFT:
+ case (REVERSI_BUTTON_LEFT | BUTTON_REPEAT):
+ if (reversi_gui_cursor_pos_hmove(-1, &row, &col)) {
+ reversi_gui_move_cursor(row, col);
+ }
+ break;
+
+ /* Move cursor right */
+ case REVERSI_BUTTON_RIGHT:
+ case (REVERSI_BUTTON_RIGHT | BUTTON_REPEAT):
+ if (reversi_gui_cursor_pos_hmove(1, &row, &col)) {
+ reversi_gui_move_cursor(row, col);
+ }
+ break;
+
+ /* Move cursor up */
+ case REVERSI_BUTTON_UP:
+ case (REVERSI_BUTTON_UP | BUTTON_REPEAT):
+ if (reversi_gui_cursor_pos_vmove(-1, &row, &col)) {
+ reversi_gui_move_cursor(row, col);
+ }
+ break;
+
+ /* Move cursor down */
+ case REVERSI_BUTTON_DOWN:
+ case (REVERSI_BUTTON_DOWN | BUTTON_REPEAT):
+ if (reversi_gui_cursor_pos_vmove(1, &row, &col)) {
+ reversi_gui_move_cursor(row, col);
+ }
+ break;
+
+ case REVERSI_BUTTON_MENU:
+#ifdef REVERSI_BUTTON_MENU_PRE
+ if (lastbutton != REVERSI_BUTTON_MENU_PRE) {
+ break;
+ }
+#endif
+ if (reversi_gui_menu()) {
+ return PLUGIN_USB_CONNECTED;
+ }
+ draw_screen = true;
+ break;
+
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
+ /* Quit if USB has been connected */
+ return PLUGIN_USB_CONNECTED;
+ }
+ break;
+ }
+ if (button != BUTTON_NONE) {
+ lastbutton = button;
+ }
+ }
+
+ return PLUGIN_OK;
+}
+
+#endif
diff --git a/apps/plugins/reversi/reversi-gui.h b/apps/plugins/reversi/reversi-gui.h
new file mode 100644
index 0000000..61168e7
--- /dev/null
+++ b/apps/plugins/reversi/reversi-gui.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _REVERSI_GUI_H
+#define _REVERSI_GUI_H
+
+#include "plugin.h"
+
+#define GAME_FILE PLUGIN_DIR "/reversi.rev"
+
+/* variable button definitions */
+#if CONFIG_KEYPAD == RECORDER_PAD
+#define REVERSI_BUTTON_QUIT BUTTON_OFF
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_PLAY
+#define REVERSI_BUTTON_MENU BUTTON_F1
+
+#elif CONFIG_KEYPAD == ONDIO_PAD
+#define REVERSI_BUTTON_QUIT BUTTON_OFF
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE_PRE BUTTON_MENU
+#define REVERSI_BUTTON_MAKE_MOVE (BUTTON_MENU | BUTTON_REL)
+#define REVERSI_BUTTON_ALT_MAKE_MOVE (BUTTON_MENU | BUTTON_DOWN)
+#define REVERSI_BUTTON_MENU_PRE BUTTON_MENU
+#define REVERSI_BUTTON_MENU (BUTTON_MENU | BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define REVERSI_BUTTON_QUIT BUTTON_OFF
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU BUTTON_MODE
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD)
+#define REVERSI_BUTTON_UP BUTTON_SCROLL_BACK
+#define REVERSI_BUTTON_DOWN BUTTON_SCROLL_FWD
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU BUTTON_MENU
+
+#elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
+#define REVERSI_BUTTON_QUIT BUTTON_POWER
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU BUTTON_PLAY
+
+#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
+#define REVERSI_BUTTON_QUIT BUTTON_A
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU BUTTON_MENU
+
+#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
+#define REVERSI_BUTTON_QUIT BUTTON_POWER
+#define REVERSI_BUTTON_UP BUTTON_SCROLL_UP
+#define REVERSI_BUTTON_DOWN BUTTON_SCROLL_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_REW
+#define REVERSI_BUTTON_MENU BUTTON_PLAY
+
+#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
+#define REVERSI_BUTTON_QUIT BUTTON_POWER
+#define REVERSI_BUTTON_UP BUTTON_UP
+#define REVERSI_BUTTON_DOWN BUTTON_DOWN
+#define REVERSI_BUTTON_LEFT BUTTON_LEFT
+#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
+#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
+#define REVERSI_BUTTON_MENU (BUTTON_SELECT|BUTTON_REPEAT)
+
+#elif
+ #error REVERSI: Unsupported keypad
+#endif
+
+
+/* Modes for the cursor behaviour at the board edges */
+typedef enum _cursor_wrap_mode_t {
+ WRAP_FLAT, /* No wrapping */
+ WRAP_SPHERE, /* (7,7) > right > (7,0); (7,7) > down > (0,7) */
+ WRAP_TORUS, /* (7,7) > right > (0,0); (7,7) > down > (0,0) */
+} cursor_wrap_mode_t;
+
+
+#endif
diff --git a/apps/plugins/reversi/reversi-strategy.c b/apps/plugins/reversi/reversi-strategy.c
new file mode 100644
index 0000000..831c0cd
--- /dev/null
+++ b/apps/plugins/reversi/reversi-strategy.c
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "reversi-strategy.h"
+#include <stddef.h>
+
+
+/* Implementation of a rather weak player strategy */
+static move_t novice_move_func(const reversi_board_t *game, int color) {
+ /* TODO: Implement novice_move_func */
+ (void)game;
+ (void)color;
+ return MOVE_INVALID;
+}
+
+
+/* Implementation of a good player strategy */
+static move_t expert_move_func(const reversi_board_t *game, int color) {
+ /* TODO: Implement expert_move_func */
+ (void)game;
+ (void)color;
+ return MOVE_INVALID;
+}
+
+
+
+/* Strategy that requires interaction with the user */
+const game_strategy_t strategy_human = {
+ false,
+ NULL
+};
+
+/* Robot that plays not very well (novice) */
+const game_strategy_t strategy_novice = {
+ true,
+ novice_move_func
+};
+
+/* Robot that is hard to beat (expert) */
+const game_strategy_t strategy_expert = {
+ true,
+ expert_move_func
+};
diff --git a/apps/plugins/reversi/reversi-strategy.h b/apps/plugins/reversi/reversi-strategy.h
new file mode 100644
index 0000000..57bc3fa
--- /dev/null
+++ b/apps/plugins/reversi/reversi-strategy.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2006 Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _REVERSI_STRATEGY_H
+#define _REVERSI_STRATEGY_H
+
+#include "reversi-game.h"
+
+
+/* Function for making a move. Is called only for robots.
+ Should return the game history entry for the advised move if
+ a move has been considered or HISTORY_INVALID_ENTRY if no move
+ has been considered. The board should not be modified. */
+typedef move_t (*move_func_t)(const reversi_board_t *game, int color);
+
+/* A playing party/strategy */
+typedef struct _game_strategy_t {
+ const bool is_robot; /* Is the player a robot or does it require user input? */
+ move_func_t move_func; /* Function for advicing a move */
+} game_strategy_t;
+
+
+/* --- Possible playing strategies --- */
+extern const game_strategy_t strategy_human;
+extern const game_strategy_t strategy_novice;
+extern const game_strategy_t strategy_expert;
+
+#endif