summaryrefslogtreecommitdiff
path: root/apps/plugins/reversi/reversi-game.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/reversi/reversi-game.c')
-rw-r--r--apps/plugins/reversi/reversi-game.c370
1 files changed, 370 insertions, 0 deletions
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 */
44static 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 */
50void 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 */
71int 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. */
88int 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 */
112static 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 */
123bool 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 */
133int 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 */
145int 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 */
155static 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 */
170int 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 */
176int 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 */
184static 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 */
191static 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 */
210static 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 */
233static 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 */
276static 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 */
306static 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 */
345int 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}