Kivyでオセロ開発Part1

Kivyでオセロ開発のパートいちを示すサムネイル

こんにちは、にわこまです。

今回は、Kivyでオセロを開発します。そのためにも、まずはコマンドプロンプト上で動かすことができるオセロを開発します。私の他の記事でもオセロの作り方を紹介していますが、本記事で1からより分かりやすく紹介します。

 

誤字脱字など、何かございましたらご連絡お願い致します。

 

 

スポンサードサーチ


Kivyでオセロ開発Part1

今回のシリーズは、Kivyを使ってオセロを開発することを目的としています。

 

今回のシリーズの主な内容

1.コマンドプロンプト上で遊べるオセロの開発
2.kivyを使ってオセロを開発(Kivy Languageは使用しない)
3.kivyを使ってオセロを開発(Kivy Languageを使用する)

 

目的を達成するためにも、まずはコマンドプロンプト上で遊ぶことができるオセロを開発します。

 

次に、kivyを使ってオセロを開発します。しかし、Kivy Languageは使用しません。つまり、pythonだけをつかってオセロアプリケーションを開発します。

 

最後に、Kivy Languageとpythonを使ってオセロアプリケーションを開発します。

 

 

今回は

今回は、コマンドプロンプト上で遊べるオセロの中でも、プレイヤーとコンピュータの両者に共通する機能を開発します。

 

 

共通機能

共通機能(CommonFunc)とは、プレイヤーとコンピュータのどちらにも必要な機能のことを指します。

共通機能があることで、全体のコードを短くすることができます。

 

共通機能を見つけるために、プレイヤーに必要な機能とコンピュータに必要な機能について考えます。

 

プレイヤー

・アドレス(a1、c8、etc…)からマスの座標への変換する機能

・プレイヤーが選択したマスに置くことが可能か調べる機能

・盤面を更新する機能

 

コンピュータ

・石を置くことができるマスからランダムにマスを選択する機能

・盤面を更新する機能

 

 

プレイヤーとコンピュータに必要な機能で共通している機能は2つあると考えました。 共通機能は、「盤面を更新する機能」と「石を置くことができるマスを取得する機能」であると思います。

 

「盤面を更新する機能」については、プレイヤーとコンピュータの両方に出されている機能であるため説明は不要であると思います。そのため説明は省きます。

 

「石を置くことができるマスを取得する機能」について説明します。

コンピュータにおいては、そもそも石を置くことが出来るマスの一覧からマスを選択するため、「石を置くことができるマスを取得する機能」が必要であると考えました。

プレイヤーにおいては、選択したマスに石を置くことが可能か調べるときに、「石を置くことができるマスを取得する機能」を使うことができると考えました。

 

 

スポンサードサーチ


共通機能1

「石を置くことができるマスを取得する機能」とは、文字通り石を置くことが可能なマスの一覧を取得する機能です。

 

では「石を置くことができる」とは、どのようなことなのか考えます。

私は、以下のように考えました。

1.相手の石の隣のマス

2.空白のマス

3.1と2の条件をふまえた上で、選択したマスの隣の相手の石の方向に、空白のマスまたは範囲外になる前に自分の石が存在する場合のマス

1と2と3の3つの条件を満たした場合に、「石を置くことができる」と言えると考えました。

 

言葉では分かりにくいと思うため図で説明します。

 

1.相手の石の隣のマス

相手の石の隣のマスは、最大で8つあります。

相手の石が黒石である場合、隣のマスとは赤色の円があるマスのことを指します。

相手の石の隣のマスを示す画像

具体的な盤面で示します。自分の石を白石、相手の石を黒石として、下図の左側のような盤面について考えます。このときの相手の隣のマスは、下図の右側の薄い赤い円があるマスのことを指します。

相手の隣のマスを具体的に示した画像

 

2.空白のマス

空白のマスは、単純に石が置かれていないマスのことです。 具体的な盤面で示します。下図の左側のような盤面について考えます。このとき空白のマスは、下図の右側の薄い青い円があるマスのことを指します。

空白のマスを具体的に示した画像

 

3.1と2の条件をふまえた上で、選択したマスの隣の相手の石の方向に、空白のマスまたは範囲外になる前に自分の石が存在する場合のマス

1と2の条件を満たしているマスを具体的な盤面で示します。自分の石を白石、相手の石を黒石として、下図の左側のような盤面について考えます。

このとき、「相手の石の隣のマス」と「空白のマス」を満たすマスは、下図の右側の薄い紫色の円があるマスのことを指します。

相手の石の隣のマスと空白のマスという2つの条件を満たしたマスを具体的に示した画像

次に、「選択したマスの隣の相手の石の方向」について説明します。1と2の条件を満たしたマスから、下図の薄い紫色の円があるマスを選択した場合を考えます。

このとき「選択したマスの隣の相手の石の方向」は、赤矢印のことを指します。

方向を具体的に示した画像

次に、「空白のマスまたは範囲外になる前に自分の石が存在する場合のマス」について説明します。この条件について考えるパターンは、3つあります。

1.「○●○」のように、自分の石で相手の石を1つ以上挟んでいる場合

2.「○●空白」のように、相手の石の方向に調べていったとき、自分の石よりも先に空白のマスがある場合

3.「○●範囲外」のように、相手の石の方向に調べていったとき、自分の石よりも先に範囲外になる場合

選択したマスが、石を置くことができるマスと言える場合は、1の場合のみです。

 

各場合において、具体的な盤面の例を示します。

1の場合。(自分の石は白石。選択したマスは薄紫。方向は赤矢印。)

2の場合。(自分の石は白石。選択したマスは薄紫。方向は赤矢印。)

3の場合。(自分の石は白石。選択したマスは薄紫。方向は赤矢印。)

 

以上で、石を置くことができるマスの見つけ方を理解できたと思います。何回読んでも分からない方は、メールかTwitterのDMまで連絡をお願い致します。

 

石を置くことができるマスについて、具体的な盤面の例を示します。自分の石を白石、相手の石を黒石として、下図の左側のような盤面について考えます。

このとき、石を置くことができるマスは、下図の右側の薄い緑色の円があるマスのことを指します。

 

 

コード

今まで説明してきた、「石を置くことができるマス」をコード化しました。「石を置くことができるマス」のソースコードです。othello_alpha.pyというファイルを新規作成して、以下のコードを書きこみ保存します。

import numpy as np
import random
import sys

class CommonFunc:
    def __init__(self, mystone, opponentstone):
        self.mystone = mystone
        self.opponentstone = opponentstone

    def getStoneLocation(self):
        # 相手stoneの座標のリストを返す
        locaList = list(zip(*np.where(field == self.opponentstone)))
        return locaList

    def getNextLocation(self):
        # 相手stoneの隣マス(8方向)の座標のリストを返す
        sl = self.getStoneLocation()
        locaList = []
        for x, y in sl:
            for i, j in offset:
                t = (x+i, y+j)
                locaList.append(t)
        return locaList

    def getEmptyLocation(self):
        # 空のマスを返す
        locaList = list(zip(*np.where(field == 0)))
        return locaList

    def getPossibleLocation(self):
        # 石を置くことが出来るマスを返す
        nl = self.getNextLocation()
        el = self.getEmptyLocation()
        andList = list(set(nl) & set(el))
        locaDict = {}
        for x, y in andList:
            offsetList = []
            for i, j in offset:
                xpos, ypos = x + i, y + j
                while((0 <= xpos and xpos < field.shape[0]) and (0 <= ypos and ypos < field.shape[1])):
                    if(field[xpos, ypos] == self.opponentstone):
                        xpos = xpos + i
                        ypos = ypos + j
                        continue
                    elif(field[xpos, ypos] == self.mystone):
                        if(2 <= abs(xpos - x) or 2 <= abs(ypos - y)):
                            offsetList.append((i, j))
                        break
                    else:
                        break
            if(len(offsetList) != 0):
                locaDict[(x, y)] = offsetList
        return locaDict

 

コードの解説

1行目~3行目は、今回使うモジュールをインポートしています。

 

5行目の「class CommonFunc:」は、共通機能のクラスを作成しています。

 

6行目~8行目は、クラスのインスタンス変数を設定しています。簡単に言うと初期値を設定しています。自分の石と相手の石を設定しています。

 

10行目~13行目の「getStoneLocation」は、相手の石が置いてある座標をリストで返却する関数です。

np.whereにより、相手の石の行と列を取得。その後zip関数により(行,列)の形に変換。さらに、それをリスト型に変換しています。

 

15行目~23行目の「getNextLocation」は、相手の石の隣のマスをリストで返却する関数です。

私が設定したグローバル変数offsetを使い、相手の石の隣のマスを導ぎだしています。

 

25行目~28行目の「getEmptyLocation」は、盤面の空白のマスの座標をリストで返却する関数です。

np.whereにより、空白のマスの行と列を取得。その後zip関数により(行,列)の形に変換。さらに、それをリスト型に変換しています。

 

30行目~53行目の「getPossibleLocation」は、石を置くことができるマス(座標)を辞書型で返却する関数です。座標をキー、方向を値として辞書型にしています。

まず、「相手の石の隣のマス」と「空白のマス」の両方の条件を満たしているマスを取得。その後、offsetを使い相手の石の方向とその方向に「空白のマスまたは範囲外になる前に自分の石がある」か確かめます。ある場合は辞書に追加します。

 

 

共通機能2

「盤面を更新する機能」とは、石をひっくり返す機能のことです。自分の石で挟んだ相手の石を自分の石に変えていきます。

 

「盤面を更新する機能」のソースコードです。othello_alpha.pyに以下のコードを追記します。

import numpy as np
import random
import sys

class CommonFunc:
    
  # 省略(インスタンス化、石を置くことができるマスを取得する関数)

    def updateField(self, pos):
        # fieldの更新を行う
        possibleDict = self.getPossibleLocation()
        field[pos[0], pos[1]] = self.mystone
        for x, y in possibleDict[pos]:
            xpos = pos[0] + x
            ypos = pos[1] + y
            while((0 <= xpos and xpos < field.shape[0]) and (0 <= ypos and ypos < field.shape[1])):
                if(field[xpos, ypos] == self.opponentstone):
                    field[xpos, ypos] = self.mystone
                    xpos = xpos + x
                    ypos = ypos + y
                else:
                    break

 

コードの解説

この関数は前提条件として、選択できないマスを考慮しません。つまり、プレイヤーが選択してマスが選択可能かどうかの確認は行わないということです。

 

9行目~22行目の「updateField」が、盤面を更新する関数です。引数には、選択したマスの座標が(行,列)という形で与えられます。

 

11行目の「possibleDict = self.getPossibleLocation」は、選択することができるマスの辞書型を取得しています。

 

12行目の「field[pos[0], pos[1]] = self.mystone」は、引数のマスの座標に自分の石を代入しています。

 

13行目以降は、辞書から方向を取り出し、その方向のマスを1つひとつ確認して、自分の石を代入しています。

 

 

スポンサードサーチ


動作確認

今回作成した、2つの機能の動作確認を行います。その前に、グローバル変数の説明をします。

今回の動作確認で必要なグローバル変数は、「field」と「offset」です。

「field」は、盤面を表します。また、黒石を1、白石を2、空白のマスを0と表現しています。

「offset」は、方向を表します。盤面をnumpyのarrayで表現している関係上(左上基準)、また基準のマスにoffsetをプラスして使用するため、以下の図のような表現になります。

例として、基準のマスが(3, 4)である場合のoffsetを下図に示します。

 

グローバル変数のソースコードです。othello_alpha.pyに以下のコードを追記します。

import numpy as np
import random
import sys

class CommonFunc:
  #省略

# global 変数
global field
field = np.array([[0,0,0,0,0,0,0,0],
                  [0,0,0,0,0,0,0,0],
                  [0,0,0,0,0,0,0,0],
                  [0,0,0,2,1,0,0,0],
                  [0,0,0,1,2,0,0,0],
                  [0,0,0,0,0,0,0,0],
                  [0,0,0,0,0,0,0,0],
                  [0,0,0,0,0,0,0,0]])
global offset
offset = [(-1, -1), (-1, 0), (-1, 1),
          ( 0, -1),          ( 0, 1),
          ( 1, -1), ( 1, 0), ( 1, 1)]

 

 

共通機能1の動作確認

「石を置くことができるマスを取得する機能」の動作確認を行います。コマンドプロンプトを開き、othello_alpha.pyを保存したフォルダまで移動します。その後、インタプリタを起動させます。

 

インタプリタを起動させた後、othello_alpha.pyから共通機能のクラスとfieldをインポートします。

>>> from othello_alpha import CommonFunc
>>> from othello_alpha import field
>>>

 

次に、共通機能のクラスを作成します。自分の石の色を黒(1)とします。

>>> cf = CommonFunc(1, 2)
>>>

 

getPossibleLocation関数を実行して、石を置くことができるマスを取得します。以下のように表示されれば、問題ありません。

>>> cf.getPossibleLocation()
{(5, 4): [(-1, 0)], (3, 2): [(0, 1)], (4, 5): [(0, -1)], (2, 3): [(1, 0)]}
>>>

 

 

共通機能2の動作確認

共通機能1の動作確認の状態を引き継ぎ、「盤面を更新する機能」の動作確認を行います。 まず、現状の盤面を確認します。

>>> field
array([[0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 1, 0, 0, 0],
       [0, 0, 0, 1, 2, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0]])
>>>

 

次に、updateField関数を使用して盤面の更新を行います。また、fieldを表示します。以下のように表示されていれば、問題ありません。

>>> pos = (5, 4)
>>> cf.updateField(pos)
>>> field
array([[0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 1, 0, 0, 0],
       [0, 0, 0, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0]])
>>>

 

以上で動作確認は完了です。

 

 

まとめ

今回は、コマンドプロンプト上で遊ぶことができるオセロ開発に着手しました。その中でも、プレイヤーとコンピュータの共通する機能を作成しました。

共通部分を見つけることで、全体のコードを短くすることができます。

以下に本記事で作成したコードをまとめたものを示します。

 

共通機能の「石を置くことができるマスを取得する機能」と「盤面を更新する機能」は、理解することができましたか?

出来ていなければ、メールかTwitterのDMで連絡をお願い致します。

 

 

最後までお読みいただきありがとうございます。


スポンサードサーチ