1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
|
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (c) 2006 Alexander Levin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* Reversi. Code is heavily based on othello code by Claudio Clemens which is
* copyright (c) 2003-2006 Claudio Clemens <asturio at gmx dot net> and is
* released under the GNU General Public License as published by the Free
* Software Foundation; either version 2, or (at your option) any later version.
*/
#include <stddef.h>
#include "reversi-game.h"
/*
* Constants for directions. The values are chosen so that
* they can be bit combined.
*/
#define DIR_UP 1 /* UP */
#define DIR_DO 2 /* DOWN */
#define DIR_LE 4 /* LEFT */
#define DIR_RI 8 /* RIGHT */
#define DIR_UL 16 /* UP LEFT */
#define DIR_UR 32 /* UP RIGHT */
#define DIR_DL 64 /* DOWN LEFT */
#define DIR_DR 128 /* DOWN RIGHT */
/* Array of directions for easy iteration through all of them */
static int DIRECTIONS[] =
{DIR_UP, DIR_DO, DIR_LE, DIR_RI, DIR_UL, DIR_UR, DIR_DL, DIR_DR};
#define NUM_OF_DIRECTIONS 8
/* Initializes a reversi game */
void reversi_init_game(reversi_board_t *game) {
int r, c;
for (r = 0; r < BOARD_SIZE; r++) {
for (c = 0; c < BOARD_SIZE; c++) {
game->board[r][c] = FREE;
}
}
game->board[3][3] = WHITE;
game->board[4][4] = WHITE;
game->board[3][4] = BLACK;
game->board[4][3] = BLACK;
/* Invalidate the history */
c = sizeof(game->history) / sizeof(game->history[0]);
for (r = 0; r < c; r++) {
game->history[r] = MOVE_INVALID;
}
}
/* Returns the 'flipped' color, e.g. WHITE for BLACK and vice versa */
int reversi_flipped_color(const int color) {
switch (color) {
case WHITE:
return BLACK;
case BLACK:
return WHITE;
default:
return FREE;
}
}
/* Counts and returns the number of occupied cells on the board.
* If white_count and/or black_count is not null, the number of
* white/black stones is placed there. */
int reversi_count_occupied_cells(const reversi_board_t *game,
int *white_count, int *black_count) {
int w_cnt, b_cnt, r, c;
w_cnt = b_cnt = 0;
for (r = 0; r < BOARD_SIZE; r++) {
for (c = 0; c < BOARD_SIZE; c++) {
if (game->board[r][c] == WHITE) {
w_cnt++;
} else if (game->board[r][c] == BLACK) {
b_cnt++;
}
}
}
if (white_count != NULL) {
*white_count = w_cnt;
}
if (black_count != NULL) {
*black_count = b_cnt;
}
return w_cnt + b_cnt;
}
/* Returns the number of free cells on the board */
static int reversi_count_free_cells(const reversi_board_t *game) {
int cnt;
cnt = reversi_count_occupied_cells(game, NULL, NULL);
return BOARD_SIZE*BOARD_SIZE - cnt;
}
/* Checks whether the game is finished. That means that nobody
* can make a move. Note that the implementation is not correct
* as a game may be finished even if there are free cells
*/
bool reversi_game_is_finished(const reversi_board_t *game, int player) {
return (reversi_count_free_cells(game) == 0)
|| (reversi_count_passes(game,player) == 2);
}
/* 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 available for the specified player */
int reversi_count_player_available_moves(const reversi_board_t *game,
const int player) {
int cnt = 0, row, col;
for(row=0;row<BOARD_SIZE;row++) {
for(col=0;col<BOARD_SIZE;col++) {
if(reversi_is_valid_move(game, row, col, player)) cnt++;
}
}
return cnt;
}
/* Returns the number of players who HAVE to pass (2 == game is stuck) */
int reversi_count_passes(const reversi_board_t *game, const int player) {
if(reversi_count_player_available_moves(game,player)==0) {
if(reversi_count_player_available_moves(game,
reversi_flipped_color(player))==0) {
return 2;
}
return 1;
}
return 0;
}
/* 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.
*/
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;
}
|