diff options
Diffstat (limited to 'apps/plugins/reversi/reversi-game.c')
-rw-r--r-- | apps/plugins/reversi/reversi-game.c | 370 |
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 */ | ||
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 | } | ||