プログラミング上達講座の5回目です。
オセロを題材にして
プログラムを考えてみましょう。
解説動画はこちら
オセロとは
オセロご存知でしょうか?
結構昔からあるゲームで
やったことがある人も多いんじゃないかと。
一応のルールは8 x 8 のマスを用いて
白黒の石を置き、相手の石をひっくり返してゆくゲーム。
相手の石を自分の石で挟めば
ひっくり返しが成立し、ひっくり返せないところには置けません。
最終的に石の多い方が勝ちというルールではあります。
初級編:ボードデータを作成してみよう
グローバル変数board
として作成する。
変数SIZE
を用意しておいて
そのサイズの分だけ サイズ x サイズ のボードを作り
ボードデータは二次元のリスト型
とする
初期配置として
WHITE(0)
,BLACK(1)
を
ボードの真ん中に交互に4つ置く
それ以外はNone
で埋める
中級編:石を置いたらひっくり返せるかどうかを判定する関数を考える
石を置いたらひっくり返せるかどうかを判定する
関数flippable
を作成しよう。
置く場所には石がなくて、石は置けるものとする。
def flippable(y , x , player):
処理
return True or False
引数player
は WHITE(0) or BLACK(1)が入る
y
, x
は縦 , 横方向の座標(0 - 7)が入るものとする
上級編:石を置く処理を行う関数を考える
石を置く処理を行う関数
flip_stone
を作成しよう。
1.おく場所に石があるかどうかを判定する。
石があったら置けないのでFalseを返す。
2.置いたらひっくり返せるかを判定する。
返せる石がなければ置けないのでFalseを返す。
3.石を置いてひっくり返す
相手の石をひっくり返して
ボードの状態を変更してTrueを返す。
def flip_stone(y , x , player):
処理
return True or False
ボードデータはグローバル変数の
board
として考えてみましょう。
解答編
初級編の解答
オセロの盤面のデータ構造を考えます。
二次元のリストを作れば良いです。
# 初期設定
SIZE,WHITE,BLACK = 8,0,1
# ボードを作成
board = [[None for i1 in range(SIZE)] for i2 in range(SIZE)] # サイズ分の大きさのボードを作成
board[3][3],board[3][4],board[4][3],board[4][4] = WHITE,BLACK,BLACK,WHITE # 初期配置
print(board)
[[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, 0, 1, None, None, None],
[None, None, None, 1, 0, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None]]
こんな感じで石は0,1
石がなければNoneで格納すれば良いでしょう。
中級編の解答
プログラミングではよく用いられる手法ですが
ここでは「探索」というものが出てきます。
今その場所に石を置いたと考えて
(y,xを置く座標と考える)
8方向に探索に行きます。
8方向に進んだ時に
ひっくり返せる石が
有るかどうかを探します。
8方向の考え方:
今いる位置からどの方向に
進むのか数値で考えます。
右方向に探索する場合
自分が1の時
置いた場所から次の石が
None → 石が無いので置けない
1,0,0 → 最初が自分と同じなのでひっくり返せない
0,0,0 → 自分と同じ石が無いのでひっくり返せない
0,0,1 → 最初が自分と同じ石でなく
どこかに自分と同い石があるのでひっくり返せる
という感じになります。
あとは最初の石と、最後までの過程を
変数に記録しておけば探索できると思います。
コードはこんな感じになりました。
# ボードに石を置いたら返せるかどうかを判定する関数
def flippable(y , x , player):
enemy = 1 if player==0 else 0
# 8方向を探索する
for dy,dx in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:
depth,flg = 1,0
for i in range(SIZE):
# 検索対象の座標を設定する
ry,rx = y + (dy * depth) , x + (dx * depth)
# ボードの範囲を超えていたら抜ける
if any([rx<=-1,ry<=-1,rx>=SIZE,ry>=SIZE]):
break
# 返せる石がなくてNoneだったら抜ける
if board[ry][rx] is None:
break
# 返せる石がなくてplayerと同じ石だったら抜ける
elif flg==0 and board[ry][rx]==player:
break
# 返せる石が初めて見つかったらflg=1にする
elif flg==0 and board[ry][rx]==enemy:
flg = 1
# flg=1の状態でplayerと同じ石が見つかったら処理を抜ける
elif flg==1 and board[ry][rx]==player:
return True
depth += 1
return False
# ボードを作成
board = [[None for i1 in range(SIZE)] for i2 in range(SIZE)] # サイズ分の大きさのボードを作成
board[3][3],board[3][4],board[4][3],board[4][4] = WHITE,WHITE,BLACK,WHITE # 初期配置
flippable(2 , 5 , 1)
True
上級編の解答
考え方としてはまずはボードが空いていないなら
Falseを返します。
置けるかどうかは中級編のflippableを再利用し
ひっくり返せる所を探索して、その座標を記録しておき
最後に石をひっくり返す処理を行います。
コードはこんな感じになりました。
# 石をひっくり返す
def flip_stone(y , x , player):
# ボードが空いていなければ処理を抜ける
if board[y][x] is not None:
return False
# 置いたらひっくり返せるかを判定する
if not flippable(y , x , player):
# ひっくり返せないなら置けない
return False
# ひっくり返せるなら、石をひっくり返してボードを更新する
else:
enemy = 1 if player==0 else 0
result = [(y , x)]
for dy,dx in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:
depth,flg = 1,0
tmp = []
for i in range(SIZE):
# 検索対象の座標を設定する
ry,rx = y + (dy * depth) , x + (dx * depth)
# ボードの範囲を超えていたら抜ける
if any([rx<=-1,ry<=-1,rx>=SIZE,ry>=SIZE]):
break
# 返せる石がなくてNoneだったら抜ける
if board[ry][rx] is None:
break
# 返せる石がなくてplayerと同じ石だったら抜ける
elif flg==0 and board[ry][rx]==player:
break
# 返せる石が初めて見つかったらflg=1にする
elif flg==0 and board[ry][rx]==enemy:
flg = 1
# 返せる座標を追加する
tmp.append((ry,rx))
# 返せる石が初めて見つかったら返せる座標を追加する
elif flg==1 and board[ry][rx]==enemy:
tmp.append((ry,rx))
# flg=1の状態でplayerと同じ石が見つかったらresultに追加する
elif flg==1 and board[ry][rx]==player:
result += tmp
break
depth += 1
# 石をひっくり返す
for y2,x2 in result:
board[y2][x2]=player
return True
さてこれでオセロゲームの用意が出来ました。
描画用の関数なども用意して
オセロゲームを試してみましょう。
# ゲームを試してみる
# ボードを作成
board = [[None for i1 in range(SIZE)] for i2 in range(SIZE)] # サイズ分の大きさのボードを作成
board[3][3],board[3][4],board[4][3],board[4][4] = WHITE,BLACK,BLACK,WHITE # 初期配置
# ボードの状態を描画する
def print_board():
print('',end='\t')
for x0 in range(8):
print('X:{0}'.format(x0),end='\t')
print()
for y1,b in enumerate(board):
print('Y:{0}'.format(y1),end='\t')
for x1,s in enumerate(b):
print(' ' if s is None else s,end='\t')
print()
# 初期配置を確認
print_board()

print(flip_stone(2 , 3 , 1) )
# 更新後の配置
print_board()

まとめ
自分一人でやるのは寂しいので
このあと、コードを加えて
オセロゲームっぽく仕上げて
AIなんかも搭載してあげれば
立派なオセロゲームになると思います。
プログラミングの基本的な考え方を
身につけるにはちょうど良い題材だと思いますので
暇な方は試してみてください。
過去にはこんなオセロも
作っていましたね。
三つ巴対決盛り上がる説を検証するためにオセロゲーム作ってみた
今回はこれまでです。
それでは。
作者の情報
乙pyのHP:
http://www.otupy.net/
Youtube:
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw
Twitter:
https://twitter.com/otupython