オセロの機能としては最後 待ったの実装
0. ビット・マスクによる裏返し処理の修正
JAVAでオセロを作る本
を読んだところ裏返しの判定にはビット・マスクを利用するとよいとの事だったので実装しなおした
今までは配列に方向を保存して判別していたものをビット演算*1で判別できるようになる
NONE = 0
UPPER_LEFT = 1
UPPER = 2
UPPER_RIGHT = 4
RIGHT = 8
LOWER_RIGHT = 16
LOWER = 32
LOWER_LEFT = 64
LEFT = 128
...
def turnable_direction (col, row)
direction = NONE
if @board[col-1][row-1] == -@stone_color
...
if @board[col-i][row-i] == @stone_color
direction |= UPPER_LEFT
end
end
if @board[col-1][row] == -@stone_color
...
if @board[col-i][row] == @stone_color
direction |= UPPER
end
end
if @board[col-1][row+1] == -@stone_color
...
if @board[col-i][row+i] == @stone_color
direction |= UPPER_RIGHT
end
end
if @board[col][row+1] == -@stone_color
...
if @board[col][row+i] == @stone_color
direction |= RIGHT
end
end
if @board[col+1][row+1] == -@stone_color
...
if @board[col+i][row+i] == @stone_color
direction |= LOWER_RIGHT
end
end
if @board[col+1][row] == -@stone_color
...
if @board[col+i][row] == @stone_color
direction |= LOWER
end
end
if @board[col+1][row-1] == -@stone_color
...
if @board[col+i][row-i] == @stone_color
direction |= LOWER_LEFT
end
end
if @board[col][row-1] == -@stone_color
...
if @board[col][row-i] == @stone_color
direction |= LEFT
end
end
return direction
end
def get_turnable_cells
turnable_cells = []
@board.each_with_index do |col, i|
col.each_with_index do |row, j|
if row == EMPTY
turnable_direction = turnable_direction (i,j)
if turnable_direction != NONE
turnable_cells.push([i,j])
end
end
end
end
return turnable_cells
end
def reverse_stone(col, row)
turn_direction = turnable_direction (col, row)
if turn_direction & UPPER_LEFT != 0
...
end
if turn_direction & UPPER != 0
...
end
if turn_direction & UPPER_RIGHT != 0
...
end
if turn_direction & RIGHT != 0
...
end
if turn_direction & LOWER_RIGHT != 0
...
end
if turn_direction & LOWER != 0
...
end
if turn_direction & LOWER_LEFT != 0
...
end
if turn_direction & LEFT != 0
...
end
end
1. 実装の方針その1〜盤の状態の保存方法〜
考えたのは3通り
- 全部の盤の様子を保存する
- 裏返した石の座標を保存する
- 裏返した方向と枚数を保存する
1は無駄が多すぎるのでなし、3はひっくり返すメソッドを再利用できないのでなしにして、2の方針でいくことにした。
2. 実装の方針その2〜パスの処理〜
特に何もない展開なら今までと逆にターン数を1戻して石の色を反対にするだけで待ったの実装は完了するはずだけれども、実際にはパスをして2ターン以上連続で同じ色の石が置かれる場合がある。そのときに律儀に色を反転させているとめちゃくちゃになってしまうのでパスの処理をうまく考えてやる必要がある
で考えたのは
- パスだったことを記録する
- 置いた石の色を記録する
1の方法では実際のターン数との乖離をなくす方法が楽じゃなさそうだったので2でいくことに
3. 座標の保存
裏返した石の座標とその色は棋譜を保存する配列に一緒に入れることにした
1手戻したあとに別の手を打つこともあるので棋譜の保存は配列にpushするのではなくターン番目の要素に追加するようにした
- 石を置いた時にその座標と石の色を保存する
- 石を返した時にその座標を保存し返し終わったらまとめて棋譜と同じ配列に保存する
...
def put_stone(cell, move)
...
@record[@turn] = [move, @stone_color]
...
end
def reverse_stone(col, row)
turn_direction = turnable_direction(col, row)
turned_cells = []
if turn_direction & UPPER_LEFT != 0
i = 1
while @board[col-i][row-i] == -@stone_color
@board[col-i][row-i] = @stone_color
turned_cells.push([col-i,row-i])
i += 1
end
end
if turn_direction & UPPER != 0
i = 1
while @board[col-i][row] == -@stone_color
@board[col-i][row] = @stone_color
turned_cells.push([col-i,row])
i += 1
end
end
if turn_direction & UPPER_RIGHT != 0
i = 1
while @board[col-i][row+i] == -@stone_color
@board[col-i][row+i] = @stone_color
turned_cells.push([col-i,row+i])
i += 1
end
end
if turn_direction & RIGHT != 0
i = 1
while @board[col][row+i] == -@stone_color
@board[col][row+i] = @stone_color
turned_cells.push([col,row+i])
i += 1
end
end
if turn_direction & LOWER_RIGHT != 0
i = 1
while @board[col+i][row+i] == -@stone_color
@board[col+i][row+i] = @stone_color
turned_cells.push([col+i,row+i])
i += 1
end
end
if turn_direction & LOWER != 0
i = 1
while @board[col+i][row] == -@stone_color
@board[col+i][row] = @stone_color
turned_cells.push([col+i,row])
i += 1
end
end
if turn_direction & LOWER_LEFT != 0
i = 1
while @board[col+i][row-i] == -@stone_color
@board[col+i][row-i] = @stone_color
turned_cells.push([col+i,row-i])
i += 1
end
end
if turn_direction & LEFT != 0
i = 1
while @board[col][row-i] == -@stone_color
@board[col][row-i] = @stone_color
turned_cells.push([col,row-i])
i += 1
end
end
@record[@turn].push(turned_cells)
end
4. 待ったの実装
棋譜を保存している配列@record
の中は
[[指し手, 石の色, [返した石の座標1, 2, 3]], [..], ...]
のようになっているこれを使って待ったを作る
- 初手以外のとき(初手の待ったは変化しない)
- 棋譜の配列から指し手の座標を取得する
- その座標のマスを空きマスにする
- 棋譜の配列から石の色を取得する
- 棋譜の配列から返した石の座標を取得し、そのマスを取得した色と反対の色にする
- 現在の色を取得した色にする
def undo
if @turn != 0
@pass_count = 0
@turn -= 1
cell = cordinate_transformation(@record[@turn][0])
@board[cell[0]][cell[1]] = EMPTY
@stone_color = -@record[@turn][1]
@record[@turn][2].each do |cell|
@board[cell[0]][cell[1]] = @stone_color
end
@stone_color = -@stone_color
end
end
完成
ここから変更したこと
- 60手になったら終了をなくし2連続でパスしたら終了のみに
- start_gameメソッドのwhile文の部分をphaseメソッドに移動
- start_gameメソッドのwhile文の後の部分をend_gameメソッドに
- ゲームの進行をループではなく再帰に
- パスになった時に次も確認しパスならend_gameメソッドを呼び出す
gist.github.com
とりあえずこれで基本機能は完成オブジェクト指向っぽく直していく