From b4b34f0c0723c29f368d7793889ef92bc6404a52 Mon Sep 17 00:00:00 2001 From: Antoine Cellerier Date: Sat, 30 Jun 2007 20:04:42 +0000 Subject: 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 --- apps/plugins/SUBDIRS | 1 + apps/plugins/reversi/Makefile | 111 ++++++ apps/plugins/reversi/SOURCES | 3 + apps/plugins/reversi/reversi-game.c | 370 ++++++++++++++++++ apps/plugins/reversi/reversi-game.h | 75 ++++ apps/plugins/reversi/reversi-gui.c | 669 ++++++++++++++++++++++++++++++++ apps/plugins/reversi/reversi-gui.h | 117 ++++++ apps/plugins/reversi/reversi-strategy.c | 59 +++ apps/plugins/reversi/reversi-strategy.h | 44 +++ 9 files changed, 1449 insertions(+) create mode 100644 apps/plugins/reversi/Makefile create mode 100644 apps/plugins/reversi/SOURCES create mode 100644 apps/plugins/reversi/reversi-game.c create mode 100644 apps/plugins/reversi/reversi-game.h create mode 100644 apps/plugins/reversi/reversi-gui.c create mode 100644 apps/plugins/reversi/reversi-gui.h create mode 100644 apps/plugins/reversi/reversi-strategy.c create mode 100644 apps/plugins/reversi/reversi-strategy.h diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS index 5ac85a3115..6665f41e4e 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 0000000000..b008738335 --- /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 0000000000..342e4d0e26 --- /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 0000000000..80893b7270 --- /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 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 +#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 0000000000..a7d0329d1f --- /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 + +#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 0000000000..e543563729 --- /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; rlcd_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 0000000000..61168e7034 --- /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 0000000000..831c0cd92b --- /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 + + +/* 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 0000000000..57bc3faf57 --- /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 -- cgit v1.2.3