reversi 0.2.0 → 1.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/Gemfile +1 -0
- data/Rakefile +4 -0
- data/ext/reversi/extconf.rb +3 -0
- data/ext/reversi/reversi.c +299 -0
- data/ext/reversi/reversi.h +43 -0
- data/lib/reversi/board.rb +12 -95
- data/lib/reversi/player/human.rb +1 -1
- data/lib/reversi/player/negamax_ai.rb +8 -8
- data/lib/reversi/version.rb +1 -1
- data/lib/reversi.rb +1 -0
- data/reversi.gemspec +1 -0
- data/spec/base_player_spec.rb +6 -6
- data/spec/board_spec.rb +2 -9
- data/spec/game_spec.rb +2 -2
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ff1bf1812bca5e760682bda5b45a12ccdba645f
|
|
4
|
+
data.tar.gz: b8d577cc947632a87cc90baf852825f5c65d2a07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06780bd5ce1eaa0581e80f60356de0f0ddf66d41c49e5a48789e84141a2f6d231e55b4f5f415ff737ea9673eef87bf9c449eabec663ac0b5e466025c1dd265a6
|
|
7
|
+
data.tar.gz: e7fa1a96392164b0359ad673334cddc5b9e4a8b19c0b98e44988da387c423e0a1adc18cf43410dc340e107e5dbfa6330aaa9a667a850825e7ea4702474a93844
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#include "reversi.h"
|
|
2
|
+
|
|
3
|
+
void Init_reversi(void) {
|
|
4
|
+
VALUE reversi = rb_define_module("Reversi");
|
|
5
|
+
VALUE reversi_board = rb_define_class_under(reversi, "Board", rb_cObject);
|
|
6
|
+
|
|
7
|
+
rb_define_alloc_func(reversi_board, board_alloc);
|
|
8
|
+
|
|
9
|
+
rb_define_private_method(reversi_board, "board_initialize", board_initialize, 0);
|
|
10
|
+
rb_define_method(reversi_board, "columns", board_columns_getter, 0);
|
|
11
|
+
rb_define_method(reversi_board, "stack", board_stack_getter, 0);
|
|
12
|
+
rb_define_method(reversi_board, "undo!", board_undo, 0);
|
|
13
|
+
|
|
14
|
+
rb_define_private_method(reversi_board, "board_push_stack", board_push_stack, 1);
|
|
15
|
+
rb_define_private_method(reversi_board, "board_status", board_status, 0);
|
|
16
|
+
rb_define_private_method(reversi_board, "board_openness", board_openness, 2);
|
|
17
|
+
rb_define_private_method(reversi_board, "board_at", board_at, 2);
|
|
18
|
+
rb_define_private_method(reversi_board, "board_count_disks", board_count_disks, 1);
|
|
19
|
+
rb_define_private_method(reversi_board, "board_next_moves", board_next_moves, 1);
|
|
20
|
+
rb_define_private_method(reversi_board, "board_put_disk", board_put_disk, 3);
|
|
21
|
+
rb_define_private_method(reversi_board, "board_flip_disks", board_flip_disks, 3);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static VALUE board_initialize(VALUE self) {
|
|
25
|
+
int x, y;
|
|
26
|
+
struct board *ptr;
|
|
27
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
28
|
+
|
|
29
|
+
reset_stack();
|
|
30
|
+
head = NULL;
|
|
31
|
+
|
|
32
|
+
for(x = 0; x < 10; x++) {
|
|
33
|
+
for(y = 0; y < 10; y++) {
|
|
34
|
+
if(x == 0 || x == 9) ptr->columns[x][y] = 2;
|
|
35
|
+
else if(y == 0 || y == 9) ptr->columns[x][y] = 2;
|
|
36
|
+
else ptr->columns[x][y] = 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Qnil;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static VALUE board_columns_getter(VALUE self) {
|
|
43
|
+
VALUE column = rb_ary_new();
|
|
44
|
+
VALUE columns = rb_ary_new();
|
|
45
|
+
int x, y;
|
|
46
|
+
struct board *ptr;
|
|
47
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
48
|
+
|
|
49
|
+
for(x = 0; x < 10; x++) {
|
|
50
|
+
column = rb_ary_new();
|
|
51
|
+
for(y = 0; y < 10; y++) rb_ary_push(column, INT2FIX(ptr->columns[x][y]));
|
|
52
|
+
rb_ary_push(columns, column);
|
|
53
|
+
}
|
|
54
|
+
return columns;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static VALUE board_stack_getter(VALUE self) {
|
|
58
|
+
VALUE stack = rb_ary_new();
|
|
59
|
+
VALUE column = rb_ary_new();
|
|
60
|
+
VALUE columns = rb_ary_new();
|
|
61
|
+
int x, y;
|
|
62
|
+
struct stack *sp;
|
|
63
|
+
|
|
64
|
+
for(sp = head; sp != NULL; sp = sp->next) {
|
|
65
|
+
columns = rb_ary_new();
|
|
66
|
+
for(x = 0; x < 10; x++) {
|
|
67
|
+
column = rb_ary_new();
|
|
68
|
+
for(y = 0; y < 10; y++) rb_ary_push(column, INT2FIX(sp->stack_columns[x][y]));
|
|
69
|
+
rb_ary_push(columns, column);
|
|
70
|
+
}
|
|
71
|
+
rb_ary_push(stack, columns);
|
|
72
|
+
}
|
|
73
|
+
return stack;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static VALUE board_undo(VALUE self) {
|
|
77
|
+
struct board *ptr;
|
|
78
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
79
|
+
pop_stack(ptr->columns);
|
|
80
|
+
return Qnil;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static VALUE board_push_stack(VALUE self, VALUE limit) {
|
|
84
|
+
push_stack(self, FIX2INT(limit));
|
|
85
|
+
return Qnil;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static VALUE board_status(VALUE self) {
|
|
89
|
+
VALUE black = rb_ary_new();
|
|
90
|
+
VALUE white = rb_ary_new();
|
|
91
|
+
VALUE none = rb_ary_new();
|
|
92
|
+
VALUE position = rb_ary_new();
|
|
93
|
+
VALUE status = rb_ary_new();
|
|
94
|
+
int x, y;
|
|
95
|
+
struct board *ptr;
|
|
96
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
97
|
+
|
|
98
|
+
for(x = 0; x < 10; x++) {
|
|
99
|
+
for(y = 0; y < 10; y++) {
|
|
100
|
+
switch (ptr->columns[x][y]) {
|
|
101
|
+
case -1:
|
|
102
|
+
rb_ary_push(position, INT2FIX(x));
|
|
103
|
+
rb_ary_push(position, INT2FIX(y));
|
|
104
|
+
rb_ary_push(black, position);
|
|
105
|
+
position = rb_ary_new(); break;
|
|
106
|
+
case 1:
|
|
107
|
+
rb_ary_push(position, INT2FIX(x));
|
|
108
|
+
rb_ary_push(position, INT2FIX(y));
|
|
109
|
+
rb_ary_push(white, position);
|
|
110
|
+
position = rb_ary_new(); break;
|
|
111
|
+
case 0:
|
|
112
|
+
rb_ary_push(position, INT2FIX(x));
|
|
113
|
+
rb_ary_push(position, INT2FIX(y));
|
|
114
|
+
rb_ary_push(none, position);
|
|
115
|
+
position = rb_ary_new(); break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
rb_ary_push(status, black);
|
|
121
|
+
rb_ary_push(status, white);
|
|
122
|
+
rb_ary_push(status, none);
|
|
123
|
+
return status;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static VALUE board_openness(VALUE self, VALUE x, VALUE y) {
|
|
127
|
+
int dx, dy, sum = 0;
|
|
128
|
+
struct board *ptr;
|
|
129
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
130
|
+
|
|
131
|
+
for(dx = -1; dx < 2; dx++){
|
|
132
|
+
for(dy = -1; dy < 2; dy++){
|
|
133
|
+
if (dx == 0 && dy == 0) continue;
|
|
134
|
+
if (ptr->columns[FIX2INT(x) + dx][FIX2INT(y) + dy] == 0) sum += 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return INT2FIX(sum);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static VALUE board_at(VALUE self, VALUE x, VALUE y) {
|
|
141
|
+
struct board *ptr;
|
|
142
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
143
|
+
return INT2FIX(ptr->columns[FIX2INT(x)][FIX2INT(y)]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static VALUE board_count_disks(VALUE self, VALUE color) {
|
|
147
|
+
int x, y, count = 0;
|
|
148
|
+
struct board *ptr;
|
|
149
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
150
|
+
|
|
151
|
+
for(x = 1; x < 9; x++){
|
|
152
|
+
for(y = 1; y < 9; y++){
|
|
153
|
+
if (ptr->columns[x][y] == FIX2INT(color)) count += 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return INT2FIX(count);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static VALUE board_next_moves(VALUE self, VALUE color) {
|
|
160
|
+
int x, y;
|
|
161
|
+
VALUE move = rb_ary_new();
|
|
162
|
+
VALUE moves = rb_ary_new();
|
|
163
|
+
|
|
164
|
+
for(x = 1; x < 9; x++){
|
|
165
|
+
for(y = 1; y < 9; y++){
|
|
166
|
+
if (can_put(self, x, y, FIX2INT(color)) == 1){
|
|
167
|
+
move = rb_ary_new();
|
|
168
|
+
rb_ary_push(move, INT2FIX(x));
|
|
169
|
+
rb_ary_push(move, INT2FIX(y));
|
|
170
|
+
rb_ary_push(moves, move);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return moves;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static VALUE board_put_disk(VALUE self, VALUE x, VALUE y, VALUE color) {
|
|
178
|
+
struct board *ptr;
|
|
179
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
180
|
+
|
|
181
|
+
ptr->columns[FIX2INT(x)][FIX2INT(y)] = FIX2INT(color);
|
|
182
|
+
return Qnil;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
static VALUE board_flip_disks(VALUE self, VALUE x, VALUE y, VALUE color) {
|
|
186
|
+
int dx, dy;
|
|
187
|
+
struct board *ptr;
|
|
188
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
189
|
+
x = FIX2INT(x);
|
|
190
|
+
y = FIX2INT(y);
|
|
191
|
+
color = FIX2INT(color);
|
|
192
|
+
|
|
193
|
+
for(dx = -1; dx < 2; dx++){
|
|
194
|
+
for(dy = -1; dy < 2; dy++){
|
|
195
|
+
if (dx == 0 && dy == 0) continue;
|
|
196
|
+
if (ptr->columns[x + dx][y + dy] == (signed)(-color) &&
|
|
197
|
+
can_flip(self, x, y, dx, dy, color) == 1)
|
|
198
|
+
flip_disk(self, x, y, dx, dy, color);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return Qnil;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
void flip_disk(VALUE self, int x, int y, int dx, int dy, int color){
|
|
205
|
+
struct board *ptr;
|
|
206
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
207
|
+
|
|
208
|
+
x += dx; y += dy;
|
|
209
|
+
if (ptr->columns[x][y] == 2 ||
|
|
210
|
+
ptr->columns[x][y] == 0 ||
|
|
211
|
+
ptr->columns[x][y] == color) return;
|
|
212
|
+
ptr->columns[x][y] = color;
|
|
213
|
+
flip_disk(self, x, y, dx, dy, color);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
int can_put(VALUE self, int x, int y, int color){
|
|
218
|
+
int dx, dy;
|
|
219
|
+
struct board *ptr;
|
|
220
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
221
|
+
|
|
222
|
+
if (ptr->columns[x][y] != 0) return 0;
|
|
223
|
+
|
|
224
|
+
for(dx = -1; dx < 2; dx++){
|
|
225
|
+
for(dy = -1; dy < 2; dy++){
|
|
226
|
+
if (dx == 0 && dy == 0) continue;
|
|
227
|
+
if (ptr->columns[x + dx][y + dy] == -color){
|
|
228
|
+
if (can_flip(self, x, y, dx, dy, color) == 1) return 1;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
int can_flip(VALUE self, int x, int y, int dx, int dy, int color){
|
|
236
|
+
struct board *ptr;
|
|
237
|
+
Data_Get_Struct(self, struct board, ptr);
|
|
238
|
+
|
|
239
|
+
while(1){
|
|
240
|
+
x += dx; y += dy;
|
|
241
|
+
if (ptr->columns[x][y] == color) return 1;
|
|
242
|
+
if (ptr->columns[x][y] == 2 || ptr->columns[x][y] == 0) break;
|
|
243
|
+
}
|
|
244
|
+
return 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
void push_stack(VALUE self, int limit){
|
|
248
|
+
int size = 0;
|
|
249
|
+
struct board *bp;
|
|
250
|
+
struct stack *sp;
|
|
251
|
+
Data_Get_Struct(self, struct board, bp);
|
|
252
|
+
|
|
253
|
+
for(sp = head; sp != NULL; sp = sp->next);
|
|
254
|
+
sp = (struct stack *)malloc(sizeof(struct stack));
|
|
255
|
+
|
|
256
|
+
memcpy(sp->stack_columns, bp->columns, sizeof(bp->columns));
|
|
257
|
+
sp->next = head;
|
|
258
|
+
head = sp;
|
|
259
|
+
|
|
260
|
+
size = stack_size();
|
|
261
|
+
if (size > limit) delete_old();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
void pop_stack(int ary[10][10]){
|
|
266
|
+
struct stack *p;
|
|
267
|
+
memcpy(ary, head->stack_columns, sizeof(head->stack_columns));
|
|
268
|
+
p = head;
|
|
269
|
+
head = p->next;
|
|
270
|
+
free(p);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
int stack_size(void){
|
|
275
|
+
int count = 0;
|
|
276
|
+
struct stack *p;
|
|
277
|
+
for(p = head; p != NULL; p = p->next) count++;
|
|
278
|
+
return count;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
void delete_old(void){
|
|
282
|
+
struct stack *p;
|
|
283
|
+
for(p = head; p->next->next != NULL; p = p->next);
|
|
284
|
+
p->next = NULL;
|
|
285
|
+
free(p->next);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
void reset_stack(void){
|
|
290
|
+
struct stack *p = head;
|
|
291
|
+
struct stack *tmp;
|
|
292
|
+
|
|
293
|
+
while(p != NULL){
|
|
294
|
+
tmp = p->next;
|
|
295
|
+
free(p);
|
|
296
|
+
p = tmp;
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <stdlib.h>
|
|
3
|
+
#include <memory.h>
|
|
4
|
+
|
|
5
|
+
void Init_board(void);
|
|
6
|
+
static VALUE board_alloc(VALUE class);
|
|
7
|
+
static VALUE board_initialize(VALUE self);
|
|
8
|
+
static VALUE board_columns_getter(VALUE self);
|
|
9
|
+
static VALUE board_stack_getter(VALUE self);
|
|
10
|
+
static VALUE board_push_stack(VALUE self, VALUE limit);
|
|
11
|
+
static VALUE board_undo(VALUE self);
|
|
12
|
+
static VALUE board_status(VALUE self);
|
|
13
|
+
static VALUE board_openness(VALUE self, VALUE x, VALUE y);
|
|
14
|
+
static VALUE board_at(VALUE self, VALUE x, VALUE y);
|
|
15
|
+
static VALUE board_count_disks(VALUE self, VALUE color);
|
|
16
|
+
static VALUE board_next_moves(VALUE self, VALUE color);
|
|
17
|
+
static VALUE board_put_disk(VALUE self, VALUE x, VALUE y, VALUE color);
|
|
18
|
+
static VALUE board_flip_disks(VALUE self, VALUE x, VALUE y, VALUE color);
|
|
19
|
+
|
|
20
|
+
void flip_disk(VALUE self, int x, int y, int dx, int dy, int color);
|
|
21
|
+
int can_put(VALUE self, int x, int y, int color);
|
|
22
|
+
int can_flip(VALUE self, int x, int y, int dx, int dy, int color);
|
|
23
|
+
|
|
24
|
+
void push_stack(VALUE self, int limit);
|
|
25
|
+
void pop_stack(int ary[10][10]);
|
|
26
|
+
int stack_size(void);
|
|
27
|
+
void delete_old(void);
|
|
28
|
+
void reset_stack(void);
|
|
29
|
+
|
|
30
|
+
struct stack{
|
|
31
|
+
int stack_columns[10][10];
|
|
32
|
+
struct stack *next;
|
|
33
|
+
};
|
|
34
|
+
struct stack *head = NULL;
|
|
35
|
+
|
|
36
|
+
struct board {
|
|
37
|
+
int columns[10][10];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
static VALUE board_alloc(VALUE class) {
|
|
41
|
+
struct board *ptr = ALLOC(struct board);
|
|
42
|
+
return Data_Wrap_Struct(class, 0, -1, ptr);
|
|
43
|
+
}
|
data/lib/reversi/board.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module Reversi
|
|
2
2
|
class Board
|
|
3
|
-
attr_reader :options
|
|
4
|
-
|
|
5
|
-
COORDINATES = (:a..:h).map{ |x| (1..8).map{ |y| [x, y] } }.flatten(1).freeze
|
|
3
|
+
attr_reader :options
|
|
6
4
|
|
|
7
5
|
DISK = {
|
|
8
6
|
:none => 0,
|
|
@@ -30,21 +28,15 @@ module Reversi
|
|
|
30
28
|
# @return [Reversi::Board]
|
|
31
29
|
def initialize(options = {})
|
|
32
30
|
@options = options
|
|
33
|
-
@stack = []
|
|
34
31
|
[:disk_color_b, :disk_color_w].each do |color|
|
|
35
32
|
if @options[color].is_a?(Symbol) || @options[color].is_a?(String)
|
|
36
33
|
@options[color] = DISK_COLOR[@options[color].to_sym].to_i
|
|
37
34
|
end
|
|
38
35
|
end
|
|
39
|
-
|
|
36
|
+
board_initialize
|
|
40
37
|
@options[:initial_position].each do |color, positions|
|
|
41
38
|
positions.each{ |position| put_disk(*position, color) }
|
|
42
39
|
end
|
|
43
|
-
@columns.each do |col|
|
|
44
|
-
col[0] = 2; col[-1] = 2
|
|
45
|
-
end.tap do |cols|
|
|
46
|
-
cols[0].fill(2); cols[-1].fill(2)
|
|
47
|
-
end
|
|
48
40
|
end
|
|
49
41
|
|
|
50
42
|
# Returns a string of the game board in human-readable form.
|
|
@@ -53,7 +45,7 @@ module Reversi
|
|
|
53
45
|
def to_s
|
|
54
46
|
" #{[*'a'..'h'].join(" ")}\n" <<
|
|
55
47
|
" #{"+---"*8}+\n" <<
|
|
56
|
-
|
|
48
|
+
columns[1][1..-2].zip(*columns[2..8].map{ |col| col[1..-2] })
|
|
57
49
|
.map{ |row| row.map do |e|
|
|
58
50
|
case e
|
|
59
51
|
when 0 then " "
|
|
@@ -70,26 +62,15 @@ module Reversi
|
|
|
70
62
|
# Pushes an array of the game board onto a stack.
|
|
71
63
|
# The stack size limit is 3(default).
|
|
72
64
|
def push_stack
|
|
73
|
-
|
|
74
|
-
@stack.push(Marshal.load(Marshal.dump(@columns)))
|
|
75
|
-
@stack.shift if @stack.size > @options[:stack_limit]
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Pops an array of the game board off of the stack,
|
|
79
|
-
# and that is stored in the instance variable.(`@columns`)
|
|
80
|
-
#
|
|
81
|
-
# @param num [Integer] Be popped from the stack by the supplied number of times.
|
|
82
|
-
def undo!(num = 1)
|
|
83
|
-
num.times{ @columns = @stack.pop }
|
|
65
|
+
board_push_stack(@options[:stack_limit])
|
|
84
66
|
end
|
|
85
67
|
|
|
86
68
|
# Returns a hash containing the coordinates of each color.
|
|
87
69
|
#
|
|
88
70
|
# @return [Hash{Symbol => Array<Symbol, Integer>}]
|
|
89
71
|
def status
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
end.flatten(1)]
|
|
72
|
+
ary = board_status
|
|
73
|
+
{:black => ary[0], :white => ary[1], :none => ary[2]}
|
|
93
74
|
end
|
|
94
75
|
|
|
95
76
|
# Returns the openness of the coordinates.
|
|
@@ -99,9 +80,7 @@ module Reversi
|
|
|
99
80
|
# @return [Integer] the openness
|
|
100
81
|
def openness(x, y)
|
|
101
82
|
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
102
|
-
(
|
|
103
|
-
sum + (@columns[x + dx][y + dy] == 0 ? 1 : 0)
|
|
104
|
-
end
|
|
83
|
+
board_openness(x, y)
|
|
105
84
|
end
|
|
106
85
|
|
|
107
86
|
# Returns the color of supplied coordinates.
|
|
@@ -111,7 +90,7 @@ module Reversi
|
|
|
111
90
|
# @return [Symbol] the color or `:none`
|
|
112
91
|
def at(x, y)
|
|
113
92
|
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
114
|
-
DISK.key(
|
|
93
|
+
DISK.key(board_at(x, y))
|
|
115
94
|
end
|
|
116
95
|
|
|
117
96
|
# Counts the number of the supplied color's disks.
|
|
@@ -119,9 +98,7 @@ module Reversi
|
|
|
119
98
|
# @param color [Symbol]
|
|
120
99
|
# @return [Integer] the sum of the counted disks
|
|
121
100
|
def count_disks(color)
|
|
122
|
-
|
|
123
|
-
sum + (e == DISK[color] ? 1 : 0)
|
|
124
|
-
end
|
|
101
|
+
board_count_disks(DISK[color])
|
|
125
102
|
end
|
|
126
103
|
|
|
127
104
|
# Returns an array of the next moves.
|
|
@@ -129,10 +106,7 @@ module Reversi
|
|
|
129
106
|
# @param color [Symbol]
|
|
130
107
|
# @return [Array<Array<Symbol, Integer>>]
|
|
131
108
|
def next_moves(color)
|
|
132
|
-
|
|
133
|
-
.flatten.each_with_index.inject([]) do |list, (_, i)|
|
|
134
|
-
list << (puttable?(*COORDINATES[i], color) ? COORDINATES[i] : nil)
|
|
135
|
-
end.compact
|
|
109
|
+
board_next_moves(DISK[color])
|
|
136
110
|
end
|
|
137
111
|
|
|
138
112
|
# Places a supplied color's disk on specified position.
|
|
@@ -142,7 +116,7 @@ module Reversi
|
|
|
142
116
|
# @param color [Symbol]
|
|
143
117
|
def put_disk(x, y, color)
|
|
144
118
|
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
145
|
-
|
|
119
|
+
board_put_disk(x, y, DISK[color])
|
|
146
120
|
end
|
|
147
121
|
|
|
148
122
|
# Flips the opponent's disks between a new disk and another disk of my color.
|
|
@@ -153,64 +127,7 @@ module Reversi
|
|
|
153
127
|
# @param color [Symbol]
|
|
154
128
|
def flip_disks(x, y, color)
|
|
155
129
|
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
156
|
-
|
|
157
|
-
next if dx == 0 && dy == 0
|
|
158
|
-
if @columns[x + dx][y + dy] == DISK[color]*(-1)
|
|
159
|
-
flip_disk(x, y, dx, dy, color) if flippable?(x, y, dx, dy, color)
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
private
|
|
165
|
-
|
|
166
|
-
# Flips the opponent's disks on one of these straight lines
|
|
167
|
-
# between a new disk and another disk of my color.
|
|
168
|
-
#
|
|
169
|
-
# @param x [Symbol, Integer] the column number
|
|
170
|
-
# @param y [Integer] the row number
|
|
171
|
-
# @param dx [Integer] a horizontal difference
|
|
172
|
-
# @param dy [Integer] a verticaldistance
|
|
173
|
-
# @param color [Symbol]
|
|
174
|
-
def flip_disk(x, y, dx, dy, color)
|
|
175
|
-
return if [DISK[:wall], DISK[:none], DISK[color]].include?(@columns[x+dx][y+dy])
|
|
176
|
-
@columns[x+dx][y+dy] = DISK[color]
|
|
177
|
-
flip_disk(x+dx, y+dy, dx, dy, color)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Whether or not a player can place a new disk on specified position.
|
|
181
|
-
# Returns true if the move is valid.
|
|
182
|
-
#
|
|
183
|
-
# @param x [Symbol, Integer] the column number
|
|
184
|
-
# @param y [Integer] the row number
|
|
185
|
-
# @param color [Symbol]
|
|
186
|
-
# @return [Boolean]
|
|
187
|
-
def puttable?(x, y, color)
|
|
188
|
-
x = [*:a..:h].index(x) + 1 if x.is_a? Symbol
|
|
189
|
-
return false if @columns[x][y] != 0
|
|
190
|
-
[-1,0,1].product([-1,0,1]).each do |dx, dy|
|
|
191
|
-
next if dx == 0 && dy == 0
|
|
192
|
-
if @columns[x + dx][y + dy] == DISK[color]*(-1)
|
|
193
|
-
return true if flippable?(x, y, dx, dy, color)
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
false
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Whether or not a player can flip the opponent's disks.
|
|
200
|
-
#
|
|
201
|
-
# @param x [Symbol, Integer] the column number
|
|
202
|
-
# @param y [Integer] the row number
|
|
203
|
-
# @param dx [Integer] a horizontal difference
|
|
204
|
-
# @param dy [Integer] a verticaldistance
|
|
205
|
-
# @param color [Symbol]
|
|
206
|
-
# @return [Boolean]
|
|
207
|
-
def flippable?(x, y, dx, dy, color)
|
|
208
|
-
loop do
|
|
209
|
-
x += dx; y += dy
|
|
210
|
-
return true if @columns[x][y] == DISK[color]
|
|
211
|
-
break if [DISK[:wall] ,DISK[:none]].include?(@columns[x][y])
|
|
212
|
-
end
|
|
213
|
-
false
|
|
130
|
+
board_flip_disks(x, y, DISK[color])
|
|
214
131
|
end
|
|
215
132
|
end
|
|
216
133
|
end
|
data/lib/reversi/player/human.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Reversi::Player
|
|
|
18
18
|
@input_move = gets.chomp.split("")
|
|
19
19
|
exit if [['q'], ['e','x','i','t']].include? @input_move
|
|
20
20
|
redo if check_size == :redo
|
|
21
|
-
@input_move[0] = @input_move[0].to_sym
|
|
21
|
+
@input_move[0] = [*:a..:h].index(@input_move[0].to_sym) + 1
|
|
22
22
|
@input_move[1] = @input_move[1].to_i
|
|
23
23
|
printf "\e[#{1}A"; STDOUT.flush
|
|
24
24
|
print "#{" "*9}"
|
|
@@ -2,14 +2,14 @@ module Reversi::Player
|
|
|
2
2
|
class NegamaxAI < BasePlayer
|
|
3
3
|
|
|
4
4
|
POINT = {
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
5
|
+
[1, 1] => 100, [2, 1] => -10, [3, 1] => 0, [4, 1] => -1, [5, 1] => -1, [6, 1] => 0, [7, 1] => -10, [8, 1] => 100,
|
|
6
|
+
[1, 2] => -10, [2, 2] => -30, [3, 2] => -5, [4, 2] => -5, [5, 2] => -5, [6, 2] => -5, [7, 2] => -30, [8, 2] => -10,
|
|
7
|
+
[1, 3] => 0, [2, 3] => -5, [3, 3] => 0, [4, 3] => -1, [5, 3] => -1, [6, 3] => 0, [7, 3] => -5, [8, 3] => 0,
|
|
8
|
+
[1, 4] => -1, [2, 4] => -5, [3, 4] => -1, [4, 4] => -1, [5, 4] => -1, [6, 4] => -1, [7, 4] => -5, [8, 4] => -1,
|
|
9
|
+
[1, 5] => -1, [2, 5] => -5, [3, 5] => -1, [4, 5] => -1, [5, 5] => -1, [6, 5] => -1, [7, 5] => -5, [8, 5] => -1,
|
|
10
|
+
[1, 6] => 0, [2, 6] => -5, [3, 6] => 0, [4, 6] => -1, [5, 6] => -1, [6, 6] => 0, [7, 6] => -5, [8, 6] => 0,
|
|
11
|
+
[1, 7] => -10, [2, 7] => -30, [3, 7] => -5, [4, 7] => -5, [5, 7] => -5, [6, 7] => -5, [7, 7] => -30, [8, 7] => -10,
|
|
12
|
+
[1, 8] => 100, [2, 8] => -10, [3, 8] => 0, [4, 8] => -1, [5, 8] => -1, [6, 8] => 0, [7, 8] => -12, [8, 8] => 100
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
15
|
def move(board)
|
data/lib/reversi/version.rb
CHANGED
data/lib/reversi.rb
CHANGED
data/reversi.gemspec
CHANGED
|
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
|
+
spec.extensions = ["ext/reversi/extconf.rb"]
|
|
20
21
|
|
|
21
22
|
spec.add_development_dependency "bundler", "~> 1.7"
|
|
22
23
|
|
data/spec/base_player_spec.rb
CHANGED
|
@@ -22,7 +22,7 @@ describe Reversi::Player::BasePlayer do
|
|
|
22
22
|
describe "#next_moves" do
|
|
23
23
|
context "when the first argument is omitted" do
|
|
24
24
|
it do
|
|
25
|
-
ans = [[
|
|
25
|
+
ans = [[3, 4], [4, 3], [5, 6], [6, 5]]
|
|
26
26
|
expect(player.next_moves.map{ |move| move[:move] }).to eq ans
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -30,13 +30,13 @@ describe Reversi::Player::BasePlayer do
|
|
|
30
30
|
player.put_disk(:d, 3)
|
|
31
31
|
player.put_disk(:e, 3, false)
|
|
32
32
|
expect(player.next_moves[1][:openness]).to eq 8
|
|
33
|
-
expect(player.next_moves[1][:result]).to eq [[
|
|
33
|
+
expect(player.next_moves[1][:result]).to eq [[5, 3], [5, 4]]
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
context "when the first argument is `false`" do
|
|
38
38
|
it do
|
|
39
|
-
ans = [[
|
|
39
|
+
ans = [[3, 5], [4, 6], [5, 3], [6, 4]]
|
|
40
40
|
expect(player.next_moves(false).map{ |move| move[:move] }).to eq ans
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -44,7 +44,7 @@ describe Reversi::Player::BasePlayer do
|
|
|
44
44
|
player.put_disk(:d, 3)
|
|
45
45
|
player.put_disk(:e, 3, false)
|
|
46
46
|
expect(player.next_moves(false)[0][:openness]).to eq 5
|
|
47
|
-
expect(player.next_moves(false)[0][:result]).to eq [[
|
|
47
|
+
expect(player.next_moves(false)[0][:result]).to eq [[4, 3]]
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -66,7 +66,7 @@ describe Reversi::Player::BasePlayer do
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
describe "#status" do
|
|
69
|
-
it { expect(player.status[:mine]).to eq [[
|
|
70
|
-
it { expect(player.status[:opponent]).to eq [[
|
|
69
|
+
it { expect(player.status[:mine]).to eq [[4, 5], [5, 4]] }
|
|
70
|
+
it { expect(player.status[:opponent]).to eq [[4, 4], [5, 5]] }
|
|
71
71
|
end
|
|
72
72
|
end
|
data/spec/board_spec.rb
CHANGED
|
@@ -91,13 +91,6 @@ describe Reversi::Board do
|
|
|
91
91
|
expect{ board.undo! }.to change{ board.stack.size }.by(-1)
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
|
-
|
|
95
|
-
context "when the first argument is supplied" do
|
|
96
|
-
it "the number is used" do
|
|
97
|
-
3.times { board.push_stack }
|
|
98
|
-
expect{ board.undo! 2 }.to change{ board.stack.size }.by(-2)
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
94
|
end
|
|
102
95
|
|
|
103
96
|
describe "#status" do
|
|
@@ -149,8 +142,8 @@ describe Reversi::Board do
|
|
|
149
142
|
describe "#next_moves" do
|
|
150
143
|
let(:disk_color_b) { 0 }
|
|
151
144
|
let(:disk_color_w) { 0 }
|
|
152
|
-
it { expect(board.next_moves(:black)).to eq [[
|
|
153
|
-
it { expect(board.next_moves(:white)).to eq [[
|
|
145
|
+
it { expect(board.next_moves(:black)).to eq [[3, 4], [4, 3], [5, 6], [6, 5]] }
|
|
146
|
+
it { expect(board.next_moves(:white)).to eq [[3, 5], [4, 6], [5, 3], [6, 4]] }
|
|
154
147
|
end
|
|
155
148
|
|
|
156
149
|
describe "#put_disk, #flip_disks" do
|
data/spec/game_spec.rb
CHANGED
|
@@ -14,7 +14,7 @@ describe Reversi::Game do
|
|
|
14
14
|
|
|
15
15
|
context "before `player_w` places a piece on position [f4]" do
|
|
16
16
|
it do
|
|
17
|
-
ans = [[
|
|
17
|
+
ans = [[3, 5], [4, 6], [5, 3], [6, 4], [7, 5], [7, 7]]
|
|
18
18
|
expect(@game.player_w.next_moves.map{ |move| move[:move] }).to eq ans
|
|
19
19
|
end
|
|
20
20
|
it { expect(@game.player_w.count_disks).to eq 3 }
|
|
@@ -26,7 +26,7 @@ describe Reversi::Game do
|
|
|
26
26
|
@game.player_w.put_disk(:f, 4)
|
|
27
27
|
end
|
|
28
28
|
it do
|
|
29
|
-
ans = [[
|
|
29
|
+
ans = [[3, 5], [3, 6], [4, 6], [6, 2], [7, 2], [8, 3]]
|
|
30
30
|
expect(@game.player_w.next_moves.map{ |move| move[:move] }).to eq ans
|
|
31
31
|
end
|
|
32
32
|
it { expect(@game.player_w.count_disks).to eq 7 }
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reversi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- seinosuke
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-03-
|
|
11
|
+
date: 2015-03-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -29,7 +29,8 @@ description: |
|
|
|
29
29
|
or easily make your original reversi game programs.
|
|
30
30
|
email:
|
|
31
31
|
executables: []
|
|
32
|
-
extensions:
|
|
32
|
+
extensions:
|
|
33
|
+
- ext/reversi/extconf.rb
|
|
33
34
|
extra_rdoc_files: []
|
|
34
35
|
files:
|
|
35
36
|
- ".gitignore"
|
|
@@ -39,6 +40,9 @@ files:
|
|
|
39
40
|
- LICENSE.txt
|
|
40
41
|
- README.md
|
|
41
42
|
- Rakefile
|
|
43
|
+
- ext/reversi/extconf.rb
|
|
44
|
+
- ext/reversi/reversi.c
|
|
45
|
+
- ext/reversi/reversi.h
|
|
42
46
|
- images/reversi.gif
|
|
43
47
|
- lib/reversi.rb
|
|
44
48
|
- lib/reversi/board.rb
|