Class: Reversi::Board

Inherits:
Object
  • Object
show all
Defined in:
lib/reversi/board.rb,
ext/reversi/reversi.c

Constant Summary collapse

DISK =
{
  :none  =>  0,
  :black => -1,
  :white =>  1
}.freeze
DISK_COLOR =
{
  :black   => 30,
  :red     => 31,
  :green   => 32,
  :yellow  => 33,
  :blue    => 34,
  :magenda => 35,
  :cyan    => 36,
  :white   => 37,
  :gray    => 90
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Reversi::Board

Initializes a new Board object.

Raises:

  • (OptionError)

    Error is raised when the supplied option is invalid.

See Also:



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/reversi/board.rb', line 29

def initialize(options = {})
  @options = options
  @stack = []
  board_initialize

  [:disk_color_b, :disk_color_w].each do |color|
    if @options[color].is_a?(Symbol) || @options[color].is_a?(String)
      @options[color] = DISK_COLOR[@options[color].to_sym].to_i
    end
  end

  @options[:initial_position].each do |color, positions|
    positions.each{ |position| put_disk(*position, DISK[color]) }
  end

  if @options[:disk_b].size != 1 || @options[:disk_w].size != 1
    raise OptionError, "The length of the disk string must be one."
  end
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



3
4
5
# File 'lib/reversi/board.rb', line 3

def options
  @options
end

#stackObject (readonly)

Returns the value of attribute stack.



3
4
5
# File 'lib/reversi/board.rb', line 3

def stack
  @stack
end

Instance Method Details

#at(rb_x, rb_y) ⇒ Symbol

Returns the color of supplied coordinates.

Parameters:

  • rb_x (Integer)

    the column number

  • rb_y (Integer)

    the row number

Returns:

  • (Symbol)

    the color or ‘:none`



143
144
145
146
147
148
149
150
151
# File 'ext/reversi/board.c', line 143

VALUE at(VALUE self, VALUE rb_x, VALUE rb_y) {
  uint64_t p = XY2BB(FIX2INT(rb_x), FIX2INT(rb_y));
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  if      ((p & ptr->black) != 0) { return ID2SYM(rb_intern("black")); }
  else if ((p & ptr->white) != 0) { return ID2SYM(rb_intern("white")); }
  else { return ID2SYM(rb_intern("none")); }
}

#count_disks(color) ⇒ Integer

Counts the number of the supplied color’s disks.

Parameters:

  • color (Integer)

Returns:

  • (Integer)

    the sum of the counted disks



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'ext/reversi/board.c', line 159

VALUE count_disks(VALUE self, VALUE color) {
  uint64_t bb = 0;
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  switch (FIX2INT(color)) {
    case -1: bb = ptr->black; break;
    case  1: bb = ptr->white; break;
    default: bb = ~(ptr->black | ptr->white); break;
  }
  bb = (bb & 0x5555555555555555) + (bb >> 1  & 0x5555555555555555);
  bb = (bb & 0x3333333333333333) + (bb >> 2  & 0x3333333333333333);
  bb = (bb & 0x0F0F0F0F0F0F0F0F) + (bb >> 4  & 0x0F0F0F0F0F0F0F0F);
  bb = (bb & 0x00FF00FF00FF00FF) + (bb >> 8  & 0x00FF00FF00FF00FF);
  bb = (bb & 0x0000FFFF0000FFFF) + (bb >> 16 & 0x0000FFFF0000FFFF);
  return INT2FIX((int)((bb & 0x00000000FFFFFFFF) + (bb >> 32 & 0x00000000FFFFFFFF)));
}

#flip_disks(rb_x, rb_y, color) ⇒ Object

Flips the opponent’s disks between a new disk and another disk of my color. This method is used in Reversi::Player::BasePlayer class. When the invalid move is supplied, a disk is put only the position and Reversi::MoveError is raised at Reversi::Game.check_move method.

Parameters:

  • rb_x (Integer)

    the column number

  • rb_y (Integer)

    the row number

  • color (Integer)


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'ext/reversi/board.c', line 236

VALUE flip_disks(VALUE self, VALUE rb_x, VALUE rb_y, VALUE color) {
  uint64_t p = XY2BB(FIX2INT(rb_x), FIX2INT(rb_y));
  uint64_t my = 0, op = 0, rev = 0;
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  switch(FIX2INT(color)) {
    case -1:
      my = ptr->black; op = ptr->white;
      if (((ptr->black | ptr->white) & p) != 0) { rev = 0; }
      else { rev = horizontal_pat(my, op, p) | vertical_pat(my, op, p) | diagonal_pat(my, op, p); }
      ptr->black = (ptr->black ^ (p | rev));
      ptr->white = (ptr->white ^ rev);
      break;
    case  1:
      my = ptr->white; op = ptr->black;
      if (((ptr->black | ptr->white) & p) != 0) { rev = 0; }
      else { rev = horizontal_pat(my, op, p) | vertical_pat(my, op, p) | diagonal_pat(my, op, p); }
      ptr->white = (ptr->white ^ (p | rev));
      ptr->black = (ptr->black ^ rev);
      break;
  }
  return Qnil;
}

#next_moves(color) ⇒ Array<Array<Integer, Integer>>

Returns an array of the next moves. This method is used in Reversi::Player::BasePlayer class.

Parameters:

  • color (Integer)

Returns:

  • (Array<Array<Integer, Integer>>)


184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'ext/reversi/board.c', line 184

VALUE next_moves(VALUE self, VALUE color) {
  uint64_t my = 0, op = 0, blank = 0, p = 0, pos = 0;
  VALUE moves = rb_ary_new();
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  switch (FIX2INT(color)) {
    case -1: my = ptr->black; op = ptr->white; break;
    case  1: my = ptr->white; op = ptr->black; break;
  }
  blank = ~(my | op);
  pos = horizontal_pos(my, op, blank) | vertical_pos(my, op, blank) | diagonal_pos(my, op, blank);
  while (pos != 0) {
    p = pos & (~pos + 1);
    rb_ary_push(moves, BB2XY(p));
    pos ^= p;
  }
  return moves;
}

#openness(rb_x, rb_y) ⇒ Integer

Returns the openness of the coordinates.

Parameters:

  • rb_x (Integer)

    the column number

  • rb_y (Integer)

    the row number

Returns:

  • (Integer)

    the openness



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'ext/reversi/board.c', line 113

VALUE openness(VALUE self, VALUE rb_x, VALUE rb_y) {
  uint64_t p = XY2BB(FIX2INT(rb_x), FIX2INT(rb_y));
  uint64_t blank = 0, bb = 0;
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  blank = ~(ptr->black | ptr->white);
  bb = ((p << 1) & (blank & 0xFEFEFEFEFEFEFEFE)) |
       ((p >> 1) & (blank & 0x7F7F7F7F7F7F7F7F)) |
       ((p << 8) & (blank & 0xFFFFFFFFFFFFFFFF)) |
       ((p >> 8) & (blank & 0xFFFFFFFFFFFFFFFF)) |
       ((p << 7) & (blank & 0x7F7F7F7F7F7F7F7F)) |
       ((p >> 7) & (blank & 0xFEFEFEFEFEFEFEFE)) |
       ((p << 9) & (blank & 0xFEFEFEFEFEFEFEFE)) |
       ((p >> 9) & (blank & 0x7F7F7F7F7F7F7F7F));
  bb = (bb & 0x5555555555555555) + (bb >> 1  & 0x5555555555555555);
  bb = (bb & 0x3333333333333333) + (bb >> 2  & 0x3333333333333333);
  bb = (bb & 0x0F0F0F0F0F0F0F0F) + (bb >> 4  & 0x0F0F0F0F0F0F0F0F);
  bb = (bb & 0x00FF00FF00FF00FF) + (bb >> 8  & 0x00FF00FF00FF00FF);
  bb = (bb & 0x0000FFFF0000FFFF) + (bb >> 16 & 0x0000FFFF0000FFFF);
  return INT2FIX((int)((bb & 0x00000000FFFFFFFF) + (bb >> 32 & 0x00000000FFFFFFFF)));
}

#push_stackObject

Pushes an array of the game board onto a stack. The stack size limit is 3(default).



71
72
73
74
75
# File 'lib/reversi/board.rb', line 71

def push_stack
  bb = {:black => get_black,:white => get_white}
  @stack.push(Marshal.load(Marshal.dump(bb)))
  @stack.shift if @stack.size > @options[:stack_limit]
end

#put_disk(rb_x, rb_y, color) ⇒ Object

Places a supplied color’s disk on specified position. This method is used in Reversi::Board.initialize method for putting the disks at the initial position, but not used in Reversi::Player::BasePlayer class.

Parameters:

  • rb_x (Integer)

    the column number

  • rb_y (Integer)

    the row number

  • color (Integer)


214
215
216
217
218
219
220
221
222
223
224
# File 'ext/reversi/board.c', line 214

VALUE put_disk(VALUE self, VALUE rb_x, VALUE rb_y, VALUE color) {
  uint64_t p = XY2BB(FIX2INT(rb_x), FIX2INT(rb_y));
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  switch (FIX2INT(color)) {
    case -1: ptr->black ^= p; break;
    case  1: ptr->white ^= p; break;
  }
  return Qnil;
}

#statusHash{Symbol => Array<Array<Integer, Integer>>}

Returns a hash containing the coordinates of each color.

Returns:

  • (Hash{Symbol => Array<Array<Integer, Integer>>})


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
# File 'ext/reversi/board.c', line 70

VALUE status(VALUE self) {
  VALUE black_ary = rb_ary_new();
  VALUE white_ary = rb_ary_new();
  VALUE none_ary  = rb_ary_new();
  VALUE status = rb_hash_new();
  uint64_t black = 0, white = 0, blank = 0, p = 0;
  struct bit_board *ptr;
  Data_Get_Struct(self, struct bit_board, ptr);

  black = ptr->black;
  while (black != 0) {
    p = black & (~black + 1);
    rb_ary_push(black_ary, BB2XY(p));
    black ^= p;
  }
  rb_hash_aset(status, ID2SYM(rb_intern("black")), black_ary);

  white = ptr->white;
  while (white != 0) {
    p = white & (~white + 1);
    rb_ary_push(white_ary, BB2XY(p));
    white ^= p;
  }
  rb_hash_aset(status, ID2SYM(rb_intern("white")), white_ary);

  blank = ~(ptr->black | ptr->white);
  while (blank != 0) {
    p = blank & (~blank + 1);
    rb_ary_push(none_ary, BB2XY(p));
    blank ^= p;
  }
  rb_hash_aset(status, ID2SYM(rb_intern("none")), none_ary);

  return status;
}

#to_sString

Returns a string of the game board in human-readable form.

Returns:

  • (String)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/reversi/board.rb', line 52

def to_s
  "     #{[*'a'..'h'].join("   ")}\n" <<
  "   #{"+---"*8}+\n" <<
  (0..63).map do |i|
    case 1
    when get_black[63 - i] then "\e[#{@options[:disk_color_b]}m#{@options[:disk_b]}\e[0m"
    when get_white[63 - i] then "\e[#{@options[:disk_color_w]}m#{@options[:disk_w]}\e[0m"
    else " "
    end
  end
  .map{ |e| "| #{e} |" }.each_slice(8).map(&:join)
  .map{ |line| line.gsub(/\|\|/, "|") }
  .tap{ |lines| break (0..7).map{ |i| " #{i+1} #{lines[i]}" } }
  .join("\n   #{"+---"*8}+\n") <<
  "\n   #{"+---"*8}+\n"
end

#undo!Object

Pops a hash of the game board off of the stack, and that is stored in the instance variable.(‘@bit_board`)



79
80
81
82
83
# File 'lib/reversi/board.rb', line 79

def undo!
  bb = @stack.pop
  set_black(bb[:black])
  set_white(bb[:white])
end