これはなに
Pythonでオセロ対戦してみます。
盤面の初期化と表示
盤面は、サイズ64の1次元配列で持つことにし、値は下記の通りとします。
- 0: 空きマス
- 1: 黒駒(*で表現)
- 2: 白駒(oで表現)
python3
import numpy as np
def create_board():
a = np.zeros(64, dtype=int)
a[27] = a[36] = 1
a[28] = a[35] = 2
return a
def print_board(a):
print(' a b c d e f g h')
for i in range(8):
print(i+1, end=' ')
print(' '.join('.*o'[j] for j in a[i*8:][:8]))
a = create_board()
print_board(a)
>>>
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . * o . . .
5 . . . o * . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
駒を置く
自駒w(1 or 2)を位置p(0~63)に置く関数を作ります。
縦横斜めの8通りは、1次元データなので、現在位置から[-9, -8, -7, -1, 1, 7, 8, 9] ずれた方向を見ます。これは、[-1, 0, 1]と[-8, 0, 8]の組合せで調べます(0と0のペアは除く)。
各方向毎に配列(b)を取り出し、相手の駒かどうか(b==3-w)の0-1を累積でかけると、とれる駒が1で表されるので、合計(sum)をとると、とれる駒数nがわかります。
位置20("e3")に置いてみましょう。
python3
def put_piece(a, p, w, puton=True, chk=True):
t, x, y = 0, p%8, p//8
for di, fi in zip([-1, 0, 1], [x, 7, 7-x]):
for dj, fj in zip([-8, 0, 8], [y, 7, 7-y]):
if not di == dj == 0:
b = a[p+di+dj::di+dj][:min(fi, fj)]
n = (b==3-w).cumprod().sum()
if b.size <= n or b[n] != w: n = 0
t += n
if puton:
b[:n] = w
if puton:
if chk: assert(a[p] == 0 and t > 0)
a[p] = w
return t
put_piece(a, 20, 1)
print_board(a)
>>>
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . * . . .
4 . . . * * . . .
5 . . . o * . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
ベストポジションを探す
良い手は、下記のポイントを適当に重みをつけて足して探しましょう。
- たくさんとれる。
- 相手の打つところが少なくなる
python3
def best(a, w):
from math import exp
r, b, c = [], a.copy(), 1+exp(-np.count_nonzero(a)/16)
for i in range(64):
if b[i] != 0: continue
t = put_piece(b, i, w, True, False)
if t == 0:
b[i] = 0
continue
u = sum(b[j]==0 and put_piece(b, j, 3-w, False) > 0 for j in range(64))
r.append((t-c*u+np.random.rand()*0.5, i))
b = a.copy()
return sorted(r)[-1][1] if r else -1
対戦してみる
pでパス、qで終了します。
python3
if __name__ == '__main__':
a = create_board()
w = 1
while np.count_nonzero(a) < 64:
print_board(a)
s = input('> ')
if not s or s=='q': break
if s != 'p':
try:
x, y = ord(s[0])-97, int(s[1])-1
put_piece(a, x+8*y, w)
except:
continue
p = best(a, 3-w)
if p >= 0:
put_piece(a, p, 3-w)
print_board(a)
n1, n2 = (a==1).sum(), (a==2).sum()
print('%d - %d %s' % (n1, n2,
'You win' if n1 > n2 else
'You lose' if n1 < n2 else 'Draw'))
>>>
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . * o . . .
5 . . . o * . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
> e3
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . * o . .
4 . . . * o . . .
5 . . . o * . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
> f4
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . * o . .
4 . . . * * o . .
5 . . . o o o . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
> g3
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . . * * * .
4 . . o o o o . .
5 . . . o o o . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
> c5
a b c d e f g h
1 . . . . . . . .
2 . . . . . . . .
3 . . . o * * * .
4 . . o o o o . .
5 . . * o o o . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
>
もう少し先まで読む方がいいですね。
Docker用意しました
bash
docker run -it --rm tsutomu7/reversi python reversi.py
ブラウザでやるときは、下記のようにしてください。
bash
firefox http://localhost:8888 &
docker run -it --rm -p 8888:8888 tsutomu7/reversi
以上