「迷路」を自動生成するプログラム/学習用コード

最終的に作りたい迷路は。

ずっと家に引きこもっていると・・・
たまには外に出て体を動かしたりした方がよいのかな?
なんて考えたりする。
ちょっとした罪悪感。

でも、外出はおっくうだ。
だから、引きこもっている。

そこで考えた。

3D迷路を作って、家に居ながらにして「散歩」気分を味わえないだろうか。

それで、この罪悪感がなくなるのかは疑問だけれど、ともかくきっかけはそんな感じだった。

まだ、立体的に・・・という部分については、しっかりとした方向性も見えていないんだけれど、まずは迷路を自動生成するところまで完成させてみよう。

今回のやり方は、穴掘り法というものであるらしい。

細かなところでボクのとは異なる点もあるが、たぶん問題はないだろう。
僕は迷路専門家じゃないから、わからんけれど。

今回も動画で解説。

YouTubeをはじめてみて思う。

こういう系の解説を文字で書くのは、結構骨が折れる。

そこへ行くと動画はいくぶん楽だ。

今回も解説は動画で行こうと思います。

タイムアッタク編はコチラ(↓)
本気で作る迷路。

https://youtu.be/0qduvs-UdQE

解説編はコチラ(↓)
穴掘り法で書いたコードの解説
https://youtu.be/iEOelFSQqlc

サンプルコードはコチラ(↓)

####################   MAZE   ####################

import tkinter as tk
import random
from tkinter import messagebox

SIZE = 10       # 1マスの大きさ(ピクセル)
COLUMN = 101    # 列数(奇数を推奨)
ROW = 51        # 行数(奇数を推奨)
departure = [[1, 1]]    # 迷路作りの起点

playerX = 1, playerY = 1, beforeX = 1, beforeY = 1

# リスト「maze」作成
# 二次元配列で「壁:WALL」「フィールド:FIELD」「通路:PATH」
maze = []
for y in range(ROW) :
    sub = []
    for x in range(COLUMN) :
        if y==0 or y==ROW-1 or x==0 or x==COLUMN-1 :
            sub.append("WALL")
        else :
            sub.append("FIELD")
    maze.append(sub)

# 穴掘り関数(ランダムに通路を掘り進めて"PATH"に書き換える)
def dig(x, y) :
    maze[y][x] = "PATH"     # 引数で指定したポイントを書き換え
    # 掘り進めることが可能な方向をチェック
    canMove = []
    if maze[y-1][x]=="FIELD" and maze[y-2][x]=="FIELD" :
        canMove.append("UP")
    if maze[y+1][x]=="FIELD" and maze[y+2][x]=="FIELD" :
        canMove.append("DOWN")
    if maze[y][x-1]=="FIELD" and maze[y][x-2]=="FIELD" :
        canMove.append("LEFT")
    if maze[y][x+1]=="FIELD" and maze[y][x+2]=="FIELD" :
        canMove.append("RIGHT")
    if len(canMove)==0 :    # 可能な方向がなければ関数を抜ける
        return
    # ランダムに方向を決めて掘り進む
    direction = random.choice(canMove)
    if direction=="UP" :
        maze[y-1][x]="PATH"
        maze[y-2][x]="PATH"
        y -= 2
    if direction=="DOWN" :
        maze[y+1][x]="PATH"
        maze[y+2][x]="PATH"
        y += 2
    if direction=="LEFT" :
        maze[y][x-1]="PATH"
        maze[y][x-2]="PATH"
        x -= 2
    if direction=="RIGHT" :
        maze[y][x+1]="PATH"
        maze[y][x+2]="PATH"
        x += 2
    departure.append([x, y])    # 進んだ位置を穴掘りの起点に追加
    dig(x, y)   # 再帰(新たな地点から再び穴掘り)

# ゲームプレイ的な・・・
# イベント関数
def keyPress(event) :
    global playerX,playerY,beforeX,beforeY
    if event.keysym=="Up" and maze[playerY-1][playerX]=="PATH" :
        beforeX = playerX
        beforeY = playerY
        playerY -=1
    if event.keysym=="Down" and maze[playerY+1][playerX]=="PATH" :
        beforeX = playerX
        beforeY = playerY
        playerY +=1
    if event.keysym=="Left" and maze[playerY][playerX-1]=="PATH" :
        beforeX = playerX
        beforeY = playerY
        playerX -=1
    if event.keysym=="Right" and maze[playerY][playerX+1]=="PATH" :
        beforeX = playerX
        beforeY = playerY
        playerX +=1
# プレイヤー移動とゴール
def move() :
    can.create_oval(beforeX*SIZE+1, beforeY*SIZE+1, (beforeX+1)*SIZE-1, (beforeY+1)*SIZE-1, fill="white", width=0)
    can.create_oval(playerX*SIZE+2, playerY*SIZE+2, (playerX+1)*SIZE-2, (playerY+1)*SIZE-2, fill="blue")
    if playerX==COLUMN-2 and playerY==ROW-2 :
        messagebox.showinfo("Information", "GOAL!!!!")
        exit()
    can.after(100, move)    # 再帰
    
# ゲームウインドウ
win = tk.Tk()
can = tk.Canvas(win, width=COLUMN*SIZE, height=ROW*SIZE)
can.pack()

while len(departure)!=0 :   # 新たな起点を呼び出す
    start = departure.pop(0)
    x = start[0]
    y = start[1]
    dig(x, y)

for y in range(ROW):        # 迷路を描画する
    for x in range(COLUMN):
        if maze[y][x]=="PATH" :
            color = "white"
        else :
            color = "black"
        can.create_rectangle(x*SIZE, y*SIZE, (x+1)*SIZE, (y+1)*SIZE, fill=color, width=0)

can.create_rectangle((COLUMN-2)*SIZE, (ROW-2)*SIZE, (COLUMN-1)*SIZE, (ROW-1)*SIZE ,fill="red")  # ゴール位置

move()      # プレイヤー移動関数呼び出し

win.bind("<Any-KeyPress>", keyPress)    # キープレスをバインド

win.mainloop()      # ウインドウループ