2021年1月3日日曜日

強化学習で遊べる迷路を自動作成

先日「つくりながら学ぶ! 深層強化学習」を読んでいました。
ハードルが高くてよく分かりません。

第2章の迷路課題のところで、3✕3の固定された迷路を使ってデモがありましたが、こんなのを見ていると5✕5とか10✕10とか色々な迷路で試してみたいと思うのは私だけでしょうか?

ということで、強化学習で遊べる迷路を自動作成するプログラムを作りました。
迷路を作るためのアルゴリズムは幾つかあるようですが、今から勉強して1から作るのは骨が折れるので、子供のために迷路を自動作成するを使わせてもらいました。
自分で作ったのは出来た迷路を描画するルーチンのみです。(*^^*)
# 使用するパッケージの宣言
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
作成する迷路の1辺のブロック数を入力します。
#作成する迷路の大きさ
MAZE_SIZE = int(input('maze size->'))  # 迷路の1辺の長さ
MAZE_CELL = MAZE_SIZE * MAZE_SIZE  # 迷路のセル数
def direction():
  '''
  進行方向を選択。
  戻り値は順に「現在地の破壊壁方向」「進行部屋の壁破壊方向」「進行部屋の部屋番号」
  '''
  rand = np.random.randint(0, high=4)
  if rand == 0: return 0, 2, -1 * MAZE_SIZE  # 上
  if rand == 1: return 1, -1, 1  # 右
  if rand == 2: return 2, 0, MAZE_SIZE  # 下
  return -1, 1, -1  # 左

def check_cell(cells, c_cell, d_cell, dire):
  ''' 進めるかチェック '''
  # 進んだ部屋が迷路外かどうか
  # 上下へのはみ出し
  if d_cell < 0 or MAZE_CELL <= d_cell: return False
  # 部屋番号+1が迷路サイズで割り切れる場合、右端の部屋。右には進めない。
  if (c_cell+1) % MAZE_SIZE == 0 and dire == 1: return False
  # 部屋番号+1が迷路サイズで割り、余り1の場合、左端の部屋。左には進めない。
  if (c_cell+1) % MAZE_SIZE == 1 and dire == -1: return False
  # 現在地部屋番号 = 進んだ部屋番号。同じ部屋の場合は処理しない
  if cells[c_cell] == cells[d_cell]: return False
  # それ以外は進める
  return True

def choice_cell_no(cells, c_cell, d_cell):
  ''' 小さい方の部屋番号を取得 '''
  if cells[c_cell] < cells[d_cell]:
    return cells[c_cell], cells[d_cell]
  return cells[d_cell], cells[c_cell]

def create_maze():
  ''' 迷路を生成 '''
  # 初期状態を生成
  # [上, 右, 下, 左] = 1:壁 / 0:通路
  lst_maze = np.ones([MAZE_CELL, 4])
  # 部屋のナンバリング、最終的に全部0にする。
  lst_cells = np.array([i for i in range(0, MAZE_CELL)])

  while True:
    # すべての部屋が番号0の時は処理終了
    if np.sum(lst_cells) == 0: break

    # 壁を破る元のセルを選択(現在地)
    int_choice_cell = np.random.randint(0, high=MAZE_CELL)
    # 既にスタート地点とつながっている(0)時は処理しない。
    if lst_cells[int_choice_cell] == 0: continue

    # 進行方向を選択
    int_now_wall, int_direction_wall, int_direction = direction()
    # 進んだときのセル
    int_direction_cell = int_choice_cell + int_direction

    # 値のチェック
    if not check_cell(lst_cells, int_choice_cell, int_direction_cell, int_direction):
      continue

    # 部屋番号を確保
    int_min_value, int_max_value = choice_cell_no(lst_cells, int_choice_cell, int_direction_cell)

    # 小さい方の部屋番号に統一する
    lst_cells[lst_cells == int_max_value] = int_min_value

    # 壁も更新する
    lst_maze[int_choice_cell][int_now_wall] = 0
    lst_maze[int_direction_cell][int_direction_wall] = 0

  return lst_maze 

# 迷路を生成
MAZE_OUT = create_maze()

# 図を描く大きさと、図の変数名を宣言
fig = plt.figure(figsize=(MAZE_SIZE, MAZE_SIZE))
ax = plt.gca()

# 状態を示す文字S0~S※を描く
# 赤い壁を描く
s_num =0
for gyo in range(MAZE_SIZE):
#for gyo in range(2):
  for retu in range(MAZE_SIZE):
  #for retu in range(1):
   
    plt.text(retu+0.5, MAZE_SIZE-gyo-0.5, 'S'+str(s_num), size=10, ha='center')

    if MAZE_OUT[s_num,0] == 1:
      #print
      #print(gyo,retu,MAZE_OUT[s_num])
      plt.plot([retu, retu+1], [MAZE_SIZE-gyo, MAZE_SIZE-gyo], color='red', linewidth=2)

    if MAZE_OUT[s_num,1] == 1:
      #print
      #print(gyo,retu,MAZE_OUT[s_num])
      plt.plot([retu+1, retu+1], [MAZE_SIZE-gyo-1, MAZE_SIZE-gyo], color='red', linewidth=2)

    if MAZE_OUT[s_num,2] == 1:
      #print
      #print(gyo,retu,MAZE_OUT[s_num])
      plt.plot([retu, retu+1], [MAZE_SIZE-gyo-1, MAZE_SIZE-gyo-1], color='red', linewidth=2)

    if MAZE_OUT[s_num,3] == 1:
      #print
      #print(gyo,retu,MAZE_OUT[s_num])
      plt.plot([retu, retu], [MAZE_SIZE-gyo-1, MAZE_SIZE-gyo], color='red', linewidth=2)

    s_num += 1

plt.plot([0, MAZE_SIZE], [0, 0], color='black', linewidth=3)
plt.plot([MAZE_SIZE, MAZE_SIZE], [0, MAZE_SIZE], color='black', linewidth=3)
plt.plot([0, MAZE_SIZE], [MAZE_SIZE, MAZE_SIZE], color='black', linewidth=3)
plt.plot([0, 0], [MAZE_SIZE, 0], color='black', linewidth=3)

plt.text(0.5, MAZE_SIZE-0.7, 'START', size=8,ha='center')
plt.text(MAZE_SIZE-0.5, 0.2, 'GOAL', size=8,ha='center')

# 描画範囲の設定と目盛りを消す設定
ax.set_xlim(0, MAZE_SIZE)
ax.set_ylim(0, MAZE_SIZE)
plt.tick_params(axis='both', which='both', bottom='off', top='off',labelbottom='off', right='off', left='off', labelleft='off')

# 現在地S0に緑丸を描画する
line, = ax.plot([0.5], [MAZE_SIZE-0.5], marker="o", color='g', markersize=30)


【自動作成された迷路の例】
3✕3の迷路

5✕5の迷路
10✕10の迷路

たかが迷路を表示するだけのサブルーチンですが、昔のように頭が働かないので、試行錯誤の果です。
どこか間違っているかも知れませんが取り敢えず動いているので良しとします。m(__)m

0 件のコメント:

コメントを投稿