1
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Python3 を子供に教えるためにオセロを作ってみた(4)

Last updated at Posted at 2020-05-25

オセロゲームのプログラムを解説していきましょう

関数について理解しよう

 プログラムでは関数はとても大切な要素です。関数というのは一連の動作を機能としてくるんだものを指します。

 例えば、「食べる」という機能には、以下のような動作が含まれています。

  1. 口に入れる
  2. 咀嚼する
  3. 味わう
  4. 飲み込む
  5. (例外的に危険を感じたら吐き出すとか)

この5ステップのロジックを、何か食べるたびに書いているとコードが冗長になりますし、もしかすると「食べる」機能の中に「香りも確かめるべき」みたいな話しが出ると、全ての食べるロジックを修正して回らないとならないから大変です。ですので何回か出現するような同じコードは関数として定義し、食べる内容が変わる部分は変数として渡してあげるようにします。

 Python で関数を定義するには、以下のように記述します。パラメータとして渡す物が可変になっていれば、どんなものでも食べることができる「食べる」関数ができあがります。

def EatFood(food):

 カレーを食べさせたければ EatFood("カレー") と呼び出し、うどんを食べさせたければ EatFood("うどん") と呼び出します。括弧の中に食べさせたい値を渡すとそれを受け取った関数が機能を実行して戻ってきてくれるというわけです。この引き渡す値を引数などと呼びます。

 算数の世界で関数は sum(10,20,30) とやれば 10+20+30 を実行して合計値として 60 を返すものであったり、 average(70,70,100) とやったら 70、70、100 の平均値を求めてその結果80を返してくれるものだったりします。プログラムの世界では、関数は処理をまとめる一塊でもあり、何らかの値を渡したらそれを加工して返してくれたり、何らかの値を元に動作をした後返ってくるものを指します。

クラスについて理解しよう

 前回の(3)では変数について説明しました。そして今回の章では関数を説明しました。
次にクラスについて学んだところで、オセロゲームのソースについて解説していきたいと思います。プログラミングの世界ではクラスという言葉がやたらと目に付くはずです。クラスとは何でしょうか?

 先ほどの関数では EatFood() という関数について例示しました。これらは機能を1つにまとめた塊と話をしましたが、クラスはもっと大きなくくりでの塊と言っていいものです。

 例えば人間というクラスを作るとしましょう。

 人間クラスには以下のような要素が必要になります。

要素 種類 説明
性別 変数 性別に関する値を格納する変数
生年月日 変数 生年月日を格納する変数
身長 変数 身長を格納する変数
体重 変数 体重を格納する変数
話す 関数 話す行動をする関数
寝る 関数 寝る行動をする関数
起きる 関数 起きる行動をする関数

 実際に人間をクラス化したらこんなものではとてもとても足りませんが、このように人間を構成する要素を変数と関数の塊にしたものをクラスと呼びます。Python ぽく書くとこんな感じになります。

class Human:
    # Human クラスの変数
    def __init__(self):
        self.sex = None        # 性別変数
        self.birthday = None   # 生年月日
        self.tall = None       # 身長
        self.weight = None     # 体重
    # 話す
    def talk(self, quote):
        〜〜〜ロジック〜〜〜
    # 寝る
    def sleep(self):
        〜〜〜ロジック〜〜〜
    # 起きる
    def wakeup(self):
        〜〜〜ロジック〜〜〜

 この Human クラスはまだ実体がない人間を定義した設計図のようなものです。
 実際にプログラミングする場合には、この Humanクラスを使って、実体化しなければなりません。これをインスタンス化すると言います。
それでは、サザエさんと、マスオさんを実体化(インスタンス化)してみましょう。

サザエさん = Human()
サザエさん.sex = female
サザエさん.tall = 159
サザエさん.weight = 48
サザエさん.talk("んがぐぐっ")    # んがぐぐ としゃべる

マスオさん = Human()
マスオさん.sex = male
マスオさん.tall = 173
マスオさん.weight = 70
マスオさん.talk("えええ〜っ??")    # えええ〜っ?? としゃべる

このように同じ設計図で複数の実体を作ることで、より効率的にプログラミングをしていくことができるようになるわけです。クラス化するメリットは様々です。例えばオセロゲームではオセロゲームそのものをクラス化してしまいましたが、もっと綺麗に作れば、派生クラスというのを作ってグラフィカルなオセロクラスを作ることもできるようになるでしょう。オブジェクト指向と呼ばれるプログラミング言語には、様々なクラスのライブラリが用意されていたり、GitHub などですごいプログラマーが作ってくれたクラスが公開されていたりします。そうしたクラスライブラリを使うことで1から何でも作るのではなく、効率的にプログラムが作成できるようになっているのです。

オセロクラスについて解説していきましょう(1)

 先ほど説明したように、クラスは変数と関数の塊で作られた設計図です。オセロクラスはオセロゲームを作るためのクラスとして作成しましたが、どのような変数と関数があるのか解説していきたいと思います。

 まずは変数部です。クラスは class *className*: で宣言を開始します。
この宣言からインデントを下げた部分が全てクラスの中に含まれる要素となります。クラスが持つ変数は def __init__(self): という初期化関数の中で定義します。(違うやり方もありますが、いろいろなやり方を説明しても混乱するだけなので、今はクラス変数は初期化関数の中で定義と覚えてしまってかまいません ※多分)

class OthelloCls:
    def __init__(self):
        self.ot_bit = list()
        # 初期設定=盤面にオセロの駒を4つ置く
        for i in range(64):
            if i == 27 or i == 36:
                self.ot_bit.append('')
            elif i == 28 or i == 35:
                self.ot_bit.append('')
            else:
                self.ot_bit.append('')
        self.ot_offdef = ''                       # どっちのターンか
        self.ot_search = ''                       # その逆の手
        # 八方向の隣のコマ計算のためにディクショナリを使って
        # 計算テーブルを作っておく(方向がキー、計算値は Tuppleで定義)
        self.ot_direction = dict()                  # 八方向の検索テーブル
        self.ot_direction = { 'ul' : ( -1 , -1 ) ,  # 左斜め上
                              'up' : (  0 , -1 ) ,  # 真上
                              'ur' : ( +1 , -1 ) ,  # 右斜め上
                              'lt' : ( -1 ,  0 ) ,  # 左
                              'rt' : ( +1 ,  0 ) ,  # 右
                              'dl' : ( -1 , +1 ) ,  # 左斜め下
                              'dn' : (  0 , +1 ) ,  # 真下
                              'dr' : ( +1 , +1 ) }  # 右斜め下
        # ひっくり返せると判定した場合の終端位置
        self.ot_lastposX = 0                        # ひっくり返す終端X
        self.ot_lastposY = 0                        # ひっくり返す終端Y

早速出てくる「配列変数」

 さて、早速出てくる変数が配列です。python で配列は list と呼ばれるクラスを用いて定義します。ここではオセロの盤面である8x8の64個の配列を作成するために list() クラスを用いて変数を定義します。オセロの盤面を変数化する時に、二次元配列を使う場合がありますが、プログラミング初心者には二次元配列は分かりづらいかと思いますので、普通の配列を使うことにしました。

 list 変数の値を初期値でうめる場合に、append() 関数で追加していく方法もありますが、以下のような書き方で変数定義と同時に初期化することもできます。(ちょっと長いので省略しています)

ot_bit = [ '' , '' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' , \
'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' , \
'' , '' , '' ,'' ,'' ,'' ,'' ,'' ]

リスト以外の配列

 Python に独特な方言とでも言うか、変数で配列を表現する方法が3種類あります。書き換え可能な変数をミュータブル、書き換え不能な変数をイミュータブルとも言いますが、分かりづらいので以下の表では"書き換え"と記しています。

クラス 初期化の記述 書き換え 順序 値の重複
リスト list() [ 要素 , 要素 , 要素 , 要素 ] 順序
タプル tuple() ( 要素 , 要素 , 要素 , 要素 ) 不可 順序
セット set() { 要素 , 要素 , 要素 , 要素 } 順不動 不可

 リスト型とタプル型は書き換えができるかできないかだけが違いで、それ以外の利用方法はほぼ同じです。参照するだけの配列であれば、アクセス速度はタプルの方が速いのでタプルを利用することをお勧めします。

早速出てくる「回数が決まった繰り返し文」

 前述した繰り返し文がここで出てきます。def __init__(self): の中には変数の定義と、変数を初期化するプログラム(ロジック)が含まれています。今回は 64個と回数が決まった繰り返しになるので、64回繰り返しながら、初期状態のコマを置くために繰り返し回数が格納される変数 i が 27 または 36 であれば ○ を代入し、i が 28 または 35 であれば ● を代入し、それ以外には ・ を代入します。この配列の中には8x8 マスの要素が全て含まれており、白いコマが置かれてる部部には○が、黒いコマが置かれている部分には●が、何も置かれていないマスには ・ を代入するというルールで作っています。配列に値を追加するのは、list クラスでインスタンス化された ot_bit に対して、append() という関数を用いて値を追加していきます。配列に値を追加するのは append() という関数を用いると覚えてください。配列の値を参照するには ot_bit[index] で1つのマスを参照できます。値の更新も同様に ot_bit[index] = "●" というように添字を付けて代入することで更新が行えます。配列の一部を削除する関数は del() や pop() や remove() がありますが、ここら辺は実際に必要になったときに使い方を検索してみてください。 検索キーワードは python list 削除 などで簡単に検索することができると思います。

一般の変数

 ot_offdef は現在のターンのコマ(○または●)、ot_search は現在のターンではないコマ(○または●)が代入されています。
 ot_lastposXot_lastposY はひっくり返す最後の場所を記憶するための変数となっています。

ディクショナリ変数について

 さて今回はディクショナリ変数を説明して終わりにしたいと思います。ディクショナリ変数は、キー:値という配列を持つための構造体です。今回は敢えてディクショナリ変数にしなくても良かったのですが、勉強のためにディクショナリ変数を用いました。

 オセロでは、特定の(X,Y)の座標にコマを置いた場合に、8方向に自分の手を置いていける可能性があります。

        self.ot_direction = dict()                  # 八方向の検索テーブル
        self.ot_direction = { 'ul' : ( -1 , -1 ) ,  # 左斜め上
                              'up' : (  0 , -1 ) ,  # 真上
                              'ur' : ( +1 , -1 ) ,  # 右斜め上
                              'lt' : ( -1 ,  0 ) ,  # 左
                              'rt' : ( +1 ,  0 ) ,  # 右
                              'dl' : ( -1 , +1 ) ,  # 左斜め下
                              'dn' : (  0 , +1 ) ,  # 真下
                              'dr' : ( +1 , +1 ) }  # 右斜め下

 このテーブルはコマを置いた場所から、8方向に対してどう動くかを数値化したものです。以前の章で「プログラムは言葉で表現できる形に落とし込むこと」と書きましたが、正に「コマを置いた場合にどう動かすか」を言葉にすることが重要になります。下の図を見てください。1つのコマを置くと、人間なら「あ、左がひっくり返せるね」と分かりますが、プログラムは8方向に順に見ていくことしかできません。自分が置いた場所から斜め左上であれば、縦方向にマイナス1、横方向にマイナス1した場所を探します。

スクリーンショット 2020-05-26 0.16.00.png

 この図ではX=6、Y=5に置いていますので、以下のように探していくことになります。

  1. X=5、Y=4で、ひっくり返せるコマがあるかを探します。
  2. X=6、Y=4で、ひっくり返せるコマがあるかを探します。
  3. X=7、Y=4で、ひっくり返せるコマがあるかを探します。
  4. X=7、Y=5で、ひっくり返せるコマがあるかを探します。
  5. X=7、Y=6で、ひっくり返せるコマがあるかを探します。
  6. X=6、Y=6で、ひっくり返せるコマがあるかを探します。
  7. X=5、Y=6で、ひっくり返せるコマがあるかを探します。
  8. X=5、Y=5で、ひっくり返せるコマがあるかを探します。

 この自分の置いた座標から、どこに向かって探していくかをテーブルにしたものが、ot_direction というディクショナリ変数になります。
ul=UpLeft は ( -1 , -1 ) と、それぞれ縦方向と、横方向どっちに向かって探すかがタプル型で定義されています。

 以上が、オセロクラスでの変数に関する説明です。ディクショナリ変数は少し分かりづらいかもしれませんが、後ほどプログラムでどのように利用するのかを説明しますので、ここではキー:値という配列だとだけ覚えておいてもらえればOKです。

 次回に行く前に少し考える宿題です。「ひっくり返せるコマがあるか」はプログラムではどう言葉で表現したら良いでしょうか?例えば黒のターンの場合でX=6、Y=5にコマを置きました。プログラム的にはひっくり返せるかどうかをどう考えたら良いでしょうか?やり方は何通りもあるので実現できるならそれが正解ですよ。少し考えてみてくださいね!

 それでは、次回はオセロクラスの関数について、説明をしていきたいと思います。

c u

<<Python3 を子供に教えるためにオセロを作ってみた(3)
  Python3 を子供に教えるためにオセロを作ってみた(5)>>

1
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?