Technology Blog

技術ブログ

2014.04.14

迷路の自動生成アルゴリズム「穴掘り法」をCoffeeScriptとenchant.jsで実装してみた


前回のブログで、「enchant.jsでさくっとアルゴリズミックなゲームを作る時なんかに良さそうです。」と書いたCoffeeScriptですが、早速enchant.jsとの組み合わせで、代表的なアルゴリズムである「穴掘り法」(正確には穴掘り法に自分なりの解釈を加えたもの)を実装してみました。

分かったことですが、CoffeeScriptはやっぱりとってもコードが書きやすい。

ネスト(ループの入れ子)が多いところも書きやすい。

JavaScriptはカッコが多い言語なので、CoffeeScriptのようにカッコなしでかけるのは本当に気持ちがいいし、とても捗りますね。

やっぱり、こうしたものをコーディングするには丁度良さそうです。

最初に考えたこと

コーディングにかかる前に、迷路生成のアルゴリズムを実装する時にどのように組んでいったらいいか、ざっと考えてみました。

・最初
・・どれかひとつの座標をランダムで選ぶ(ただし、XYともに偶数偶数)
・・「基点の座標一覧」にそこを追加
・・掘る

・掘る
・・ランダムに1方向選び、2マス先の座標が掘れるかたしかめる
・・掘れれば、「基点の座標一覧」にそこの座標を追加し
・・1マス先と2マス先を掘る

・掘れなかったら、それ以外の方向の2マス先の座標が掘れるかたしかめる
・・掘れれば、「基点の座標一覧」にそこの座標を追加し
・・1マス先と2マス先を掘る

・それでも掘れなかったら、それら以外の方向の2マス先の座標が掘れるかたしかめる
・・掘れれば、「基点の座標一覧」にそこの座標を追加し
・・・・「基点の座標一覧」から現在の座標を削除し、1マス先と2マス先を掘る

・それでも掘れなかったら
・・「基点の座標一覧」から現在の座標を削除し、
・・もし「基点の座標一覧」にひとつでもあれば
・・・「基点の座標一覧」からどれかひとつをランダムで選ぶ
・・・その座標で掘る
・・「基点の座標」がもうなかったら
・・・終わり

いよいよ実装へ

digという関数はちょっと凝りました。結局、穴掘り法にちょっと変更を加えたものになりました。

numR = 39
numC = 39
posArr = []
pos = []
directions =[[2,0],[-2,0],[0,2],[0,-2]]

# 地図生成---------------------------
for row in [0...numR]
    pos[row] = []
    for col in [0...numC]
      if row is 0 or
         row is numR-1
          pos[row][col] = 2
      else if col is 0 or
          col is numC-1
              pos[row][col] = 2
      else
          pos[row][col] = 0
# 地図生成おわり---------------------

# 掘る-----------------------------
dig = (tC,tR) ->
  posArr.push([tC,tR])
  directions = diShuffle(directions)
  for direction,i in directions
    if pos[tC + directions[i][0]][tR + directions[i][1]] is 0
      #console.log "hi"
      pos[tC + directions[i][0]/2][tR + directions[i][1]/2] = 1
      pos[tC + directions[i][0]][tR + directions[i][1]] = 1
      dig(tC + directions[i][0],tR + directions[i][1])

#方向決定用に配列の順番をシャッフル
diShuffle = (arr) ->
  e = arr.length
  while e
    num = Math.floor(Math.random() * e)
    qwe = arr[--e]
    arr[e] = arr[num]
    arr[num] = qwe
  arr

#最初の一回目はランダムで選ぶ
randomPick = (numR,numC) ->
  ranR = Math.floor(Math.random() * ((numR-1) / 2 + 1)) * 2
  ranC = Math.floor(Math.random() * ((numC-1) / 2 + 1)) * 2
  if ranR is 0
    randomPick(numR,numC)
  else if ranR is (numR-1)
    randomPick(numR,numC)
  else if ranC is 0
    randomPick(numR,numC)
  else if ranC is (numC-1)
    randomPick(numR,numC)
  else
    return [ranR,ranC]

#いよいよ実行
pickRes = randomPick(numR,numC)
pos[pickRes[0]][pickRes[1]] = 1
dig(pickRes[0],pickRes[1])

# enchant.js部分-------
enchant()
window.onload = ->
  game = new Game(312, 312)
  game.preload "chara5.png","map0.png"
  game.onload = ->
    for mapy,iy in pos
      for mapx,ix in mapy
        map = new Sprite(16,16)
        map.x = ix * 8
        map.y = iy * 8
        map.scaleX = 1
        map.scaleY = 1
        map.image = game.assets["map0.png"]
        if mapx is 1
          map.frame = 2;
        else
          map.frame = 3;
        game.rootScene.addChild map
    game.rootScene.addEventListener "enterframe", ->
  game.start()
  console.log game
  return


関連ブログ