Pythonで作るn次元n目並べ その1:1次元n目並べ
2020/03/07 categories:Python| tags:Python|Connect-n-nd|
立体四目並べが大好きなのでPythonで作ろうと思いました。
作るプログラムはなるべく汎用性がある方が良いかなと考え、どうせならn目並べに対応したプログラムを作ろうと考えました。さらに立体だけじゃなく、平面にも対応したものが良いかなと考えたときに、4次元、5次元としていったらどうなるのだろうとか考えてしまい、いっそn次元n目並べを目指してみようと考えてしまいました。調べてみると多次元五目並べというソフトがあったりしますが、頭の体操とPythonで作る初めてのゲームという意味ではちょうどいいかなと思います。
そこでまずはとっかかりとして1次元n目並べから作り始めてみようと思います。
盤面と駒
1次元の盤面はいたってシンプルで、任意の大きさのリストを作成するだけです。
arr = [ 0 for i in range(field_size) ]
print('field is', arr)
## field is [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
駒が置かれていない状態を0としたのでリストの中はすべて0です。このリストにPlayer 1は1、Player 2は2を置いていくことにします。2人対戦を想定していますが、n人目のPlayerはnを入れると考えれば複数人対応もできちゃいます。n人対戦n次元n目並べがゲームとして成り立つのかはよくわかりません。
駒を置く
AIプレーヤーを作成するのは大変だろうということで、デバッグでは2人が交互にランダムな場所に駒を置くことにしました。
## put pieces randomly
for i in range( int(len(arr)/2) ):
# player 1
index = choice_index_randomly(arr)
arr[index] = 1
print('turn', i, 'player 1 choice :', index, arr)
if is_win(arr, index, connect_length):
print('player 1 is won!!!')
return
# player 2
index = choice_index_randomly(arr)
arr[index] = 2
print('turn', i, 'player 2 choice :', index, arr)
if is_win(arr, index, connect_length):
print('player 2 is won!!!')
return
交互に駒を置く処理を繰り返す回数はarr(盤面)の大きさ/Player数としています。Playerはchoice_index_randomlyメソッドで駒を置くインデックスを取得して、arrのインデックスにPlayer番号を入れるだけです。
import random
def choice_index_randomly(arr):
indexes = [ i for i, d in enumerate(arr) if d==0 ]
return random.choice(indexes)
choice_index_randomlyではarr中から値が0の要素のインデックスのリストを作成して、そのリストからrandomライブラリでランダムに選びます。
勝ちの判定
上記のforの中で駒を置いた後にis_win()メソッドで勝ちか判定しています。
def is_win(arr, index, connect_length):
s = index - connect_length + 1
if s < 0:
s = 0
e = index + connect_length + 1
if e > len(arr):
e = len(arr)
str1 = ''.join( [str(i) for i in arr[s:e]] )
str2 = ''.join( [str(arr[index]) for i in range(connect_length)] )
if str2 in str1:
return True
return False
connect_lengthの値の分、連続して駒が置かれていれば勝ちとなります。そこで、駒を置いた場所indexの値から±connect_lengthのサイズでarrをスライスします。ただし、スライスの始まりが0より小さければ0、スライスの終わりがarrの要素数以上であればarrの要素数としています。
このスライスしたリストの中でプレーヤー番号がconnect_lengthの値の分、連続しているか判定するために、スライスしたリストをいったん文字列に置き換えて判定しています。例えば、Player1が3つ連続しているかを[0,1,2,1,1,1]というリストから判定するために、012111という文字列に変換した後、この文字列の中に111という文字列が含まれるかという判定を行います。
所感
1次元ということもあり判定する方向が1方向だけなので非常に簡単でした。リスト内でn個値が連続しているという判定をどうするか少し考えましたが、ほかの部分はPythonの基本のみなので練習にちょうどいいかもしれません。
改善案としては、速度アップのためにNumPyを使うとか、リスト内の値連続判定の他のやり方を調べたり、それの速度の比較をしてみたりといったところです。次元を増やした場合に速度面で有利かもしれないので、次はNumPyへの書き換えをしてみようと思います。
ソースコード
import random
def choice_index_randomly(arr):
indexes = [ i for i, d in enumerate(arr) if d==0 ]
return random.choice(indexes)
def is_win(arr, index, connect_length):
s = index - connect_length + 1
if s < 0:
s = 0
e = index + connect_length + 1
if e > len(arr):
e = len(arr)
str1 = ''.join( [str(i) for i in arr[s:e]] )
str2 = ''.join( [str(arr[index]) for i in range(connect_length)] )
if str2 in str1:
return True
return False
def main():
field_size = 12
connect_length = 3
# create field
arr = [ 0 for i in range(field_size) ]
print('field is', arr)
# put pieces randomly
for i in range( int(len(arr)/2) ):
# player 1
index = choice_index_randomly(arr)
arr[index] = 1
print('turn', i, 'player 1 choice :', index, arr)
if is_win(arr, index, connect_length):
print('player 1 is won!!!')
return
# player 2
index = choice_index_randomly(arr)
arr[index] = 2
print('turn', i, 'player 2 choice :', index, arr)
if is_win(arr, index, connect_length):
print('player 2 is won!!!')
return
if __name__ == '__main__':
main()
実行結果
field is [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
turn 0 player 1 choice : 11 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
turn 0 player 2 choice : 4 [0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1]
turn 1 player 1 choice : 6 [0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 1]
turn 1 player 2 choice : 10 [0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 2, 1]
turn 2 player 1 choice : 8 [0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 2, 1]
turn 2 player 2 choice : 5 [0, 0, 0, 0, 2, 2, 1, 0, 1, 0, 2, 1]
turn 3 player 1 choice : 2 [0, 0, 1, 0, 2, 2, 1, 0, 1, 0, 2, 1]
turn 3 player 2 choice : 0 [2, 0, 1, 0, 2, 2, 1, 0, 1, 0, 2, 1]
turn 4 player 1 choice : 7 [2, 0, 1, 0, 2, 2, 1, 1, 1, 0, 2, 1]
player 1 is won!!!