reversi 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -4
- data/black +0 -0
- data/ext/reversi/bit_board_functions.c +226 -0
- data/ext/reversi/bit_board_functions.h +26 -0
- data/ext/reversi/board.c +253 -0
- data/ext/reversi/board.h +27 -0
- data/ext/reversi/reversi.c +15 -385
- data/ext/reversi/reversi.h +8 -23
- data/lib/reversi/board.rb +23 -76
- data/lib/reversi/game.rb +10 -8
- data/lib/reversi/player/alpha_beta_ai.rb +2 -2
- data/lib/reversi/player/base_player.rb +9 -23
- data/lib/reversi/player/human.rb +11 -2
- data/lib/reversi/player/min_max_ai.rb +2 -2
- data/lib/reversi/player/nega_max_ai.rb +2 -2
- data/lib/reversi/player/random_ai.rb +1 -1
- data/lib/reversi/version.rb +1 -1
- data/spec/base_player_spec.rb +9 -23
- data/spec/board_spec.rb +27 -30
- data/spec/game_spec.rb +56 -61
- data/spec/player_spec.rb +5 -5
- data/white +0 -0
- metadata +8 -2
data/ext/reversi/reversi.c
CHANGED
|
@@ -1,396 +1,26 @@
|
|
|
1
1
|
#include "reversi.h"
|
|
2
2
|
|
|
3
|
-
struct stack{
|
|
4
|
-
int stack_columns[10][10];
|
|
5
|
-
struct stack *next;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
struct stack *head = NULL;
|
|
9
|
-
|
|
10
|
-
struct board {
|
|
11
|
-
int columns[10][10];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
3
|
void Init_reversi(void) {
|
|
15
4
|
VALUE reversi = rb_define_module("Reversi");
|
|
16
5
|
VALUE reversi_board = rb_define_class_under(reversi, "Board", rb_cObject);
|
|
17
6
|
|
|
18
|
-
rb_define_alloc_func(reversi_board,
|
|
19
|
-
|
|
20
|
-
/* The getter method for a Reversi::Board object. */
|
|
21
|
-
rb_define_method(reversi_board, "columns", board_columns_getter, 0);
|
|
22
|
-
rb_define_method(reversi_board, "stack", board_stack_getter, 0);
|
|
23
|
-
|
|
24
|
-
/* The instance method for a Reversi::Board object. */
|
|
25
|
-
rb_define_method(reversi_board, "undo!", board_undo, 0);
|
|
7
|
+
rb_define_alloc_func(reversi_board, bit_board_alloc);
|
|
26
8
|
|
|
27
9
|
/* These private methods are used in the board.rb file. */
|
|
28
|
-
rb_define_private_method(reversi_board, "
|
|
29
|
-
rb_define_private_method(reversi_board, "
|
|
30
|
-
rb_define_private_method(reversi_board, "
|
|
31
|
-
rb_define_private_method(reversi_board, "
|
|
32
|
-
rb_define_private_method(reversi_board, "board_at", board_at, 2);
|
|
33
|
-
rb_define_private_method(reversi_board, "board_count_disks", board_count_disks, 1);
|
|
34
|
-
rb_define_private_method(reversi_board, "board_next_moves", board_next_moves, 1);
|
|
35
|
-
rb_define_private_method(reversi_board, "board_put_disk", board_put_disk, 3);
|
|
36
|
-
rb_define_private_method(reversi_board, "board_flip_disks", board_flip_disks, 3);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static VALUE board_alloc(VALUE class) {
|
|
40
|
-
struct board *ptr = ALLOC(struct board);
|
|
41
|
-
return Data_Wrap_Struct(class, 0, -1, ptr);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
static VALUE board_initialize(VALUE self) {
|
|
45
|
-
int x, y;
|
|
46
|
-
struct board *ptr;
|
|
47
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
48
|
-
|
|
49
|
-
reset_stack();
|
|
50
|
-
head = NULL;
|
|
51
|
-
|
|
52
|
-
for(x = 0; x < 10; x++) {
|
|
53
|
-
for(y = 0; y < 10; y++) {
|
|
54
|
-
if(x == 0 || x == 9) ptr->columns[x][y] = 2;
|
|
55
|
-
else if(y == 0 || y == 9) ptr->columns[x][y] = 2;
|
|
56
|
-
else ptr->columns[x][y] = 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return Qnil;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/*
|
|
63
|
-
* The getter method for the instance variable `columns`.
|
|
64
|
-
*
|
|
65
|
-
* @return [Array]
|
|
66
|
-
*/
|
|
67
|
-
static VALUE board_columns_getter(VALUE self) {
|
|
68
|
-
VALUE column = rb_ary_new();
|
|
69
|
-
VALUE columns = rb_ary_new();
|
|
70
|
-
int x, y;
|
|
71
|
-
struct board *ptr;
|
|
72
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
73
|
-
|
|
74
|
-
for(x = 0; x < 10; x++) {
|
|
75
|
-
column = rb_ary_new();
|
|
76
|
-
for(y = 0; y < 10; y++) rb_ary_push(column, INT2FIX(ptr->columns[x][y]));
|
|
77
|
-
rb_ary_push(columns, column);
|
|
78
|
-
}
|
|
79
|
-
return columns;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/*
|
|
83
|
-
* The getter method for the instance variable `stack`.
|
|
84
|
-
*
|
|
85
|
-
* @return [Array]
|
|
86
|
-
*/
|
|
87
|
-
static VALUE board_stack_getter(VALUE self) {
|
|
88
|
-
VALUE stack = rb_ary_new();
|
|
89
|
-
VALUE column = rb_ary_new();
|
|
90
|
-
VALUE columns = rb_ary_new();
|
|
91
|
-
int x, y;
|
|
92
|
-
struct stack *sp;
|
|
93
|
-
|
|
94
|
-
for(sp = head; sp != NULL; sp = sp->next) {
|
|
95
|
-
columns = rb_ary_new();
|
|
96
|
-
for(x = 0; x < 10; x++) {
|
|
97
|
-
column = rb_ary_new();
|
|
98
|
-
for(y = 0; y < 10; y++) rb_ary_push(column, INT2FIX(sp->stack_columns[x][y]));
|
|
99
|
-
rb_ary_push(columns, column);
|
|
100
|
-
}
|
|
101
|
-
rb_ary_push(stack, columns);
|
|
102
|
-
}
|
|
103
|
-
return stack;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/*
|
|
107
|
-
* Pops an array of the game board off of the stack,
|
|
108
|
-
* and that is stored in the instance variable `columns`.
|
|
109
|
-
*/
|
|
110
|
-
static VALUE board_undo(VALUE self) {
|
|
111
|
-
struct board *ptr;
|
|
112
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
113
|
-
pop_stack(ptr->columns);
|
|
114
|
-
return Qnil;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/*
|
|
118
|
-
* Pushes an array of the game board onto a stack.
|
|
119
|
-
*
|
|
120
|
-
* @param limit [Integer] An upper limit size of the stack.
|
|
121
|
-
*/
|
|
122
|
-
static VALUE board_push_stack(VALUE self, VALUE limit) {
|
|
123
|
-
push_stack(self, FIX2INT(limit));
|
|
124
|
-
return Qnil;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/*
|
|
128
|
-
* Returns an array containing the coordinates of each color.
|
|
129
|
-
* The arrays are stored `black`, `while`, and `none` in that order.
|
|
130
|
-
*
|
|
131
|
-
* @return [Array]
|
|
132
|
-
*/
|
|
133
|
-
static VALUE board_status(VALUE self) {
|
|
134
|
-
VALUE black = rb_ary_new();
|
|
135
|
-
VALUE white = rb_ary_new();
|
|
136
|
-
VALUE none = rb_ary_new();
|
|
137
|
-
VALUE position = rb_ary_new();
|
|
138
|
-
VALUE status = rb_ary_new();
|
|
139
|
-
int x, y;
|
|
140
|
-
struct board *ptr;
|
|
141
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
142
|
-
|
|
143
|
-
for(x = 0; x < 10; x++) {
|
|
144
|
-
for(y = 0; y < 10; y++) {
|
|
145
|
-
switch (ptr->columns[x][y]) {
|
|
146
|
-
case -1:
|
|
147
|
-
rb_ary_push(position, INT2FIX(x));
|
|
148
|
-
rb_ary_push(position, INT2FIX(y));
|
|
149
|
-
rb_ary_push(black, position);
|
|
150
|
-
position = rb_ary_new(); break;
|
|
151
|
-
case 1:
|
|
152
|
-
rb_ary_push(position, INT2FIX(x));
|
|
153
|
-
rb_ary_push(position, INT2FIX(y));
|
|
154
|
-
rb_ary_push(white, position);
|
|
155
|
-
position = rb_ary_new(); break;
|
|
156
|
-
case 0:
|
|
157
|
-
rb_ary_push(position, INT2FIX(x));
|
|
158
|
-
rb_ary_push(position, INT2FIX(y));
|
|
159
|
-
rb_ary_push(none, position);
|
|
160
|
-
position = rb_ary_new(); break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
rb_ary_push(status, black);
|
|
166
|
-
rb_ary_push(status, white);
|
|
167
|
-
rb_ary_push(status, none);
|
|
168
|
-
return status;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/*
|
|
172
|
-
* Returns the openness of the coordinates.
|
|
173
|
-
*
|
|
174
|
-
* @param x [Integer] the column number
|
|
175
|
-
* @param y [Integer] the row number
|
|
176
|
-
* @return [Integer] the openness
|
|
177
|
-
*/
|
|
178
|
-
static VALUE board_openness(VALUE self, VALUE x, VALUE y) {
|
|
179
|
-
int dx, dy, sum = 0;
|
|
180
|
-
struct board *ptr;
|
|
181
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
182
|
-
|
|
183
|
-
for(dx = -1; dx < 2; dx++){
|
|
184
|
-
for(dy = -1; dy < 2; dy++){
|
|
185
|
-
if (dx == 0 && dy == 0) continue;
|
|
186
|
-
if (ptr->columns[FIX2INT(x) + dx][FIX2INT(y) + dy] == 0) sum += 1;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return INT2FIX(sum);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/*
|
|
193
|
-
* Returns the disk color of supplied coordinates.
|
|
194
|
-
*
|
|
195
|
-
* @param x [Integer] the column number
|
|
196
|
-
* @param y [Integer] the row number
|
|
197
|
-
* @return [Integer] `black`: -1, `white`: 1, `none`: 0
|
|
198
|
-
*/
|
|
199
|
-
static VALUE board_at(VALUE self, VALUE x, VALUE y) {
|
|
200
|
-
struct board *ptr;
|
|
201
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
202
|
-
return INT2FIX(ptr->columns[FIX2INT(x)][FIX2INT(y)]);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/*
|
|
206
|
-
* Counts the number of the supplied color's disks.
|
|
207
|
-
*
|
|
208
|
-
* @param color [Integer] `black`: -1, `white`: 1, `none`: 0
|
|
209
|
-
* @return [Integer] the sum of the counted disks
|
|
210
|
-
*/
|
|
211
|
-
static VALUE board_count_disks(VALUE self, VALUE color) {
|
|
212
|
-
int x, y, count = 0;
|
|
213
|
-
struct board *ptr;
|
|
214
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
10
|
+
rb_define_private_method(reversi_board, "black_setter", black_setter, 1);
|
|
11
|
+
rb_define_private_method(reversi_board, "white_setter", white_setter, 1);
|
|
12
|
+
rb_define_private_method(reversi_board, "black_getter", black_getter, 0);
|
|
13
|
+
rb_define_private_method(reversi_board, "white_getter", white_getter, 0);
|
|
215
14
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (ptr->columns[x][y] == FIX2INT(color)) count += 1;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return INT2FIX(count);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/*
|
|
225
|
-
* Returns an array of the next moves.
|
|
226
|
-
*
|
|
227
|
-
* @param color [Integer] `black`: -1, `white`: 1, `none`: 0
|
|
228
|
-
* @return [Array] the next moves
|
|
229
|
-
*/
|
|
230
|
-
static VALUE board_next_moves(VALUE self, VALUE color) {
|
|
231
|
-
int x, y;
|
|
232
|
-
VALUE move = rb_ary_new();
|
|
233
|
-
VALUE moves = rb_ary_new();
|
|
234
|
-
|
|
235
|
-
for(x = 1; x < 9; x++){
|
|
236
|
-
for(y = 1; y < 9; y++){
|
|
237
|
-
if (can_put(self, x, y, FIX2INT(color)) == 1){
|
|
238
|
-
move = rb_ary_new();
|
|
239
|
-
rb_ary_push(move, INT2FIX(x));
|
|
240
|
-
rb_ary_push(move, INT2FIX(y));
|
|
241
|
-
rb_ary_push(moves, move);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return moves;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/*
|
|
249
|
-
* Places a supplied color's disk on specified position.
|
|
250
|
-
*
|
|
251
|
-
* @param x [Integer] the column number
|
|
252
|
-
* @param y [Integer] the row number
|
|
253
|
-
* @param color [Integer] `black`: -1, `white`: 1, `none`: 0
|
|
254
|
-
*/
|
|
255
|
-
static VALUE board_put_disk(VALUE self, VALUE x, VALUE y, VALUE color) {
|
|
256
|
-
struct board *ptr;
|
|
257
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
258
|
-
|
|
259
|
-
ptr->columns[FIX2INT(x)][FIX2INT(y)] = FIX2INT(color);
|
|
260
|
-
return Qnil;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/*
|
|
264
|
-
* Flips the opponent's disks between a new disk and another disk of my color.
|
|
265
|
-
* The invalid move has no effect.
|
|
266
|
-
*
|
|
267
|
-
* @param x [Integer] the column number
|
|
268
|
-
* @param y [Integer] the row number
|
|
269
|
-
* @param color [Integer] `black`: -1, `white`: 1, `none`: 0
|
|
270
|
-
*/
|
|
271
|
-
static VALUE board_flip_disks(VALUE self, VALUE x, VALUE y, VALUE color) {
|
|
272
|
-
int dx, dy;
|
|
273
|
-
struct board *ptr;
|
|
274
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
275
|
-
x = FIX2INT(x);
|
|
276
|
-
y = FIX2INT(y);
|
|
277
|
-
color = FIX2INT(color);
|
|
278
|
-
|
|
279
|
-
for(dx = -1; dx < 2; dx++){
|
|
280
|
-
for(dy = -1; dy < 2; dy++){
|
|
281
|
-
if (dx == 0 && dy == 0) continue;
|
|
282
|
-
if (ptr->columns[x + dx][y + dy] == (signed)(-color) &&
|
|
283
|
-
can_flip(self, x, y, dx, dy, color) == 1)
|
|
284
|
-
flip_disk(self, x, y, dx, dy, color);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return Qnil;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/*
|
|
291
|
-
* Flips the opponent's disks on one of these straight lines
|
|
292
|
-
* between a new disk and another disk of my color.
|
|
293
|
-
*/
|
|
294
|
-
void flip_disk(VALUE self, int x, int y, int dx, int dy, int color){
|
|
295
|
-
struct board *ptr;
|
|
296
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
297
|
-
|
|
298
|
-
x += dx; y += dy;
|
|
299
|
-
if (ptr->columns[x][y] == 2 ||
|
|
300
|
-
ptr->columns[x][y] == 0 ||
|
|
301
|
-
ptr->columns[x][y] == color) return;
|
|
302
|
-
ptr->columns[x][y] = color;
|
|
303
|
-
flip_disk(self, x, y, dx, dy, color);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/*
|
|
308
|
-
* Whether or not a player can place a new disk on specified position.
|
|
309
|
-
* Returns `1` if the move is valid.
|
|
310
|
-
*/
|
|
311
|
-
int can_put(VALUE self, int x, int y, int color){
|
|
312
|
-
int dx, dy;
|
|
313
|
-
struct board *ptr;
|
|
314
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
315
|
-
|
|
316
|
-
if (ptr->columns[x][y] != 0) return 0;
|
|
317
|
-
|
|
318
|
-
for(dx = -1; dx < 2; dx++){
|
|
319
|
-
for(dy = -1; dy < 2; dy++){
|
|
320
|
-
if (dx == 0 && dy == 0) continue;
|
|
321
|
-
if (ptr->columns[x + dx][y + dy] == -color){
|
|
322
|
-
if (can_flip(self, x, y, dx, dy, color) == 1) return 1;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return 0;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/*
|
|
330
|
-
* Whether or not a player can flip the opponent's disks.
|
|
331
|
-
*/
|
|
332
|
-
int can_flip(VALUE self, int x, int y, int dx, int dy, int color){
|
|
333
|
-
struct board *ptr;
|
|
334
|
-
Data_Get_Struct(self, struct board, ptr);
|
|
335
|
-
|
|
336
|
-
while(1){
|
|
337
|
-
x += dx; y += dy;
|
|
338
|
-
if (ptr->columns[x][y] == color) return 1;
|
|
339
|
-
if (ptr->columns[x][y] == 2 || ptr->columns[x][y] == 0) break;
|
|
340
|
-
}
|
|
341
|
-
return 0;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
void push_stack(VALUE self, int limit){
|
|
345
|
-
int size = 0;
|
|
346
|
-
struct board *bp;
|
|
347
|
-
struct stack *sp;
|
|
348
|
-
Data_Get_Struct(self, struct board, bp);
|
|
349
|
-
|
|
350
|
-
for(sp = head; sp != NULL; sp = sp->next);
|
|
351
|
-
sp = (struct stack *)malloc(sizeof(struct stack));
|
|
352
|
-
|
|
353
|
-
memcpy(sp->stack_columns, bp->columns, sizeof(bp->columns));
|
|
354
|
-
sp->next = head;
|
|
355
|
-
head = sp;
|
|
356
|
-
|
|
357
|
-
size = stack_size();
|
|
358
|
-
if (size > limit) delete_old();
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
void pop_stack(int ary[10][10]){
|
|
363
|
-
struct stack *p;
|
|
364
|
-
memcpy(ary, head->stack_columns, sizeof(head->stack_columns));
|
|
365
|
-
p = head;
|
|
366
|
-
head = p->next;
|
|
367
|
-
free(p);
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
int stack_size(void){
|
|
372
|
-
int count = 0;
|
|
373
|
-
struct stack *p;
|
|
374
|
-
for(p = head; p != NULL; p = p->next) count++;
|
|
375
|
-
return count;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
void delete_old(void){
|
|
379
|
-
struct stack *p;
|
|
380
|
-
for(p = head; p->next->next != NULL; p = p->next);
|
|
381
|
-
p->next = NULL;
|
|
382
|
-
free(p->next);
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
void reset_stack(void){
|
|
387
|
-
struct stack *p = head;
|
|
388
|
-
struct stack *tmp;
|
|
15
|
+
/* This method is used in the `Reversi::Board.new` method. */
|
|
16
|
+
rb_define_private_method(reversi_board, "board_initialize", board_initialize, 0);
|
|
389
17
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
18
|
+
/* The instance method for a Reversi::Board object. */
|
|
19
|
+
rb_define_method(reversi_board, "status", status, 0);
|
|
20
|
+
rb_define_method(reversi_board, "openness", openness, 2);
|
|
21
|
+
rb_define_method(reversi_board, "at", at, 2);
|
|
22
|
+
rb_define_method(reversi_board, "count_disks", count_disks, 1);
|
|
23
|
+
rb_define_method(reversi_board, "next_moves", next_moves, 1);
|
|
24
|
+
rb_define_method(reversi_board, "put_disk", put_disk, 3);
|
|
25
|
+
rb_define_method(reversi_board, "flip_disks", flip_disks, 3);
|
|
396
26
|
}
|
data/ext/reversi/reversi.h
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
|
+
#ifndef RUBY_REVERSI_H
|
|
2
|
+
#define RUBY_REVERSI_H
|
|
3
|
+
|
|
1
4
|
#include <ruby.h>
|
|
2
5
|
#include <stdlib.h>
|
|
3
6
|
#include <memory.h>
|
|
7
|
+
#include <stdio.h>
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
static VALUE board_alloc(VALUE class);
|
|
8
|
-
static VALUE board_initialize(VALUE self);
|
|
9
|
-
static VALUE board_columns_getter(VALUE self);
|
|
10
|
-
static VALUE board_stack_getter(VALUE self);
|
|
11
|
-
static VALUE board_push_stack(VALUE self, VALUE limit);
|
|
12
|
-
static VALUE board_undo(VALUE self);
|
|
13
|
-
static VALUE board_status(VALUE self);
|
|
14
|
-
static VALUE board_openness(VALUE self, VALUE x, VALUE y);
|
|
15
|
-
static VALUE board_at(VALUE self, VALUE x, VALUE y);
|
|
16
|
-
static VALUE board_count_disks(VALUE self, VALUE color);
|
|
17
|
-
static VALUE board_next_moves(VALUE self, VALUE color);
|
|
18
|
-
static VALUE board_put_disk(VALUE self, VALUE x, VALUE y, VALUE color);
|
|
19
|
-
static VALUE board_flip_disks(VALUE self, VALUE x, VALUE y, VALUE color);
|
|
9
|
+
#include "board.h"
|
|
10
|
+
#include "bit_board_functions.h"
|
|
20
11
|
|
|
21
|
-
void
|
|
22
|
-
int can_put(VALUE self, int x, int y, int color);
|
|
23
|
-
int can_flip(VALUE self, int x, int y, int dx, int dy, int color);
|
|
12
|
+
void Init_reversi(void);
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
void pop_stack(int ary[10][10]);
|
|
27
|
-
int stack_size(void);
|
|
28
|
-
void delete_old(void);
|
|
29
|
-
void reset_stack(void);
|
|
14
|
+
#endif
|
data/lib/reversi/board.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module Reversi
|
|
2
2
|
class Board
|
|
3
|
-
attr_reader :options
|
|
3
|
+
attr_reader :options, :stack
|
|
4
4
|
|
|
5
5
|
DISK = {
|
|
6
6
|
:none => 0,
|
|
7
|
-
:wall => 2,
|
|
8
7
|
:black => -1,
|
|
9
8
|
:white => 1
|
|
10
9
|
}.freeze
|
|
@@ -28,14 +27,17 @@ module Reversi
|
|
|
28
27
|
# @return [Reversi::Board]
|
|
29
28
|
def initialize(options = {})
|
|
30
29
|
@options = options
|
|
30
|
+
@stack = []
|
|
31
|
+
board_initialize
|
|
32
|
+
|
|
31
33
|
[:disk_color_b, :disk_color_w].each do |color|
|
|
32
34
|
if @options[color].is_a?(Symbol) || @options[color].is_a?(String)
|
|
33
35
|
@options[color] = DISK_COLOR[@options[color].to_sym].to_i
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
|
-
|
|
38
|
+
|
|
37
39
|
@options[:initial_position].each do |color, positions|
|
|
38
|
-
positions.each{ |position| put_disk(*position, color) }
|
|
40
|
+
positions.each{ |position| put_disk(*position, DISK[color]) }
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -45,16 +47,16 @@ module Reversi
|
|
|
45
47
|
def to_s
|
|
46
48
|
" #{[*'a'..'h'].join(" ")}\n" <<
|
|
47
49
|
" #{"+---"*8}+\n" <<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
when
|
|
52
|
-
|
|
53
|
-
when 1 then "\e[#{@options[:disk_color_w]}m#{@options[:disk_w]}\e[0m"
|
|
50
|
+
(0..63).map do |i|
|
|
51
|
+
case 1
|
|
52
|
+
when black_getter[63 - i] then "\e[#{@options[:disk_color_b]}m#{@options[:disk_b]}\e[0m"
|
|
53
|
+
when white_getter[63 - i] then "\e[#{@options[:disk_color_w]}m#{@options[:disk_w]}\e[0m"
|
|
54
|
+
else " "
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
|
-
.map{ |e| "| #{e} |" }.
|
|
57
|
-
.
|
|
57
|
+
.map{ |e| "| #{e} |" }.each_slice(8).map(&:join)
|
|
58
|
+
.map{ |line| line.gsub(/\|\|/, "|") }
|
|
59
|
+
.tap{ |lines| break (0..7).map{ |i| " #{i+1} #{lines[i]}" } }
|
|
58
60
|
.join("\n #{"+---"*8}+\n") <<
|
|
59
61
|
"\n #{"+---"*8}+\n"
|
|
60
62
|
end
|
|
@@ -62,72 +64,17 @@ module Reversi
|
|
|
62
64
|
# Pushes an array of the game board onto a stack.
|
|
63
65
|
# The stack size limit is 3(default).
|
|
64
66
|
def push_stack
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# Returns a hash containing the coordinates of each color.
|
|
69
|
-
#
|
|
70
|
-
# @return [Hash{Symbol => Array<Symbol, Integer>}]
|
|
71
|
-
def status
|
|
72
|
-
ary = board_status
|
|
73
|
-
{:black => ary[0], :white => ary[1], :none => ary[2]}
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Returns the openness of the coordinates.
|
|
77
|
-
#
|
|
78
|
-
# @param x [Symbol, Integer] the column number
|
|
79
|
-
# @param y [Integer] the row number
|
|
80
|
-
# @return [Integer] the openness
|
|
81
|
-
def openness(x, y)
|
|
82
|
-
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
83
|
-
board_openness(x, y)
|
|
67
|
+
bb = {:black => black_getter,:white => white_getter}
|
|
68
|
+
@stack.push(Marshal.load(Marshal.dump(bb)))
|
|
69
|
+
@stack.shift if @stack.size > @options[:stack_limit]
|
|
84
70
|
end
|
|
85
71
|
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
93
|
-
DISK.key(board_at(x, y))
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Counts the number of the supplied color's disks.
|
|
97
|
-
#
|
|
98
|
-
# @param color [Symbol]
|
|
99
|
-
# @return [Integer] the sum of the counted disks
|
|
100
|
-
def count_disks(color)
|
|
101
|
-
board_count_disks(DISK[color])
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Returns an array of the next moves.
|
|
105
|
-
#
|
|
106
|
-
# @param color [Symbol]
|
|
107
|
-
# @return [Array<Array<Symbol, Integer>>]
|
|
108
|
-
def next_moves(color)
|
|
109
|
-
board_next_moves(DISK[color])
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Places a supplied color's disk on specified position.
|
|
113
|
-
#
|
|
114
|
-
# @param x [Symbol, Integer] the column number
|
|
115
|
-
# @param y [Integer] the row number
|
|
116
|
-
# @param color [Symbol]
|
|
117
|
-
def put_disk(x, y, color)
|
|
118
|
-
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
119
|
-
board_put_disk(x, y, DISK[color])
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Flips the opponent's disks between a new disk and another disk of my color.
|
|
123
|
-
# The invalid move has no effect.
|
|
124
|
-
#
|
|
125
|
-
# @param x [Symbol, Integer] the column number
|
|
126
|
-
# @param y [Integer] the row number
|
|
127
|
-
# @param color [Symbol]
|
|
128
|
-
def flip_disks(x, y, color)
|
|
129
|
-
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
130
|
-
board_flip_disks(x, y, DISK[color])
|
|
72
|
+
# Pops a hash of the game board off of the stack,
|
|
73
|
+
# and that is stored in the instance variable.(`@bit_board`)
|
|
74
|
+
def undo!
|
|
75
|
+
bb = @stack.pop
|
|
76
|
+
black_setter(bb[:black])
|
|
77
|
+
white_setter(bb[:white])
|
|
131
78
|
end
|
|
132
79
|
end
|
|
133
80
|
end
|
data/lib/reversi/game.rb
CHANGED
|
@@ -25,8 +25,8 @@ module Reversi
|
|
|
25
25
|
@board = Board.new(@options)
|
|
26
26
|
@player_class_b = @player_b
|
|
27
27
|
@player_class_w = @player_w
|
|
28
|
-
@player_b = @player_class_b.new(:black, @board)
|
|
29
|
-
@player_w = @player_class_w.new(:white, @board)
|
|
28
|
+
@player_b = @player_class_b.new(Reversi::Board::DISK[:black], @board)
|
|
29
|
+
@player_w = @player_class_w.new(Reversi::Board::DISK[:white], @board)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# Load the configuration.
|
|
@@ -53,11 +53,13 @@ module Reversi
|
|
|
53
53
|
loop do
|
|
54
54
|
break if game_over?
|
|
55
55
|
@status = @board.status
|
|
56
|
-
@player_b.move(@board)
|
|
56
|
+
@player_b.move(@board)
|
|
57
|
+
check_move(:black)
|
|
57
58
|
show_board if mode[:progress]
|
|
58
59
|
|
|
59
60
|
@status = @board.status
|
|
60
|
-
@player_w.move(@board)
|
|
61
|
+
@player_w.move(@board)
|
|
62
|
+
check_move(:white)
|
|
61
63
|
show_board if mode[:progress]
|
|
62
64
|
end
|
|
63
65
|
puts @board.to_s if mode[:progress] || mode[:vs_human]
|
|
@@ -73,16 +75,16 @@ module Reversi
|
|
|
73
75
|
|
|
74
76
|
# Checks a move to make sure it is valid.
|
|
75
77
|
def check_move(color)
|
|
76
|
-
blank_diff = @status[:none].size - @board.count_disks(:none)
|
|
78
|
+
blank_diff = @status[:none].size - @board.count_disks(Reversi::Board::DISK[:none])
|
|
77
79
|
|
|
78
80
|
case blank_diff
|
|
79
81
|
when 1
|
|
80
|
-
if (@board.count_disks(color) - @status[color].size) < 2
|
|
82
|
+
if (@board.count_disks(Reversi::Board::DISK[color]) - @status[color].size) < 2
|
|
81
83
|
raise MoveError, "A player must flip at least one or more opponent's disks."
|
|
82
84
|
end
|
|
83
85
|
when 0
|
|
84
|
-
unless (@board.count_disks(:black) - @status[:black].size) == 0 &&
|
|
85
|
-
(@board.count_disks(:white) - @status[:white].size) == 0
|
|
86
|
+
unless (@board.count_disks(Reversi::Board::DISK[:black]) - @status[:black].size) == 0 &&
|
|
87
|
+
(@board.count_disks(Reversi::Board::DISK[:white]) - @status[:white].size) == 0
|
|
86
88
|
raise MoveError, "When a player can't make a valid move, you must not place a new disk."
|
|
87
89
|
end
|
|
88
90
|
else
|
|
@@ -21,7 +21,7 @@ module Reversi::Player
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def move(board)
|
|
24
|
-
moves = next_moves
|
|
24
|
+
moves = next_moves
|
|
25
25
|
return if moves.empty?
|
|
26
26
|
alpha = -N; beta = N
|
|
27
27
|
next_move = moves.map do |move|
|
|
@@ -32,7 +32,7 @@ module Reversi::Player
|
|
|
32
32
|
|
|
33
33
|
def evaluate(move, board, alpha, beta, depth, color)
|
|
34
34
|
put_disk(*move, color)
|
|
35
|
-
moves = next_moves(!color)
|
|
35
|
+
moves = next_moves(!color)
|
|
36
36
|
|
|
37
37
|
if depth == 1
|
|
38
38
|
status[:mine].inject(0){ |sum, xy| sum + @evaluation_value[xy] }
|