diff options
-rw-r--r-- | apps/plugins/SUBDIRS | 1 | ||||
-rw-r--r-- | apps/plugins/reversi/Makefile | 111 | ||||
-rw-r--r-- | apps/plugins/reversi/SOURCES | 3 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-game.c | 370 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-game.h | 75 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-gui.c | 669 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-gui.h | 117 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-strategy.c | 59 | ||||
-rw-r--r-- | apps/plugins/reversi/reversi-strategy.h | 44 |
9 files changed, 1449 insertions, 0 deletions
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 | |||
12 | #ifdef HAVE_LCD_BITMAP | 12 | #ifdef HAVE_LCD_BITMAP |
13 | chessbox | 13 | chessbox |
14 | sudoku | 14 | sudoku |
15 | reversi | ||
15 | #endif | 16 | #endif |
16 | 17 | ||
17 | /* For all 2bpp and colour targets */ | 18 | /* 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 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $$Id: $$ | ||
8 | # | ||
9 | |||
10 | INCLUDES = -I$(APPSDIR) -I.. -I. $(TARGET_INC) -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ | ||
11 | -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(OUTDIR) -I$(BUILDDIR) \ | ||
12 | -I$(BUILDDIR)/pluginbitmaps | ||
13 | CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \ | ||
14 | -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN | ||
15 | |||
16 | ifdef APPEXTRA | ||
17 | INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) | ||
18 | endif | ||
19 | |||
20 | LINKFILE := $(OBJDIR)/link.lds | ||
21 | DEPFILE = $(OBJDIR)/dep-reversi | ||
22 | |||
23 | # This sets up 'SRC' based on the files mentioned in SOURCES | ||
24 | include $(TOOLSDIR)/makesrc.inc | ||
25 | |||
26 | SOURCES = $(SRC) | ||
27 | OBJS := $(SRC:%.c=$(OBJDIR)/%.o) | ||
28 | DIRS = . | ||
29 | |||
30 | ifndef SIMVER | ||
31 | LDS := ../plugin.lds | ||
32 | OUTPUT = $(OUTDIR)/reversi.rock | ||
33 | else ## simulators | ||
34 | OUTPUT = $(OUTDIR)/reversi.rock | ||
35 | endif | ||
36 | |||
37 | all: $(OUTPUT) | ||
38 | |||
39 | ifndef SIMVER | ||
40 | $(OBJDIR)/reversi.elf: $(OBJS) $(LINKFILE) $(BITMAPLIBS) | ||
41 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lgcc \ | ||
42 | $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/reversi.map | ||
43 | |||
44 | $(OUTPUT): $(OBJDIR)/reversi.elf | ||
45 | $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ | ||
46 | else | ||
47 | |||
48 | ifeq ($(SIMVER), x11) | ||
49 | ################################################### | ||
50 | # This is the X11 simulator version | ||
51 | |||
52 | $(OUTPUT): $(OBJS) | ||
53 | $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ | ||
54 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
55 | # 'x' must be kept or you'll have "Win32 error 5" | ||
56 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
57 | # #define ERROR_ACCESS_DENIED 5L | ||
58 | else | ||
59 | @chmod -x $@ | ||
60 | endif | ||
61 | |||
62 | else # end of x11-simulator | ||
63 | ifeq ($(SIMVER), sdl) | ||
64 | ################################################### | ||
65 | # This is the SDL simulator version | ||
66 | |||
67 | $(OUTPUT): $(OBJS) | ||
68 | $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ | ||
69 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
70 | # 'x' must be kept or you'll have "Win32 error 5" | ||
71 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
72 | # #define ERROR_ACCESS_DENIED 5L | ||
73 | else | ||
74 | @chmod -x $@ | ||
75 | endif | ||
76 | |||
77 | else # end of sdl-simulator | ||
78 | ################################################### | ||
79 | # This is the win32 simulator version | ||
80 | DLLTOOLFLAGS = --export-all | ||
81 | DLLWRAPFLAGS = -s --entry _DllMain@12 --target=i386-mingw32 -mno-cygwin | ||
82 | |||
83 | $(OUTPUT): $(OBJS) | ||
84 | $(call PRINTS,DLL $(@F))$(DLLTOOL) $(DLLTOOLFLAGS) -z $(OBJDIR)/$*.def $(OBJS) | ||
85 | $(SILENT)$(DLLWRAP) $(DLLWRAPFLAGS) --def $(OBJDIR)/$*.def $(OBJS) \ | ||
86 | $(BUILDDIR)/libplugin.a $(BITMAPLIBS) -o $@ | ||
87 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
88 | # 'x' must be kept or you'll have "Win32 error 5" | ||
89 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
90 | # #define ERROR_ACCESS_DENIED 5L | ||
91 | else | ||
92 | @chmod -x $@ | ||
93 | endif | ||
94 | endif # end of win32-simulator | ||
95 | endif | ||
96 | endif # end of simulator section | ||
97 | |||
98 | |||
99 | include $(TOOLSDIR)/make.inc | ||
100 | |||
101 | # MEMORYSIZE should be passed on to this makefile with the chosen memory size | ||
102 | # given in number of MB | ||
103 | $(LINKFILE): $(LDS) | ||
104 | $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \ | ||
105 | $(DEFINES) -E -P - >$@ | ||
106 | |||
107 | clean: | ||
108 | $(call PRINTS,cleaning reversi)rm -rf $(OBJDIR)/reversi | ||
109 | $(SILENT)rm -f $(OBJDIR)/reversi.* $(DEPFILE) | ||
110 | |||
111 | -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 @@ | |||
1 | reversi-game.c | ||
2 | reversi-strategy.c | ||
3 | 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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | /* | ||
21 | * Reversi. Code is heavily based on othello code by Claudio Clemens which is | ||
22 | * copyright (c) 2003-2006 Claudio Clemens <asturio at gmx dot net> and is | ||
23 | * released under the GNU General Public License as published by the Free | ||
24 | * Software Foundation; either version 2, or (at your option) any later version. | ||
25 | */ | ||
26 | |||
27 | #include <stddef.h> | ||
28 | #include "reversi-game.h" | ||
29 | |||
30 | /* | ||
31 | * Constants for directions. The values are chosen so that | ||
32 | * they can be bit combined. | ||
33 | */ | ||
34 | #define DIR_UP 1 /* UP */ | ||
35 | #define DIR_DO 2 /* DOWN */ | ||
36 | #define DIR_LE 4 /* LEFT */ | ||
37 | #define DIR_RI 8 /* RIGHT */ | ||
38 | #define DIR_UL 16 /* UP LEFT */ | ||
39 | #define DIR_UR 32 /* UP RIGHT */ | ||
40 | #define DIR_DL 64 /* DOWN LEFT */ | ||
41 | #define DIR_DR 128 /* DOWN RIGHT */ | ||
42 | |||
43 | /* Array of directions for easy iteration through all of them */ | ||
44 | static int DIRECTIONS[] = | ||
45 | {DIR_UP, DIR_DO, DIR_LE, DIR_RI, DIR_UL, DIR_UR, DIR_DL, DIR_DR}; | ||
46 | #define NUM_OF_DIRECTIONS 8 | ||
47 | |||
48 | |||
49 | /* Initializes a reversi game */ | ||
50 | void reversi_init_game(reversi_board_t *game) { | ||
51 | int r, c; | ||
52 | for (r = 0; r < BOARD_SIZE; r++) { | ||
53 | for (c = 0; c < BOARD_SIZE; c++) { | ||
54 | game->board[r][c] = FREE; | ||
55 | } | ||
56 | } | ||
57 | game->board[3][3] = WHITE; | ||
58 | game->board[4][4] = WHITE; | ||
59 | game->board[3][4] = BLACK; | ||
60 | game->board[4][3] = BLACK; | ||
61 | |||
62 | /* Invalidate the history */ | ||
63 | c = sizeof(game->history) / sizeof(game->history[0]); | ||
64 | for (r = 0; r < c; r++) { | ||
65 | game->history[r] = MOVE_INVALID; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | |||
70 | /* Returns the 'flipped' color, e.g. WHITE for BLACK and vice versa */ | ||
71 | int reversi_flipped_color(const int color) { | ||
72 | switch (color) { | ||
73 | case WHITE: | ||
74 | return BLACK; | ||
75 | |||
76 | case BLACK: | ||
77 | return WHITE; | ||
78 | |||
79 | default: | ||
80 | return FREE; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | |||
85 | /* Counts and returns the number of occupied cells on the board. | ||
86 | * If white_count and/or black_count is not null, the number of | ||
87 | * white/black stones is placed there. */ | ||
88 | int reversi_count_occupied_cells(const reversi_board_t *game, | ||
89 | int *white_count, int *black_count) { | ||
90 | int w_cnt, b_cnt, r, c; | ||
91 | w_cnt = b_cnt = 0; | ||
92 | for (r = 0; r < BOARD_SIZE; r++) { | ||
93 | for (c = 0; c < BOARD_SIZE; c++) { | ||
94 | if (game->board[r][c] == WHITE) { | ||
95 | w_cnt++; | ||
96 | } else if (game->board[r][c] == BLACK) { | ||
97 | b_cnt++; | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | if (white_count != NULL) { | ||
102 | *white_count = w_cnt; | ||
103 | } | ||
104 | if (black_count != NULL) { | ||
105 | *black_count = b_cnt; | ||
106 | } | ||
107 | return w_cnt + b_cnt; | ||
108 | } | ||
109 | |||
110 | |||
111 | /* Returns the number of free cells on the board */ | ||
112 | static int reversi_count_free_cells(const reversi_board_t *game) { | ||
113 | int cnt; | ||
114 | cnt = reversi_count_occupied_cells(game, NULL, NULL); | ||
115 | return BOARD_SIZE*BOARD_SIZE - cnt; | ||
116 | } | ||
117 | |||
118 | |||
119 | /* Checks whether the game is finished. That means that nobody | ||
120 | * can make a move. Note that the implementation is not correct | ||
121 | * as a game may be finished even if there are free cells | ||
122 | */ | ||
123 | bool reversi_game_is_finished(const reversi_board_t *game) { | ||
124 | return (reversi_count_free_cells(game) == 0); | ||
125 | } | ||
126 | |||
127 | |||
128 | /* Finds out who should place the next stone. It's the partner | ||
129 | * of the last move or WHITE if the game is just started. | ||
130 | * | ||
131 | * Returns WHITE or BLACK. | ||
132 | */ | ||
133 | int reversi_get_turn(const reversi_board_t *game) { | ||
134 | int moves; | ||
135 | moves = reversi_count_moves(game); | ||
136 | if (moves == 0) { | ||
137 | return WHITE; | ||
138 | } else { | ||
139 | return reversi_flipped_color(MOVE_PLAYER(game->history[moves-1])); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | |||
144 | /* Returns the total number of moves made so far */ | ||
145 | int reversi_count_moves(const reversi_board_t *game) { | ||
146 | int cnt; | ||
147 | cnt = reversi_count_occupied_cells(game, NULL, NULL); | ||
148 | return cnt - INIT_STONES; | ||
149 | } | ||
150 | |||
151 | |||
152 | /* Returns the number of moves made by the specified | ||
153 | * player (WHITE or BLACK) so far | ||
154 | */ | ||
155 | static int reversi_count_player_moves(const reversi_board_t *game, | ||
156 | const int player) { | ||
157 | int moves, cnt, i; | ||
158 | moves = reversi_count_moves(game); | ||
159 | cnt = 0; | ||
160 | for (i = 0; i < moves; i++) { | ||
161 | if (MOVE_PLAYER(game->history[i]) == player) { | ||
162 | cnt++; | ||
163 | } | ||
164 | } | ||
165 | return cnt; | ||
166 | } | ||
167 | |||
168 | |||
169 | /* Returns the number of moves made by WHITE so far */ | ||
170 | int reversi_count_white_moves(const reversi_board_t *game) { | ||
171 | return reversi_count_player_moves(game, WHITE); | ||
172 | } | ||
173 | |||
174 | |||
175 | /* Returns the number of moves made by BLACK so far */ | ||
176 | int reversi_count_black_moves(const reversi_board_t *game) { | ||
177 | return reversi_count_player_moves(game, BLACK); | ||
178 | } | ||
179 | |||
180 | |||
181 | /* Checks whether the specified position is on the board | ||
182 | * (and not beyond) | ||
183 | */ | ||
184 | static bool reversi_is_position_on_board(const int row, const int col) { | ||
185 | return (row >= 0) && (row < BOARD_SIZE) && | ||
186 | (col >= 0) && (col < BOARD_SIZE); | ||
187 | } | ||
188 | |||
189 | |||
190 | /* Returns the delta for row to move in the specified direction */ | ||
191 | static int reversi_row_delta(const int direction) { | ||
192 | switch (direction) { | ||
193 | case DIR_UP: | ||
194 | case DIR_UL: | ||
195 | case DIR_UR: | ||
196 | return -1; | ||
197 | |||
198 | case DIR_DO: | ||
199 | case DIR_DL: | ||
200 | case DIR_DR: | ||
201 | return 1; | ||
202 | |||
203 | default: | ||
204 | return 0; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | |||
209 | /* Returns the delta for column to move in the specified direction */ | ||
210 | static int reversi_column_delta(const int direction) { | ||
211 | switch (direction) { | ||
212 | case DIR_LE: | ||
213 | case DIR_UL: | ||
214 | case DIR_DL: | ||
215 | return -1; | ||
216 | |||
217 | case DIR_RI: | ||
218 | case DIR_UR: | ||
219 | case DIR_DR: | ||
220 | return 1; | ||
221 | |||
222 | default: | ||
223 | return 0; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | |||
228 | /* Checks if some stones would be captured in the specified direction | ||
229 | * if a stone were placed in the specified cell by the specified player. | ||
230 | * | ||
231 | * Returns 0 if no stones would be captured or 'direction' otherwise | ||
232 | */ | ||
233 | static int reversi_is_valid_direction(const reversi_board_t *game, | ||
234 | const int row, const int col, const int player, const int direction) { | ||
235 | int row_delta, col_delta, r, c; | ||
236 | int other_color; | ||
237 | int flip_cnt; /* Number of stones that would be flipped */ | ||
238 | |||
239 | row_delta = reversi_row_delta(direction); | ||
240 | col_delta = reversi_column_delta(direction); | ||
241 | other_color = reversi_flipped_color(player); | ||
242 | |||
243 | r = row + row_delta; | ||
244 | c = col + col_delta; | ||
245 | |||
246 | flip_cnt = 0; | ||
247 | while (reversi_is_position_on_board(r, c) && | ||
248 | (game->board[r][c] == other_color)) { | ||
249 | r += row_delta; | ||
250 | c += col_delta; | ||
251 | flip_cnt++; | ||
252 | } | ||
253 | |||
254 | if ((flip_cnt > 0) && reversi_is_position_on_board(r, c) && | ||
255 | (game->board[r][c] == player)) { | ||
256 | return direction; | ||
257 | } else { | ||
258 | return 0; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | |||
263 | /* Checks whether the move at the specified position would be valid. | ||
264 | * Params: | ||
265 | * - game: current state of the game | ||
266 | * - row: 0-based row number of the move in question | ||
267 | * - col: 0-based column number of the move in question | ||
268 | * - player: who is about to make the move (WHITE/BLACK) | ||
269 | * | ||
270 | * Checks if the place is empty, the coordinates are legal, | ||
271 | * and some stones can be captured. | ||
272 | * | ||
273 | * Returns 0 if the move is not valid or, otherwise, the or'd | ||
274 | * directions in which stones would be captured. | ||
275 | */ | ||
276 | static int reversi_is_valid_move(const reversi_board_t *game, | ||
277 | const int row, const int col, const int player) { | ||
278 | int dirs, i; | ||
279 | dirs = 0; | ||
280 | |||
281 | /* Check if coordinates are legal */ | ||
282 | if (!reversi_is_position_on_board(row, col)) { | ||
283 | return dirs; | ||
284 | } | ||
285 | |||
286 | /* Check if the place is free */ | ||
287 | if (game->board[row][col] != FREE) { | ||
288 | return dirs; | ||
289 | } | ||
290 | |||
291 | /* Check the directions of capture */ | ||
292 | for (i = 0; i < NUM_OF_DIRECTIONS; i++) { | ||
293 | dirs |= reversi_is_valid_direction(game, row, col, player, DIRECTIONS[i]); | ||
294 | } | ||
295 | |||
296 | return dirs; | ||
297 | } | ||
298 | |||
299 | |||
300 | /* Flips the stones in the specified direction after the specified | ||
301 | * player has placed a stone in the specified cell. The move is | ||
302 | * assumed to be valid. | ||
303 | * | ||
304 | * Returns the number of flipped stones in that direction | ||
305 | */ | ||
306 | static int reversi_flip_stones(reversi_board_t *game, | ||
307 | const int row, const int col, const int player, const int direction) { | ||
308 | int row_delta, col_delta, r, c; | ||
309 | int other_color; | ||
310 | int flip_cnt; /* Number of stones flipped */ | ||
311 | |||
312 | row_delta = reversi_row_delta(direction); | ||
313 | col_delta = reversi_column_delta(direction); | ||
314 | other_color = reversi_flipped_color(player); | ||
315 | |||
316 | r = row + row_delta; | ||
317 | c = col + col_delta; | ||
318 | |||
319 | flip_cnt = 0; | ||
320 | while (reversi_is_position_on_board(r, c) && | ||
321 | (game->board[r][c] == other_color)) { | ||
322 | game->board[r][c] = player; | ||
323 | r += row_delta; | ||
324 | c += col_delta; | ||
325 | flip_cnt++; | ||
326 | } | ||
327 | |||
328 | return flip_cnt; | ||
329 | } | ||
330 | |||
331 | |||
332 | /* Tries to make a move (place a stone) at the specified position. | ||
333 | * If the move is valid the board is changed. Otherwise nothing happens. | ||
334 | * | ||
335 | * Params: | ||
336 | * - game: current state of the game | ||
337 | * - row: 0-based row number of the move to make | ||
338 | * - col: 0-based column number of the move to make | ||
339 | * - player: who makes the move (WHITE/BLACK) | ||
340 | * | ||
341 | * Returns the number of flipped (captured) stones (>0) iff the move | ||
342 | * was valid or 0 if the move was not valid. Note that in the case of | ||
343 | * a valid move, the stone itself is not counted. | ||
344 | */ | ||
345 | int reversi_make_move(reversi_board_t *game, | ||
346 | const int row, const int col, const int player) { | ||
347 | int dirs, cnt, i; | ||
348 | |||
349 | dirs = reversi_is_valid_move(game, row, col, player); | ||
350 | if (dirs == 0) { | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* Place the stone into the cell */ | ||
355 | game->board[row][col] = player; | ||
356 | |||
357 | /* Capture stones in all possible directions */ | ||
358 | cnt = 0; | ||
359 | for (i = 0; i < NUM_OF_DIRECTIONS; i++) { | ||
360 | if (dirs & DIRECTIONS[i]) { | ||
361 | cnt += reversi_flip_stones(game, row, col, player, DIRECTIONS[i]); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | /* Remember the move */ | ||
366 | i = reversi_count_moves(game); | ||
367 | game->history[i-1] = MAKE_MOVE(row, col, player); | ||
368 | |||
369 | return cnt; | ||
370 | } | ||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _REVERSI_GAME_H | ||
21 | #define _REVERSI_GAME_H | ||
22 | |||
23 | #include <stdbool.h> | ||
24 | |||
25 | #define WHITE 1 /* WHITE constant, it always plays first (as in chess) */ | ||
26 | #define BLACK 2 /* BLACK constant */ | ||
27 | #define FREE 0 /* Free place constant */ | ||
28 | |||
29 | #define BOARD_SIZE 8 | ||
30 | #define INIT_STONES 4 | ||
31 | |||
32 | /* Description of a move. A move is stored as a byte in the following format: | ||
33 | * - bit 7 : 0 for valid entries (i.e. those containing a move info, | ||
34 | * 1 for invalid entries | ||
35 | * - bits 6..4: row | ||
36 | * - bit 3 : 0 if it's white move, 1 if it's black move | ||
37 | * - bits 2..0: column | ||
38 | */ | ||
39 | typedef unsigned char move_t; | ||
40 | |||
41 | #define MOVE_ROW(h) (((h) >> 4) & 0x7) | ||
42 | #define MOVE_COL(h) ((h) & 0x7) | ||
43 | #define MOVE_PLAYER(h) (((h) & 0x8) ? BLACK : WHITE) | ||
44 | #define MAKE_MOVE(r,c,player) ( ((r)<<4) | ((c)&0x7) | \ | ||
45 | ((player) == WHITE ? 0 : 0x8) ) | ||
46 | #define MOVE_INVALID 0x80 | ||
47 | |||
48 | |||
49 | /* State of a board */ | ||
50 | typedef struct _reversi_board_t { | ||
51 | /* The current state of the game (BLACK/WHITE/FREE) */ | ||
52 | char board[BOARD_SIZE][BOARD_SIZE]; | ||
53 | |||
54 | /* Game history. First move (mostly, but not necessarily, black) is stored | ||
55 | * in history[0], second move (mostly, but not necessarily, white) is | ||
56 | * stored in history[1] etc. | ||
57 | */ | ||
58 | move_t history[BOARD_SIZE*BOARD_SIZE - INIT_STONES]; | ||
59 | } reversi_board_t; | ||
60 | |||
61 | |||
62 | void reversi_init_game(reversi_board_t *game); | ||
63 | int reversi_flipped_color(const int color); | ||
64 | bool reversi_game_is_finished(const reversi_board_t *game); | ||
65 | int reversi_get_turn(const reversi_board_t *game); | ||
66 | int reversi_count_occupied_cells(const reversi_board_t *game, | ||
67 | int *white_count, int *black_count); | ||
68 | int reversi_count_moves(const reversi_board_t *game); | ||
69 | int reversi_count_white_moves(const reversi_board_t *game); | ||
70 | int reversi_count_black_moves(const reversi_board_t *game); | ||
71 | int reversi_make_move(reversi_board_t *game, const int row, | ||
72 | const int col, const int player); | ||
73 | |||
74 | |||
75 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | /* | ||
21 | GUI part of reversi. Code is inspired by sudoku code by Dave Chapman | ||
22 | which is copyright (c) 2005 Dave Chapman and is released under the | ||
23 | GNU General Public License. | ||
24 | |||
25 | |||
26 | User instructions | ||
27 | ----------------- | ||
28 | |||
29 | Use the arrow keys to move cursor, and press TOGGLE to place a stone. | ||
30 | |||
31 | At any time during the game, press MENU to bring up the game menu with | ||
32 | further options: | ||
33 | |||
34 | - Save | ||
35 | - Reload | ||
36 | - Clear | ||
37 | |||
38 | */ | ||
39 | |||
40 | #include "plugin.h" | ||
41 | |||
42 | #ifdef HAVE_LCD_BITMAP | ||
43 | |||
44 | #include "reversi-game.h" | ||
45 | #include "reversi-strategy.h" | ||
46 | #include "reversi-gui.h" | ||
47 | |||
48 | #include "../lib/oldmenuapi.h" | ||
49 | |||
50 | PLUGIN_HEADER | ||
51 | |||
52 | /* The global api struct pointer. While not strictly necessary, | ||
53 | it's nice not to have to pass the api pointer in all function | ||
54 | calls in the plugin */ | ||
55 | static struct plugin_api* rb; | ||
56 | |||
57 | /* Thickness of the grid lines */ | ||
58 | #define LINE_THCK 1 | ||
59 | |||
60 | #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout */ | ||
61 | |||
62 | #if (LCD_HEIGHT==64) && (LCD_WIDTH==112) | ||
63 | /* Archos Recorders and Ondios - 112x64, 8 cells @ 8x6 with 9 border lines */ | ||
64 | |||
65 | /* Internal dimensions of a cell */ | ||
66 | #define CELL_WIDTH 8 | ||
67 | #define CELL_HEIGHT 6 | ||
68 | #define SMALL_BOARD | ||
69 | |||
70 | #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138) | ||
71 | /* iPod Mini - 138x110, 8 cells @ 10x10 with 9 border lines */ | ||
72 | |||
73 | /* Internal dimensions of a cell */ | ||
74 | #define CELL_WIDTH 10 | ||
75 | #define CELL_HEIGHT 10 | ||
76 | |||
77 | #elif (LCD_HEIGHT==128) && (LCD_WIDTH==128) | ||
78 | /* iriver H10 5-6GB - 128x128, 8 cells @ 10x10 with 9 border lines */ | ||
79 | |||
80 | /* Internal dimensions of a cell */ | ||
81 | #define CELL_WIDTH 10 | ||
82 | #define CELL_HEIGHT 10 | ||
83 | |||
84 | #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \ | ||
85 | ((LCD_HEIGHT==132) && (LCD_WIDTH==176)) | ||
86 | /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */ | ||
87 | /* iPod Nano - 176x132, 8 cells @ 12x12 with 9 border lines */ | ||
88 | |||
89 | /* Internal dimensions of a cell */ | ||
90 | #define CELL_WIDTH 12 | ||
91 | #define CELL_HEIGHT 12 | ||
92 | |||
93 | #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220)) || \ | ||
94 | ((LCD_HEIGHT==220) && (LCD_WIDTH==176)) | ||
95 | /* Iriver h300, iPod Color/Photo - 220x176, 8 cells @ 16x16 with 9 border lines */ | ||
96 | |||
97 | /* Internal dimensions of a cell */ | ||
98 | #define CELL_WIDTH 16 | ||
99 | #define CELL_HEIGHT 16 | ||
100 | |||
101 | #elif (LCD_HEIGHT>=240) && (LCD_WIDTH>=320) | ||
102 | /* iPod Video - 320x240, 8 cells @ 24x24 with 9 border lines */ | ||
103 | |||
104 | /* Internal dimensions of a cell */ | ||
105 | #define CELL_WIDTH 24 | ||
106 | #define CELL_HEIGHT 24 | ||
107 | |||
108 | #else | ||
109 | #error REVERSI: Unsupported LCD size | ||
110 | #endif | ||
111 | |||
112 | #else /* Vertical layout */ | ||
113 | #define VERTICAL_LAYOUT | ||
114 | |||
115 | #if (LCD_HEIGHT>=320) && (LCD_WIDTH>=240) | ||
116 | /* Gigabeat - 240x320, 8 cells @ 24x24 with 9 border lines */ | ||
117 | |||
118 | /* Internal dimensions of a cell */ | ||
119 | #define CELL_WIDTH 24 | ||
120 | #define CELL_HEIGHT 24 | ||
121 | |||
122 | #elif (LCD_HEIGHT>=220) && (LCD_WIDTH>=176) | ||
123 | /* e200 - 176x220, 8 cells @ 12x12 with 9 border lines */ | ||
124 | |||
125 | /* Internal dimensions of a cell */ | ||
126 | #define CELL_WIDTH 18 | ||
127 | #define CELL_HEIGHT 18 | ||
128 | |||
129 | #else | ||
130 | #error REVERSI: Unsupported LCD size | ||
131 | #endif | ||
132 | |||
133 | #endif /* Layout */ | ||
134 | |||
135 | |||
136 | /* Where the board begins */ | ||
137 | #define XOFS 4 | ||
138 | #define YOFS 4 | ||
139 | |||
140 | /* Total width and height of the board without enclosing box */ | ||
141 | #define BOARD_WIDTH (CELL_WIDTH*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1)) | ||
142 | #define BOARD_HEIGHT (CELL_HEIGHT*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1)) | ||
143 | |||
144 | /* Thickness of the white cells' lines */ | ||
145 | #if (CELL_WIDTH >= 15) && (CELL_HEIGHT >= 15) | ||
146 | #define CELL_LINE_THICKNESS 2 | ||
147 | #else | ||
148 | #define CELL_LINE_THICKNESS 1 | ||
149 | #endif | ||
150 | |||
151 | /* Margins within a cell */ | ||
152 | #if (CELL_WIDTH >= 10) && (CELL_HEIGHT >= 10) | ||
153 | #define STONE_MARGIN 2 | ||
154 | #else | ||
155 | #define STONE_MARGIN 1 | ||
156 | #endif | ||
157 | |||
158 | #define CURSOR_MARGIN (STONE_MARGIN + CELL_LINE_THICKNESS) | ||
159 | |||
160 | /* Upper left corner of a cell */ | ||
161 | #define CELL_X(c) (XOFS + (c)*CELL_WIDTH + ((c)+1)*LINE_THCK) | ||
162 | #define CELL_Y(r) (YOFS + (r)*CELL_HEIGHT + ((r)+1)*LINE_THCK) | ||
163 | |||
164 | |||
165 | #ifdef VERTICAL_LAYOUT | ||
166 | #define LEGEND_X(lc) (CELL_X(lc)) | ||
167 | #define LEGEND_Y(lr) (CELL_Y(BOARD_SIZE+(lr)) + CELL_HEIGHT/2) | ||
168 | #else | ||
169 | #define LEGEND_X(lc) (CELL_X(BOARD_SIZE+(lc)) + CELL_WIDTH/2) | ||
170 | #define LEGEND_Y(lr) (CELL_Y(lr)) | ||
171 | #endif | ||
172 | |||
173 | |||
174 | /* Board state */ | ||
175 | static reversi_board_t game; | ||
176 | |||
177 | /* --- Setting values --- */ | ||
178 | |||
179 | /* Playing strategies used by white and black players */ | ||
180 | const game_strategy_t *white_strategy; | ||
181 | const game_strategy_t *black_strategy; | ||
182 | |||
183 | /* Cursor position */ | ||
184 | static int cur_row, cur_col; | ||
185 | |||
186 | /* Color for the next move (BLACK/WHITE) */ | ||
187 | static int cur_player; | ||
188 | |||
189 | /* Active cursor wrapping mode */ | ||
190 | static cursor_wrap_mode_t cursor_wrap_mode; | ||
191 | |||
192 | static bool quit_plugin; | ||
193 | |||
194 | |||
195 | /* Initialises the state of the game (starts a new game) */ | ||
196 | static void reversi_gui_init(void) { | ||
197 | reversi_init_game(&game); | ||
198 | white_strategy = &strategy_human; | ||
199 | black_strategy = &strategy_human; | ||
200 | cur_player = BLACK; | ||
201 | |||
202 | /* Place the cursor so that WHITE can make a move */ | ||
203 | cur_row = 2; | ||
204 | cur_col = 3; | ||
205 | } | ||
206 | |||
207 | |||
208 | /* Draws the cursor in the specified cell. Cursor is drawn in the complement | ||
209 | * mode, i.e. drawing it twice will result in no changes on the screen. | ||
210 | */ | ||
211 | static void reversi_gui_display_cursor(int row, int col) { | ||
212 | int old_mode, x, y; | ||
213 | old_mode = rb->lcd_get_drawmode(); | ||
214 | x = CELL_X(col); | ||
215 | y = CELL_Y(row); | ||
216 | |||
217 | rb->lcd_set_drawmode(DRMODE_COMPLEMENT); | ||
218 | rb->lcd_drawline(x+CURSOR_MARGIN, y+CURSOR_MARGIN, | ||
219 | x+CELL_WIDTH-CURSOR_MARGIN-1, y+CELL_HEIGHT-CURSOR_MARGIN-1); | ||
220 | rb->lcd_drawline(x+CURSOR_MARGIN, y+CELL_HEIGHT-CURSOR_MARGIN-1, | ||
221 | x+CELL_WIDTH-CURSOR_MARGIN-1, y+CURSOR_MARGIN); | ||
222 | |||
223 | /* Draw the shadows */ | ||
224 | rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS-3); | ||
225 | rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS+BOARD_HEIGHT+2); | ||
226 | rb->lcd_vline(XOFS-3, y, y+CELL_HEIGHT-1); | ||
227 | rb->lcd_vline(XOFS+BOARD_WIDTH+2, y, y+CELL_HEIGHT-1); | ||
228 | |||
229 | rb->lcd_set_drawmode(old_mode); | ||
230 | rb->lcd_update(); | ||
231 | } | ||
232 | |||
233 | |||
234 | /* Draws the cell of the specified color (WHITE/BLACK) assuming that | ||
235 | * the upper left corner of the cell is at (x, y) */ | ||
236 | static void reversi_gui_draw_cell(int x, int y, int color) { | ||
237 | int i; | ||
238 | if (color == WHITE) { | ||
239 | for (i = 0; i < CELL_LINE_THICKNESS; i++) { | ||
240 | rb->lcd_drawrect(x+STONE_MARGIN+i, y+STONE_MARGIN+i, | ||
241 | CELL_WIDTH-2*(STONE_MARGIN+i), CELL_HEIGHT-2*(STONE_MARGIN+i)); | ||
242 | } | ||
243 | } else if (color == BLACK) { | ||
244 | rb->lcd_fillrect(x+STONE_MARGIN, y+STONE_MARGIN, | ||
245 | CELL_WIDTH-2*STONE_MARGIN, CELL_HEIGHT-2*STONE_MARGIN); | ||
246 | } else { | ||
247 | /* Cell is free -> nothing to do */ | ||
248 | } | ||
249 | } | ||
250 | |||
251 | |||
252 | /* Draws the complete screen */ | ||
253 | static void reversi_gui_display_board(void) { | ||
254 | int x, y, r, c, x_width, x_height; | ||
255 | char buf[8]; | ||
256 | |||
257 | /* Clear the display buffer */ | ||
258 | rb->lcd_clear_display(); | ||
259 | rb->lcd_set_drawmode(DRMODE_FG); | ||
260 | |||
261 | /* Thicker board box */ | ||
262 | rb->lcd_drawrect(XOFS-1, YOFS-1, BOARD_WIDTH+2, BOARD_HEIGHT+2); | ||
263 | |||
264 | /* Draw the gridlines */ | ||
265 | for (r=0, x=XOFS, y=YOFS; r<=BOARD_SIZE; | ||
266 | r++, x+=CELL_WIDTH+LINE_THCK, y+=CELL_HEIGHT+LINE_THCK) { | ||
267 | rb->lcd_hline(XOFS, XOFS+BOARD_WIDTH-1, y); | ||
268 | rb->lcd_vline(x, YOFS, YOFS+BOARD_HEIGHT-1); | ||
269 | } | ||
270 | |||
271 | /* Draw the stones. This is not the most efficient way but more readable */ | ||
272 | for (r=0; r<BOARD_SIZE; r++) { | ||
273 | y = CELL_Y(r); | ||
274 | for (c=0; c<BOARD_SIZE; c++) { | ||
275 | x = CELL_X(c); | ||
276 | reversi_gui_draw_cell(x, y, game.board[r][c]); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* Draw the cursor */ | ||
281 | reversi_gui_display_cursor(cur_row, cur_col); | ||
282 | |||
283 | /* Draw the current score */ | ||
284 | reversi_count_occupied_cells(&game, &r, &c); | ||
285 | rb->lcd_getstringsize("x", &x_width, &x_height); | ||
286 | |||
287 | x = LEGEND_X(0); | ||
288 | y = LEGEND_Y(0); | ||
289 | reversi_gui_draw_cell(x, y, BLACK); | ||
290 | rb->snprintf(buf, sizeof(buf), "%d", c); | ||
291 | y += (CELL_HEIGHT-x_height) / 2; | ||
292 | rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf); | ||
293 | |||
294 | y = LEGEND_Y(1); | ||
295 | reversi_gui_draw_cell(x, y, WHITE); | ||
296 | rb->snprintf(buf, sizeof(buf), "%d", r); | ||
297 | y += (CELL_HEIGHT-x_height) / 2; | ||
298 | rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf); | ||
299 | |||
300 | /* Draw the box around the current player */ | ||
301 | r = (cur_player == BLACK ? 0 : 1); | ||
302 | y = LEGEND_Y(r); | ||
303 | rb->lcd_drawrect(x-1, y-1, CELL_WIDTH+2, CELL_HEIGHT+2); | ||
304 | |||
305 | /* Update the screen */ | ||
306 | rb->lcd_update(); | ||
307 | } | ||
308 | |||
309 | |||
310 | /* | ||
311 | * Menu related stuff | ||
312 | */ | ||
313 | |||
314 | /* Menu entries and the corresponding values for cursor wrap mode */ | ||
315 | #define MENU_TEXT_WRAP_MODE "Cursor wrap mode" | ||
316 | static const struct opt_items cursor_wrap_mode_settings[] = { | ||
317 | { "Flat board", NULL }, | ||
318 | { "Sphere", NULL }, | ||
319 | { "Torus", NULL }, | ||
320 | }; | ||
321 | static const cursor_wrap_mode_t cursor_wrap_mode_values[3] = { | ||
322 | WRAP_FLAT, WRAP_SPHERE, WRAP_TORUS }; | ||
323 | |||
324 | |||
325 | /* Menu entries and the corresponding values for available strategies */ | ||
326 | #define MENU_TEXT_STRAT_WHITE "Strategy for white" | ||
327 | #define MENU_TEXT_STRAT_BLACK "Strategy for black" | ||
328 | |||
329 | static struct opt_items strategy_settings[] = { | ||
330 | { "Human", NULL }, | ||
331 | { "Silly robot", NULL }, | ||
332 | { "Smart robot", NULL }, | ||
333 | }; | ||
334 | static const game_strategy_t * const strategy_values[] = { | ||
335 | &strategy_human, &strategy_novice, &strategy_expert }; | ||
336 | |||
337 | |||
338 | /* Sets the strategy for the specified player. 'player' is the | ||
339 | pointer to the player to set the strategy for (actually, | ||
340 | either white_strategy or black_strategy). propmpt is the | ||
341 | text to show as the prompt in the menu */ | ||
342 | static bool reversi_gui_choose_strategy( | ||
343 | const game_strategy_t **player, const char *prompt) { | ||
344 | int index = 0, i; | ||
345 | int num_items = sizeof(strategy_settings)/sizeof(strategy_settings[0]); | ||
346 | bool result; | ||
347 | |||
348 | for (i = 0; i < num_items; i++) { | ||
349 | if ((*player) == strategy_values[i]) { | ||
350 | index = i; | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | result = rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL); | ||
355 | (*player) = strategy_values[index]; | ||
356 | |||
357 | return result; | ||
358 | } | ||
359 | |||
360 | |||
361 | /* Returns true iff USB ws connected while in the menu */ | ||
362 | static bool reversi_gui_menu(void) { | ||
363 | int m, index, num_items, i; | ||
364 | int result; | ||
365 | |||
366 | static const struct menu_item items[] = { | ||
367 | { "Start new game", NULL }, | ||
368 | { "Pass the move", NULL }, | ||
369 | { MENU_TEXT_STRAT_BLACK, NULL }, | ||
370 | { MENU_TEXT_STRAT_WHITE, NULL }, | ||
371 | { MENU_TEXT_WRAP_MODE, NULL }, | ||
372 | { "Quit", NULL }, | ||
373 | }; | ||
374 | |||
375 | m = menu_init(rb, items, sizeof(items) / sizeof(*items), | ||
376 | NULL, NULL, NULL, NULL); | ||
377 | |||
378 | result = menu_show(m); | ||
379 | |||
380 | switch (result) { | ||
381 | case 0: /* Start a new game */ | ||
382 | reversi_gui_init(); | ||
383 | break; | ||
384 | |||
385 | case 1: /* Pass the move to the partner */ | ||
386 | cur_player = reversi_flipped_color(cur_player); | ||
387 | break; | ||
388 | |||
389 | case 2: /* Strategy for black */ | ||
390 | reversi_gui_choose_strategy(&black_strategy, MENU_TEXT_STRAT_BLACK); | ||
391 | break; | ||
392 | |||
393 | case 3: /* Strategy for white */ | ||
394 | reversi_gui_choose_strategy(&white_strategy, MENU_TEXT_STRAT_WHITE); | ||
395 | break; | ||
396 | |||
397 | case 4: /* Cursor wrap mode */ | ||
398 | num_items = sizeof(cursor_wrap_mode_values)/sizeof(cursor_wrap_mode_values[0]); | ||
399 | index = 0; | ||
400 | for (i = 0; i < num_items; i++) { | ||
401 | if (cursor_wrap_mode == cursor_wrap_mode_values[i]) { | ||
402 | index = i; | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | rb->set_option(MENU_TEXT_WRAP_MODE, &index, INT, | ||
407 | cursor_wrap_mode_settings, 3, NULL); | ||
408 | cursor_wrap_mode = cursor_wrap_mode_values[index]; | ||
409 | break; | ||
410 | |||
411 | case 5: /* Quit */ | ||
412 | quit_plugin = true; | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | menu_exit(m); | ||
417 | |||
418 | return (result == MENU_ATTACHED_USB); | ||
419 | } | ||
420 | |||
421 | |||
422 | /* Calculates the new cursor position if the user wants to move it | ||
423 | * vertically as specified by delta. Current wrap mode is respected. | ||
424 | * The cursor is not actually moved. | ||
425 | * | ||
426 | * Returns true iff the cursor would be really moved. In any case, the | ||
427 | * new cursor position is stored in (new_row, new_col). | ||
428 | */ | ||
429 | static bool reversi_gui_cursor_pos_vmove(int row_delta, int *new_row, int *new_col) { | ||
430 | *new_row = cur_row + row_delta; | ||
431 | *new_col = cur_col; | ||
432 | |||
433 | if (*new_row < 0) { | ||
434 | switch (cursor_wrap_mode) { | ||
435 | case WRAP_FLAT: | ||
436 | *new_row = cur_row; | ||
437 | break; | ||
438 | case WRAP_SPHERE: | ||
439 | *new_row = BOARD_SIZE - 1; | ||
440 | break; | ||
441 | case WRAP_TORUS: | ||
442 | *new_row = BOARD_SIZE - 1; | ||
443 | (*new_col)--; | ||
444 | if (*new_col < 0) { | ||
445 | *new_col = BOARD_SIZE - 1; | ||
446 | } | ||
447 | break; | ||
448 | } | ||
449 | } else if (*new_row >= BOARD_SIZE) { | ||
450 | switch (cursor_wrap_mode) { | ||
451 | case WRAP_FLAT: | ||
452 | *new_row = cur_row; | ||
453 | break; | ||
454 | case WRAP_SPHERE: | ||
455 | *new_row = 0; | ||
456 | break; | ||
457 | case WRAP_TORUS: | ||
458 | *new_row = 0; | ||
459 | (*new_col)++; | ||
460 | if (*new_col >= BOARD_SIZE) { | ||
461 | *new_col = 0; | ||
462 | } | ||
463 | break; | ||
464 | } | ||
465 | } | ||
466 | |||
467 | return (cur_row != (*new_row)) || (cur_col != (*new_col)); | ||
468 | } | ||
469 | |||
470 | |||
471 | /* Calculates the new cursor position if the user wants to move it | ||
472 | * horisontally as specified by delta. Current wrap mode is respected. | ||
473 | * The cursor is not actually moved. | ||
474 | * | ||
475 | * Returns true iff the cursor would be really moved. In any case, the | ||
476 | * new cursor position is stored in (new_row, new_col). | ||
477 | */ | ||
478 | static bool reversi_gui_cursor_pos_hmove(int col_delta, int *new_row, int *new_col) { | ||
479 | *new_row = cur_row; | ||
480 | *new_col = cur_col + col_delta; | ||
481 | |||
482 | if (*new_col < 0) { | ||
483 | switch (cursor_wrap_mode) { | ||
484 | case WRAP_FLAT: | ||
485 | *new_col = cur_col; | ||
486 | break; | ||
487 | case WRAP_SPHERE: | ||
488 | *new_col = BOARD_SIZE - 1; | ||
489 | break; | ||
490 | case WRAP_TORUS: | ||
491 | *new_col = BOARD_SIZE - 1; | ||
492 | (*new_row)--; | ||
493 | if (*new_row < 0) { | ||
494 | *new_row = BOARD_SIZE - 1; | ||
495 | } | ||
496 | break; | ||
497 | } | ||
498 | } else if (*new_col >= BOARD_SIZE) { | ||
499 | switch (cursor_wrap_mode) { | ||
500 | case WRAP_FLAT: | ||
501 | *new_col = cur_col; | ||
502 | break; | ||
503 | case WRAP_SPHERE: | ||
504 | *new_col = 0; | ||
505 | break; | ||
506 | case WRAP_TORUS: | ||
507 | *new_col = 0; | ||
508 | (*new_row)++; | ||
509 | if (*new_row >= BOARD_SIZE) { | ||
510 | *new_row = 0; | ||
511 | } | ||
512 | break; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | return (cur_row != (*new_row)) || (cur_col != (*new_col)); | ||
517 | } | ||
518 | |||
519 | |||
520 | /* Actually moves the cursor to the new position and updates the screen */ | ||
521 | static void reversi_gui_move_cursor(int new_row, int new_col) { | ||
522 | int old_row, old_col; | ||
523 | |||
524 | old_row = cur_row; | ||
525 | old_col = cur_col; | ||
526 | |||
527 | cur_row = new_row; | ||
528 | cur_col = new_col; | ||
529 | |||
530 | /* Only update the changed cells since there are no global changes */ | ||
531 | reversi_gui_display_cursor(old_row, old_col); | ||
532 | reversi_gui_display_cursor(new_row, new_col); | ||
533 | } | ||
534 | |||
535 | |||
536 | /* plugin entry point */ | ||
537 | enum plugin_status plugin_start(struct plugin_api *api, void *parameter) { | ||
538 | bool exit, draw_screen; | ||
539 | int button; | ||
540 | int lastbutton = BUTTON_NONE; | ||
541 | int row, col; | ||
542 | int w_cnt, b_cnt; | ||
543 | char msg_buf[30]; | ||
544 | |||
545 | /* plugin init */ | ||
546 | rb = api; | ||
547 | /* end of plugin init */ | ||
548 | |||
549 | #if LCD_DEPTH > 1 | ||
550 | rb->lcd_set_backdrop(NULL); | ||
551 | rb->lcd_set_foreground(LCD_BLACK); | ||
552 | rb->lcd_set_background(LCD_WHITE); | ||
553 | #endif | ||
554 | |||
555 | /* Avoid compiler warnings */ | ||
556 | (void)parameter; | ||
557 | |||
558 | reversi_gui_init(); | ||
559 | cursor_wrap_mode = WRAP_FLAT; | ||
560 | |||
561 | /* The main game loop */ | ||
562 | exit = false; | ||
563 | quit_plugin = false; | ||
564 | draw_screen = true; | ||
565 | while (!exit && !quit_plugin) { | ||
566 | if (draw_screen) { | ||
567 | reversi_gui_display_board(); | ||
568 | draw_screen = false; | ||
569 | } | ||
570 | button = rb->button_get(true); | ||
571 | |||
572 | switch (button) { | ||
573 | #ifdef REVERSI_BUTTON_QUIT | ||
574 | /* Exit game */ | ||
575 | case REVERSI_BUTTON_QUIT: | ||
576 | exit = true; | ||
577 | break; | ||
578 | #endif | ||
579 | |||
580 | #ifdef REVERSI_BUTTON_ALT_MAKE_MOVE | ||
581 | case REVERSI_BUTTON_ALT_MAKE_MOVE: | ||
582 | #endif | ||
583 | case REVERSI_BUTTON_MAKE_MOVE: | ||
584 | #ifdef REVERSI_BUTTON_MAKE_MOVE_PRE | ||
585 | if ((button == REVERSI_BUTTON_MAKE_MOVE) | ||
586 | && (lastbutton != REVERSI_BUTTON_MAKE_MOVE_PRE)) | ||
587 | break; | ||
588 | #endif | ||
589 | if (reversi_make_move(&game, cur_row, cur_col, cur_player) > 0) { | ||
590 | /* Move was made. Global changes on the board are possible */ | ||
591 | draw_screen = true; /* Redraw the screen next time */ | ||
592 | cur_player = reversi_flipped_color(cur_player); | ||
593 | if (reversi_game_is_finished(&game)) { | ||
594 | reversi_count_occupied_cells(&game, &w_cnt, &b_cnt); | ||
595 | rb->snprintf(msg_buf, sizeof(msg_buf), | ||
596 | "Game over. %s have won.", | ||
597 | (w_cnt>b_cnt?"WHITE":"BLACK")); | ||
598 | rb->splash(HZ*2, msg_buf); | ||
599 | draw_screen = true; /* Must update screen after splash */ | ||
600 | } | ||
601 | } else { | ||
602 | /* An attempt to make an invalid move */ | ||
603 | rb->splash(HZ/2, "Illegal move!"); | ||
604 | draw_screen = true; | ||
605 | /* Ignore any button presses during the splash */ | ||
606 | rb->button_clear_queue(); | ||
607 | } | ||
608 | break; | ||
609 | |||
610 | /* Move cursor left */ | ||
611 | case REVERSI_BUTTON_LEFT: | ||
612 | case (REVERSI_BUTTON_LEFT | BUTTON_REPEAT): | ||
613 | if (reversi_gui_cursor_pos_hmove(-1, &row, &col)) { | ||
614 | reversi_gui_move_cursor(row, col); | ||
615 | } | ||
616 | break; | ||
617 | |||
618 | /* Move cursor right */ | ||
619 | case REVERSI_BUTTON_RIGHT: | ||
620 | case (REVERSI_BUTTON_RIGHT | BUTTON_REPEAT): | ||
621 | if (reversi_gui_cursor_pos_hmove(1, &row, &col)) { | ||
622 | reversi_gui_move_cursor(row, col); | ||
623 | } | ||
624 | break; | ||
625 | |||
626 | /* Move cursor up */ | ||
627 | case REVERSI_BUTTON_UP: | ||
628 | case (REVERSI_BUTTON_UP | BUTTON_REPEAT): | ||
629 | if (reversi_gui_cursor_pos_vmove(-1, &row, &col)) { | ||
630 | reversi_gui_move_cursor(row, col); | ||
631 | } | ||
632 | break; | ||
633 | |||
634 | /* Move cursor down */ | ||
635 | case REVERSI_BUTTON_DOWN: | ||
636 | case (REVERSI_BUTTON_DOWN | BUTTON_REPEAT): | ||
637 | if (reversi_gui_cursor_pos_vmove(1, &row, &col)) { | ||
638 | reversi_gui_move_cursor(row, col); | ||
639 | } | ||
640 | break; | ||
641 | |||
642 | case REVERSI_BUTTON_MENU: | ||
643 | #ifdef REVERSI_BUTTON_MENU_PRE | ||
644 | if (lastbutton != REVERSI_BUTTON_MENU_PRE) { | ||
645 | break; | ||
646 | } | ||
647 | #endif | ||
648 | if (reversi_gui_menu()) { | ||
649 | return PLUGIN_USB_CONNECTED; | ||
650 | } | ||
651 | draw_screen = true; | ||
652 | break; | ||
653 | |||
654 | default: | ||
655 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { | ||
656 | /* Quit if USB has been connected */ | ||
657 | return PLUGIN_USB_CONNECTED; | ||
658 | } | ||
659 | break; | ||
660 | } | ||
661 | if (button != BUTTON_NONE) { | ||
662 | lastbutton = button; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | return PLUGIN_OK; | ||
667 | } | ||
668 | |||
669 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _REVERSI_GUI_H | ||
21 | #define _REVERSI_GUI_H | ||
22 | |||
23 | #include "plugin.h" | ||
24 | |||
25 | #define GAME_FILE PLUGIN_DIR "/reversi.rev" | ||
26 | |||
27 | /* variable button definitions */ | ||
28 | #if CONFIG_KEYPAD == RECORDER_PAD | ||
29 | #define REVERSI_BUTTON_QUIT BUTTON_OFF | ||
30 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
31 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
32 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
33 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
34 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_PLAY | ||
35 | #define REVERSI_BUTTON_MENU BUTTON_F1 | ||
36 | |||
37 | #elif CONFIG_KEYPAD == ONDIO_PAD | ||
38 | #define REVERSI_BUTTON_QUIT BUTTON_OFF | ||
39 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
40 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
41 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
42 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
43 | #define REVERSI_BUTTON_MAKE_MOVE_PRE BUTTON_MENU | ||
44 | #define REVERSI_BUTTON_MAKE_MOVE (BUTTON_MENU | BUTTON_REL) | ||
45 | #define REVERSI_BUTTON_ALT_MAKE_MOVE (BUTTON_MENU | BUTTON_DOWN) | ||
46 | #define REVERSI_BUTTON_MENU_PRE BUTTON_MENU | ||
47 | #define REVERSI_BUTTON_MENU (BUTTON_MENU | BUTTON_REPEAT) | ||
48 | |||
49 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ | ||
50 | (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
51 | #define REVERSI_BUTTON_QUIT BUTTON_OFF | ||
52 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
53 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
54 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
55 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
56 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT | ||
57 | #define REVERSI_BUTTON_MENU BUTTON_MODE | ||
58 | |||
59 | #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ | ||
60 | (CONFIG_KEYPAD == IPOD_3G_PAD) | ||
61 | #define REVERSI_BUTTON_UP BUTTON_SCROLL_BACK | ||
62 | #define REVERSI_BUTTON_DOWN BUTTON_SCROLL_FWD | ||
63 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
64 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
65 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT | ||
66 | #define REVERSI_BUTTON_MENU BUTTON_MENU | ||
67 | |||
68 | #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) | ||
69 | #define REVERSI_BUTTON_QUIT BUTTON_POWER | ||
70 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
71 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
72 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
73 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
74 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT | ||
75 | #define REVERSI_BUTTON_MENU BUTTON_PLAY | ||
76 | |||
77 | #elif (CONFIG_KEYPAD == GIGABEAT_PAD) | ||
78 | #define REVERSI_BUTTON_QUIT BUTTON_A | ||
79 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
80 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
81 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
82 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
83 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT | ||
84 | #define REVERSI_BUTTON_MENU BUTTON_MENU | ||
85 | |||
86 | #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) | ||
87 | #define REVERSI_BUTTON_QUIT BUTTON_POWER | ||
88 | #define REVERSI_BUTTON_UP BUTTON_SCROLL_UP | ||
89 | #define REVERSI_BUTTON_DOWN BUTTON_SCROLL_DOWN | ||
90 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
91 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
92 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_REW | ||
93 | #define REVERSI_BUTTON_MENU BUTTON_PLAY | ||
94 | |||
95 | #elif (CONFIG_KEYPAD == SANSA_E200_PAD) | ||
96 | #define REVERSI_BUTTON_QUIT BUTTON_POWER | ||
97 | #define REVERSI_BUTTON_UP BUTTON_UP | ||
98 | #define REVERSI_BUTTON_DOWN BUTTON_DOWN | ||
99 | #define REVERSI_BUTTON_LEFT BUTTON_LEFT | ||
100 | #define REVERSI_BUTTON_RIGHT BUTTON_RIGHT | ||
101 | #define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT | ||
102 | #define REVERSI_BUTTON_MENU (BUTTON_SELECT|BUTTON_REPEAT) | ||
103 | |||
104 | #elif | ||
105 | #error REVERSI: Unsupported keypad | ||
106 | #endif | ||
107 | |||
108 | |||
109 | /* Modes for the cursor behaviour at the board edges */ | ||
110 | typedef enum _cursor_wrap_mode_t { | ||
111 | WRAP_FLAT, /* No wrapping */ | ||
112 | WRAP_SPHERE, /* (7,7) > right > (7,0); (7,7) > down > (0,7) */ | ||
113 | WRAP_TORUS, /* (7,7) > right > (0,0); (7,7) > down > (0,0) */ | ||
114 | } cursor_wrap_mode_t; | ||
115 | |||
116 | |||
117 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include "reversi-strategy.h" | ||
21 | #include <stddef.h> | ||
22 | |||
23 | |||
24 | /* Implementation of a rather weak player strategy */ | ||
25 | static move_t novice_move_func(const reversi_board_t *game, int color) { | ||
26 | /* TODO: Implement novice_move_func */ | ||
27 | (void)game; | ||
28 | (void)color; | ||
29 | return MOVE_INVALID; | ||
30 | } | ||
31 | |||
32 | |||
33 | /* Implementation of a good player strategy */ | ||
34 | static move_t expert_move_func(const reversi_board_t *game, int color) { | ||
35 | /* TODO: Implement expert_move_func */ | ||
36 | (void)game; | ||
37 | (void)color; | ||
38 | return MOVE_INVALID; | ||
39 | } | ||
40 | |||
41 | |||
42 | |||
43 | /* Strategy that requires interaction with the user */ | ||
44 | const game_strategy_t strategy_human = { | ||
45 | false, | ||
46 | NULL | ||
47 | }; | ||
48 | |||
49 | /* Robot that plays not very well (novice) */ | ||
50 | const game_strategy_t strategy_novice = { | ||
51 | true, | ||
52 | novice_move_func | ||
53 | }; | ||
54 | |||
55 | /* Robot that is hard to beat (expert) */ | ||
56 | const game_strategy_t strategy_expert = { | ||
57 | true, | ||
58 | expert_move_func | ||
59 | }; | ||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2006 Alexander Levin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _REVERSI_STRATEGY_H | ||
21 | #define _REVERSI_STRATEGY_H | ||
22 | |||
23 | #include "reversi-game.h" | ||
24 | |||
25 | |||
26 | /* Function for making a move. Is called only for robots. | ||
27 | Should return the game history entry for the advised move if | ||
28 | a move has been considered or HISTORY_INVALID_ENTRY if no move | ||
29 | has been considered. The board should not be modified. */ | ||
30 | typedef move_t (*move_func_t)(const reversi_board_t *game, int color); | ||
31 | |||
32 | /* A playing party/strategy */ | ||
33 | typedef struct _game_strategy_t { | ||
34 | const bool is_robot; /* Is the player a robot or does it require user input? */ | ||
35 | move_func_t move_func; /* Function for advicing a move */ | ||
36 | } game_strategy_t; | ||
37 | |||
38 | |||
39 | /* --- Possible playing strategies --- */ | ||
40 | extern const game_strategy_t strategy_human; | ||
41 | extern const game_strategy_t strategy_novice; | ||
42 | extern const game_strategy_t strategy_expert; | ||
43 | |||
44 | #endif | ||