【はじめてのPython】オセロを作る③~記載を簡素にする~

ITスキル

こんにちは。ヒトツメです。
前回は、実際に石を置いた場合の処理についてみていきました。
ただ、前回の内容だけだと、エラーが起きてしまいます。また、for文やif文が長く、入り組んでおり、分かりにくくなってしまっています。
そこで、記載の内容を簡素にしながら、エラーを除去していき、より分かりやすい記載に変えていきたいと思います。

スポンサーリンク

drとdcの処理

前回の記載の内容は、次のようになっていました。
ただこれだと、最初にfor文が続いており、一見して分かりにくいです。

game[3][5] = 1
for dc in range(-1, 2, 1):
    for dr in range(-1, 2, 1):
        if game[3 + dc][5 + dr] == -1:
            #一8向を見ていき、相手の石があればひっくり返すかどうかの判定に移る
            for length in range(2, 8, 1):
                #石を置いた場所を基準としてさらにその先を見ていく
                if game[3 + dc * length][5 + dr * length] == 1:
                    #自分の石があれば処理をする
                    for iter in range(length):
                        #自分の石がある場所まで一つ一つ処理をする
                        game[3 + dc * iter][5 + dr * iter] = 1

そこで、2行目から3行目を、次のように書き換えます。

for iter in range(9):
    dc = int(iter / 3) - 1
    dr = iter % 3 - 1

この方が若干長いですが、for文が一つになりました。これにより、一つの繰り返し処理に対してfor文を一つにすることができ、何のためにfor文を入れているかが明確になりました。
ちなみにint関数は整数部分のみを取り出す関数、「%」は割り算の余り部分のみを返すための演算記号です。

端の場合の処理

また、前回までの内容だと、端の場合に処理ができなくなってしまいます。もともとgameという配列は、第一次元も第二次元も長さが8です。なので、例えばgame[8][8]やgame[9][9]といった記載はできません。
試しにやってみると次のようにエラーが出てしまいます。

そこで、後半の記載を簡潔にしつつ、はみ出す場合には処理をしないように、次のような記載に変更します。

    length = 0
    while True:
        length += 1
        if 3 + dr * length < 0 or 3 + dr * length > 7 \
        or 5 + dc * length < 0 or 5 + dc * length > 7:
            break
        buf = game[3 + dr * length][5 + dc * length ]
        if length == 1 and buf != -1:
            break
        if length > 1 and buf == 0:
            break
        if length > 1 and buf == 1:
            for iter in range(length):
                game[3 + dr * iter][5 + dc * iter] = 1

while文はfor文と同じように、繰り返し処理に関する構文ですが、while以下の条件式がTrueの場合に永遠に繰り返します。この処理と、4行目・5行目から、端に行き着くまでずっと処理を繰り返すということになります。「length += 1」の部分は、Python独自の書き方で、左辺の変数を1足した結果に変更するという処理で、「length = length + 1」という処理と同じです。
while文は、breakという処理により、途中で離脱することができるので、このような構文により、端を超えるとwhile文を終わり、次の方向を見ていくという処理になります。

続く二つ目以降のif文は、それぞれ、下記のような処理をしています。

  • 二つ目:隣の石が「-1」でなければその方向は離脱
  • 三つ目:隣のさらに向こうが「0」ならばその方向は離脱
  • 四つ目:隣のさらに向こうに「1」があれば、その場所までの長さ分すべて「1」にする

関数にする

以上をまとめると、石を置く操作は次のようになります。

for d_iter in range(9):
    dc = int(d_iter / 3) - 1
    dr = d_iter % 3 - 1
    length = 0
    while True:
        length += 1
        if 3 + dr * length < 0 or 3 + dr * length > 7 \
        or 5 + dc * length < 0 or 5 + dc * length > 7:
            break
        buf = game[3 + dr * length][5 + dc * length ]
        if length == 1 and buf != -1:
            break
        if length > 1 and buf == 0:
            break
        if length > 1 and buf == 1:
            for iter in range(length):
                game[3 + dr * iter][5 + dc * iter] = 1

全体的に少し長くなってしまいましたが、for文やif文が大量にネストされておらず、処理が分かりやすくなりました。また、端を超えた場合の処理についてもエラーが起きないようにできています。
しかしながら、このような処理を、石を置く度、毎回記載するのは大変です。なので、これを関数に入れ込み、1行で呼び出せるようにすると、便利です。
そこで、下記のような記載をすると、設置する場所を指定してあげるだけで、game配列が変更されます。

def nextgame(game, row, clm, Player):
    if game[row][clm] == 0:
        for d_iter in range(9):
            dc = int(d_iter / 3) - 1
            dr = d_iter % 3 - 1
            length = 0
            while True:
                length += 1
                if row + dr * length < 0 or row + dr * length > 7 \
                or clm + dc * length < 0 or clm + dc * length > 7:
                    break
                buf = game[row + dr * length][clm + dc * length]
                if length == 1 and buf != -1 * Player:
                    break
                if length > 1 and buf == 0:
                    break
                if length > 1 and buf == Player:
                    for iter in range(length):
                        game[row + dr * iter][clm + dc * iter] = Player

2022年5月29日修正:配置しようとする場所に石がある場合でに置けないようにする処理を追記しました。また、arrayの最大値が「8」と間違っていたので、「7」に修正しました。

「def」というのは、続く構文は関数になるということを示しており、その後ろで関数名と引数を指定します。記載している引数は、それぞれ次のようになっています。

  • game:game配列をそのまま入れ込む
  • rowとclm:石を設置する場所を指定する
  • Player:1か-1を入れて、置く石の色を指定する

このように関数を作ることで、次のように、石を置く動作を1行で処理しつつ、交互に石を置くことができます。

コメント

タイトルとURLをコピーしました