【Kivy】Pythonで迷路開発Part1

【Kivy】Pythonで迷路開発Part1を表すサムネイル

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

今回は、迷路を作成します。まずは、迷路の盤面を作成するプログラムを実装し、そのごKivyを使ってアプリケーションとして遊べるようにします。

 

誤字脱字や分からない点がございましたら、ご連絡お願いいたします。

 

 

スポンサードサーチ


【Kivy】Pythonで迷路開発Part1

Pythonで迷路開発Part1を表す画像

今回のシリーズは、2Dの迷路を開発することを目的としています。(プレイヤーを操作してスタートからゴールまでの時間を競います。)

 

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

1.迷路の盤面を生成するプログラムの開発

2.Kivyで迷路アプリケーション開発

 

迷路の盤面を生成するプログラムでは、手書きで迷路を作るような処理を、プログラムに直していきます。

Kivyでの開発は、プレイヤーと壁との当たり判定に気を付けて開発を行います。

 

 

今回は

今回は、1つ目の迷路の盤面を作成するプログラムを作成します。

 

 

盤面生成1

盤面生成1を表す画僧

盤面生成の方法は、初期値全て壁の状態にスタート地点とゴール地点を決めて、スタートからゴールまでの道を作る方法です。

 

まずは、4×4(実質2×2)で盤面を生成します。labyrinth01.pyを作成し、以下のコードを書きこみます。

import numpy as np
import random


def makeLabyrinth(field):
  rowMax, colMax = np.shape(field)
    rowStart, colStart = (1, 1)
    offset = [(0, -1), (-1, 0), (0, 1), (1, 0)]
    possible = []
    for pos in offset:
        row = rowStart + pos[0]
        col = colStart + pos[1]
        if(0 < row and row < rowMax-1 and 0 < col and col < colMax-1):
            possible.append((row, col))
    r = random.randrange(len(possible))
    field[possible[r]] = 1
    print(field)
    pass


field = np.array([[0, 0, 0, 0],
                  [0, 1, 0, 0],
                  [0, 0, 2, 0],
                  [0, 0, 0, 0]])

if __name__ == '__main__':
    makeLabyrinth(field)

 

 

コードの解説

1、2行目の「import numpy as np」と「import random」は、盤面を生成する上で必要なライブラリをインポートしています。

 

5行目~17行目は、迷路の盤面を生成する関数です。

6行目の「rowMax, colMax = np.shape(field)」は、縦方向の最大と横方向の最大を取得しています。

7行目の「rowStart, colStart = (1, 1)」は、迷路のスタート地点を指定しています。このスタート地点は「【Kivy】Pythonで迷路開発」の最後のPartまでずっと同じです。

8行目の「offset = [(0, -1), (-1, 0), (0, 1), (1, 0)]」は、指定した座標の上下左右を指定する際に使用します。

9行目の「possible = []」は、道を作ることができる座標を保存するためのリストです。

13行目の「if文」は、道を作ることができるかどうかを判定するためのものです。盤面の1周りは壁とするため、「0より大きく、最大値未満」という条件になります。

15行目の「r = random.randrange(len(possible))」は、0からpossibleの大きさ未満の数字をランダムに選出します。

16行目の「field[possible[r]] = 1」は、14行目の選ばれた数字番目に格納されている座標に1代入して道を作ります。

0:壁 / 1:道 / 2:ゴール地点

 

 

動作確認

正しく迷路が生成されるか動作確認を行います。コマンドプロンプトを開き、ファイルを保存したフォルダまで移動します。移動したら以下のコマンドを入力し、実行します。

python labyrinth01.py

 

以下のどちらかの迷路が生成されていれば、正しく動作しています。

# パターン1
[[0 0 0 0]
 [0 1 0 0]
 [0 1 2 0]
 [0 0 0 0]]

# パターン2
[[0 0 0 0]
 [0 1 1 0]
 [0 0 2 0]
 [0 0 0 0]]

 

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

 

 

スポンサードサーチ


盤面生成2

盤面生成2を表す画像

labyrinth01.pyでは、1つの座標を選び出せれば盤面が完成しました。次は、5×5(実質3×3)の盤面を生成します。

5×5の盤面生成では、選び出す座標の条件ことなるため、その点を詳しく解説します。

 

labyrinth02.pyを作成し、以下のコードを書きこみます。

import numpy as np
import random


def nextCrossBlocks(field, pos, rowNext, colNext):
    offset = [(0, -1), (-1, 0), (0, 1), (1, 0)]
    pos = (pos[0] * (-1), pos[1] * (-1))
    offset.remove(pos)
    rowMax, colMax = np.shape(field)
    blocks = []
    for pos in offset:
        row = rowNext + pos[0]
        col = colNext + pos[1]
        if(row != 0 and row != rowMax-1 and col != 0 and col != colMax):
            blocks.append(field[(row, col)])
    return blocks


def makeLabyrinth(field):
    rowMax, colMax = np.shape(field)
    posGoal = list(zip(*np.where(field == 2)))[0]
    rowCurrent, colCurrent = (1, 1)
    offset = [(0, -1), (-1, 0), (0, 1), (1, 0)]
    count = 0
    while(True):
        possible = []
        for pos in offset:
            row = rowCurrent + pos[0]
            col = colCurrent + pos[1]
            block = field[(row, col)]
            if(0 < row and row < rowMax-1 and 0 < col and col < colMax-1 and block != 1):
                blocks = nextCrossBlocks(field, pos, row, col)
                if(1 not in blocks):
                    possible.append((row, col))
        if(posGoal in possible):
            break
        r = random.randrange(len(possible))
        rowCurrent, colCurrent = possible[r]
        field[(rowCurrent, colCurrent)] = 1
        count = count + 1
    print(field)
    pass


field = np.array([[0, 0, 0, 0, 0],
                  [0, 1, 0, 0, 0],
                  [0, 0, 0, 0, 0],
                  [0, 0, 0, 2, 0],
                  [0, 0, 0, 0, 0]])


if __name__ == '__main__':
    makeLabyrinth(field)

 

 

コードの解説

1、2行目の「import numpy as np」と「import random」は、盤面生成に必要なライブラリをインポートしています。

 

5行目~16行目の「nextCrossBlocks」は、次の道にする予定の座標の上下左右の座標の状態を調べる関数です。「field」は盤面のことです。「pos」は、現在の座標から見た次の座標の方向です(offset)。「rowNext」と「colNext」は、次の座標のrowとcolです。

この関数が必要な理由を、以下の図を使って説明します。

nextCrossBlocksが必要な理由を説明するための参考画像

labyrinth01.pyの条件で盤面を生成したら、道の隣に道を生成することができるため、道が大きくなり迷路が台無しになります。ゆえに、この関数が必要になります。

今から道を生成しようとしている座標の周りの座標に、道がないか調べるためにこの関数を使用します。

7行目の「pos = (pos[0] * (-1), pos[1] * (-1))」は、現在の座標から見た次の座標の方向を逆にしています。

現在の座標から見た次の座標の方向と次の座標から見た現在の座標の方向の違い

この関数では、次の座標の周りの座標を調べます。しかし、現在の座標は調べなくてよいため現在の座標から見た次の座標の方向の逆を作っています。

8行目の「offset.remove(pos)」は、offsetから必要ない方向を削除しています。

10行目の「blocks = []」は、周りの座標の状態を格納するためのリストです。

 

19行目~41行目の「makeLabyrinth」は、実際に迷路の盤面を生成する関数です。

21行目の「posGoal = list(zip(*np.where(field == 2)))[0]」は、ゴールの座標をposGoalに代入しています。

22行目の「rowCurrent, colCurrent = (1, 1)」は、スタートの座標を指定しています。

30、31行目の「if文」は、道を作ることができるかどうかを判定するためのものです。条件としては、「0より大きく、最大値未満+blockが道以外」です。

33行目の「if(posGoal in possible):」は、道を作ることができる座標にゴールの座標が含まれているかを判定するためのものです。つまりwhile文を終了するためのものです。possibleにposGoalが含まれているということは、ゴールの座標の隣の座標まで道が作られたことを示します。

36行目の「rowCurrent, colCurrent = possible[r]」は、指定された座標を現在の座標として更新しています。

 

 

動作確認

迷路の盤面が正しく生成されるか動作確認を行います。コマンドプロンプトを開き、ファイルを保存したフォルダまで移動します。移動したら以下のコマンドを入力し、実行します。

python labyrinth02.py

 

以下のように出力されれば、正しく動作しています。

[[0 0 0 0 0]
 [0 1 1 1 0
 [0 0 0 1 0]
 [0 0 0 2 0]
 [0 0 0 0 0]]

 

ランダムで盤面を生成しているため、紹介した盤面以外に5つのパターンの出力があります。

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

 

 

まとめ

まとめの画像

今回は、迷路開発の中でも迷路の盤面を生成するプログラムを作成しました。しかし、まだまだ盤面生成のプログラムは完成していません。

 

以下に今回作成したコードを示します。

 

次回は、今回作成した盤面生成プログラムにどんな問題があるのか提起し、その問題を解決する盤面生成のプログラムを紹介します。

 

 

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


スポンサードサーチ