Kivyでオセロ開発Part8

Kivyでオセロ開発パートはちを表すサムネイル

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

今回は、実際にオセロをプレイする画面を作成します。しかし、プレイ画面は記述するコードが多いため、今回はオセロの盤面だけを作成します。

 

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

 

 

スポンサードサーチ


Kivyでオセロ開発Part8

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

 

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

1.コマンドプロンプト上で遊べるオセロの開発

2.kivyを使ってオセロを開発(Kivy Languageは使用しない)←今ココ

3.kivyを使ってオセロを開発(Kivy Languageを使用する)

 

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

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

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

 

 

今回は

今回は、オセロを実際にプレイする画面のオセロの盤面を作成します。オセロの盤面は、実際に背景や線を計算して描画しているため難しいです。 Kivy

Languageを使わずにアプリケーションを作成する理由は、Kivy Languageを使ったときにpythonコードをどれだけ減らせるか実感して欲しいからです。

 

 

PlayWidget

PlayWidgetは、プレイ画面を表現するクラスです。

PlayWidgetは、BoxLayoutを継承して作られています。そのため左側と右側に分けることができます。

左側は、オセロの盤面を描画します。

右側は、得点板やタイトル画面に戻るボタンを設置します。

 

どのようなレイアウトか図で示します。

本記事で作成するプレイウィジェットのレイアウト

 

今回作成するのは、左側です。つまり、「2.AnchorLayout」と「3.FieldWidget(Widget)」です。

AnchorLayoutは、このレイアウトに追加するオブジェクトの表示位置を設定することができます。盤面を左側の真ん中に配置したかったため使用しています。

FieldWidgetは、盤面を表現するクラスです。線を引き、円で石を表現します。また、ゲームの流れもこのクラスで設定します。

 

PlayWidgetクラスのソースコードです。othello_beta.pyに以下のコードを追記します。

# 上記省略

class PlayWidget(BoxLayout):
    def __init__(self, **kwargs):
        super(PlayWidget, self).__init__(**kwargs)
        self.orientation = 'horizontal'
        anchor1 = AnchorLayout(anchor_x='center', anchor_y='center', size_hint_x=2)
        box = BoxLayout(orientation='vertical')
        anchor1.add_widget(Button(text='test1'))    # テスト用
        box.add_widget(Button(text='test2'))        # テスト用
        self.add_widget(anchor1)
        self.add_widget(box)
    pass

class BtnBlack(Button):
    # 省略

class BtnWhite(Button):
    # 省略

class SelectTurn(FloatLayout):
    # 省略

class BtnStart(Button):
    # 省略

class TitleWidget(FloatLayout):
    # 省略

class RootWidget(FloatLayout):
    def __init__(self, **kwargs):
        # 省略
    def update(self, *args):
        # 省略
    def gotoTitle(self):
        # 省略
    def gotoSelectTurn(self):
        # 省略
    def gotoPlayDisplay(self):
        self.clear_widgets()
        self.add_widget(PlayWidget())
    pass

class OthelloApp(App):
    def build(self):
        root = RootWidget()
        root.gotoPlayDisplay()  # 変更部
        return root


if __name__ == '__main__':
    OthelloApp().run()

 

コードの解説

3行目~13行目は、プレイ画面を表現するクラスです。

3行目の「PlayWidget(BoxLayout)」は、BoxLayoutを継承してPlayWidgetを作成しています。

6行目の「self.orientation = ‘horizontal’」は、PlayWidgetにオブジェクトを追加する方向を設定しています。horizontalは水平という意味であるため、追加されるオブジェクトは水平に追加されます。つまり、横方向(右側)に追加されます。

7行目の「anchor1 = AnchorLayout(省略)」は、左側のレイアウトです。anchor_xとanchor_yをcenterとしているため、追加されたオブジェクトは中央に表示されます。

また、size_hint_xが2であり、後述するboxはsize_hint_xがデフォルトの1であるため、横幅が、「左:右=2:1」で表示されます。

8行目の「box = BoxLayout(orientation=’vertical’)」は、右側のレイアウトです。オブジェクトが追加される方向は縦方向(下側)です。

9、10行目は、テスト用に左側、右側それぞれにボタンを追加しています。

11、12行目は、左側、右側の順でPlayWidgetにレイアウトを追加しています。

 

39行目~41行目は、プレイ画面を表示する関数です。

40行目の「self.clear_widgets()」は、RootWidgetに追加されていたオブジェクトを全て削除しています。

41行目の「self.add_widget(PlayWidget())」は、RootWidgetにPlayWidgetを追加しています。

 

 

動作確認

正しく表示されるか確認するため、動作確認を行います。コマンドプロンプトを開き、othello_beta.pyを保存したフォルダまで移動します。

移動したら、以下のコマンドプロンプトを入力し、othello_beta.pyを実行します。

python othello_beta.py

 

以下のように、左:右=2:1で表示されたら、完了です。

プレイウィジェットの仮レイアウト

 

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

 

 

スポンサードサーチ


FieldWidget

FieldWidgetは、盤面を表現するクラスです。

FieldWidgetには、盤面を表示する関数とオセロを進める関数が必要です。2つの関数は、それぞれ長いため分けて解説します。

 

まず、盤面を表示する関数のソースコードです。othello_beta.pyに以下のコードを追記します。

# 上記省略

class FieldWidget(Widget):
    def __init__(self, **kwargs):
        super(FieldWidget, self).__init__(**kwargs)
        # othelllo : 16.[48, 5D, 3F] / 10.[72, 93, 63] / %.[0.281, 0.363, 0.246]
        self.size_hint = [None, None]
    self.size = [800, 800]
        self.bind(pos=self.update)
        self.update()
    def update(self, *args):
        self.canvas.clear()
        self.canvas.add(Color(rgb=[0.281, 0.363, 0.246]))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        self.canvas.add(Color(rgb=[0, 0, 0]))
        self.canvas.add(Line(rectangle=[self.x, self.y, self.width, self.height], width=5))
        for i in range(1, 8):
            row = (self.height / 8 * i) + self.y
            rowLine = Line(points=[self.x, row, self.width + self.x, row],
                           width=3, cap='none', joint='none', close=False)
            self.canvas.add(rowLine)
        for i in range(1, 8):
            col = (self.width / 8 * i) + self.x
            colLine = Line(points=[col, self.y, col, self.height + self.y],
                           width=3, cap='none', joint='none', close=False)
            self.canvas.add(colLine)
        self.drawfield()
    def drawfield(self):
        poscalc = lambda z, wh: (wh / 16) + (wh / 8) * z
        rad = self.width / 8 * 0.4
        for i in range(8):
            ypos = (self.height - poscalc(i, self.height)) + self.y
            for j in range(8):
                xpos = poscalc(j, self.width) + self.x
                num = othello_alpha.field[(i, j)]
                if(num == 0):
                    pass
                elif(num == 1):
                    self.canvas.add(Color(rgb=[0, 0, 0]))
                    self.canvas.add(Ellipse(pos=[xpos-rad, ypos-rad], size=[rad*2, rad*2]))
                elif(num == 2):
                    self.canvas.add(Color(rgb=[0.9, 0.9, 0.9]))
                    self.canvas.add(Ellipse(pos=[xpos-rad, ypos-rad], size=[rad*2, rad*2]))
                    pass
                else:
                    print("FieldWidget drawfield Error")
                    sys.exit()
    pass

class PlayWidget(BoxLayout):
    def __init__(self, **kwargs):
        super(PlayWidget, self).__init__(**kwargs)
        self.orientation = 'horizontal'
        anchor1 = AnchorLayout(anchor_x='center', anchor_y='center', size_hint_x=2)
        box = BoxLayout(orientation='vertical')
        anchor1.add_widget(FieldWidget())           # 盤面
        box.add_widget(Button(text='test2'))        # テスト用
        self.add_widget(anchor1)
        self.add_widget(box)
    pass

# 下記省略

 

コードの解説

3行目~48行目は、盤面を表すクラスです。

3行目の「class FieldWidget(Widget)」は、Widgetを継承してFieldWidgetを作成しています。

7行目の「self.size_hint = [None, None]」と8行目の「self.size = [800, 800]」は、このクラスのサイズを固定しています。絶対指定を使用するため、相対指定を行うsize_hintはNoneに設定しておきます。

9行目の「self.bind(pos=self.update)」と10行目の「self.update()」は、このクラスの表示位置が変更したときに、update関数が実行されるように設定しています。

11行目の「def update(self, *args)」は、表示位置が変更されたときに実行する関数です。

12行目の「self.canvas.clear()」は、既に表示されていたオブジェクトを全て削除しています。

13行目の「self.canvas.add(Color(rgb=[0.281, 0.363, 0.246]))」と14行目の「self.canvas.add(Rectangle(pos=self.pos, size=self.size))」は、縦横800の緑色の四角形をこのクラスに追加しています。

15行目の「self.canvas.add(Color(rgb=[0, 0, 0]))」は、この行以降は線を引くため、色を黒に設定しています。

16行目の「self.canvas.add(Line(省略))」は、13、14行目の四角形の周りに太さ5の線を引いています。

17行目~21行目は、計算をして行の線を引いています。

22行目~26行目は、計算をして列の線を引いています。

27行目の「self.drawfield()」は、後述する関数です。

 

28行目~47行目の「def drawfield(self)」は、石を表示する関数です。

29行目の「poscalc = lambda z, wh: (wh / 16) + (wh / 8) * z」は無名関数であり、石をマスの中心に表示するための関数です。

30行目の「rad = self.width / 8 * 0.4」は、石の半径を求めています。

31行目~47行目は、othello_alpha.pyのグローバル変数であるfieldから、1つひとつ数字を取り出して、その数字に合う石を表示しています。

 

55行目の「anchor1.add_widget(FieldWidget())」は、PlayWidgetの左側に盤面を追加しています。

 

 

動作確認

正しく盤面が表示されているか、動作確認を行います。コマンドプロンプトを開き、othello_beta.pyを保存したフォルダまで移動します。

移動したら、以下のコマンド入力し、実行します。

python othello_beta.py

 

生成されたウィンドウを最大画面にして、以下のように表示されていたら完了です。

フィールドウィジェットの実行画面

 

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

 

 

スポンサードサーチ


FieldWidget

FieldWidgetは、盤面を表現するクラスです。

FieldWidgetには、盤面を表示する関数とオセロを進める関数が必要です。2つの関数は、それぞれ長いため分けて解説します。

 

ここでは、後者の「オセロを進める関数」を作成します。

オセロを進める関数のソースコードです。othello_beta.pyに以下のコードを追記します。

# 上記省略

class FieldWidget(Widget):
    def __init__(self, **kwargs):
        # 省略
    def update(self, *args):
        # 省略
    def drawfield(self):
        # 省略
    def on_touch_down(self, touch):
        xpos = (touch.x - self.x) / (self.width / 8)
        ypos = 8 - (touch.y - self.y) / (self.height / 8)
        if(othello_alpha.Turn == othello_alpha.playerStone):
            if(len(player.getPossibleLocation()) == 0):
                othello_alpha.Turn = othello_alpha.computerStone
                pass
            else:
                if(0 <= int(xpos) and int(xpos) < 8 and 0 <= int(ypos) and int(ypos) < 8):
                    mypos = (int(ypos), int(xpos))
                    if(player.confPos(mypos)):
                        player.updateField(mypos)
                        othello_alpha.Turn = othello_alpha.computerStone
                        self.drawfield()
            pass
        elif(othello_alpha.Turn == othello_alpha.computerStone):
            mypos = comp.electRandom()
            if(mypos == None):
                othello_alpha.Turn = othello_alpha.playerStone
            else:
                comp.updateField(mypos)
                othello_alpha.Turn = othello_alpha.playerStone
                self.drawfield()
            pass
        else:
            pass
    pass

class PlayWidget(BoxLayout):
    # 省略

class BtnBlack(Button):
    def __init__(self, **kwargs):
        # 省略
    def update(self, *args):
        # 省略
    def on_press(self):
        self.setStone()
        app = App.get_running_app()  # 変更部
        app.root.gotoPlayDisplay()    # 変更部
    def setStone(self):
        # 省略
    pass

class BtnWhite(Button):
    def __init__(self, **kwargs):
        # 省略
    def update(self, *args):
        # 省略
    def on_press(self):
        self.setStone()
        app = App.get_running_app()  # 変更部
        app.root.gotoPlayDisplay()    # 変更部
    def setStone(self):
        # 省略
    pass


# 中略


class OthelloApp(App):
    def build(self):
        root = RootWidget()
        root.gotoSelectTurn()  # 変更部
        return root

if __name__ == '__main__':
    OthelloApp().run()

 

コードの解説

10行目~35行目の「def on_touch_down(self, touch)」は、オセロを進めるための関数です。さらに、「on_touch_down」はWidgetに準備されている関数で、クリックされたときに実行されます。引数のtouchには、クリックされた座標が代入されます。

11、12行目の「xpos」「ypos」は、与えられた座標から0~7の数字に変換しています。

 

13行目~24行目は、プレイヤーのターンであるときの処理です。

14行目~16行目は、passであるかどうかを確認しています。

18行目の「if文」は、confPosに代入する座標がfieldに存在しない数字であるとエラーを発生させるため、それを回避するためのものです。

20行目~23行目は、盤面を更新し、石を再表示しています。

 

25行目~33行目は、コンピュータのターンであるときの処理です。othello_alpha.pyのmain文に書いたコードをほぼ同じであるため説明は省略します。

 

48、49行目は、先手後手選択画面からプレイ画面に移動するためのものです。BtnWhiteにも同じコードを記述しています。

 

 

動作確認

正しくオセロを行えるか、動作確認を行います。コマンドプロンプトを開きothello_beta.pyを保存したフォルダまで移動します。

移動したら、以下のコマンドを入力し、アプリケーションを実行します。

python othello_beta.py

 

先手後手選択画面において、手番を選びます。その後、プレイ画面に移動したらオセロを行います。相手のターンであるときは、画面をクリックするとプレイヤーに手番が回ってきます。

 

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

 

 

スポンサードサーチ


まとめ

今回は、プレイ画面の盤面と、オセロを進める関数を作成しました。

盤面の描画では、いろいろと計算して線を引きました。きれいに描画するためには、計算は欠かせません。

オセロは画面をクリックする毎に盤面が変わるようにしました。

以下に本記事で使用したコードを示します。

 

しかし、盤面だけであると途中でプレイヤーの番かコンピュータの番か分からなくなります。また、どちらの石の方が多いか知りたいこともあると思います。

次回Part9は、プレイ画面の右側に得点板と手番とタイトルに戻るボタンを作成します。

 

 

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


スポンサードサーチ