穴掘り法によるダンジョンの自動生成をRubyで書いてみる

穴掘り法によるダンジョンの自動生成 (Unity2Dのサンプルコードつき)
https://qiita.com/2dgames_jp/items/037dc62afb268759d16d

の記事がとても興味深かったのでアルゴリズムの勉強をかねてRubyで書いてみました。

```rb
require "matrix"
V = Vector

class App
  def initialize(w, h)
    @w = w
    @h = h
    @m = {}

    @h.times do |y|
      @w.times do |x|
        @m[V[x, y]] = true
      end
    end
  end

  def run(v)
    @m[v] = false
    around.shuffle.each do |a|
      v1 = v + a
      v2 = v1 + a
      if @m[v2]
        @m[v1] = false
        run(v2)
      end
    end
  end

  def to_s
    @h.times.collect { |y|
      @w.times.collect { |x|
        v = V[x, y]
        case
        when @m[v]
          ":black_medium_square:"
        when v == V[1, 1]
          ":hamster:"
        when v == V[@w - 2, @h - 2]
          ":cheese:"
        else
          ":white_medium_square:"
        end
      }.join + "\n"
    }.join
  end

  def around
    @around ||= [[0, -1], [-1,  0], [1,  0],[0,  1]].collect { |e| V[*e] }
  end
end

app = App.new(31, 23)
app.run(V[1, 1])
puts app
```

:black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square:
:black_medium_square::hamster::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square:
:black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::black_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square::white_medium_square::black_medium_square:
:black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::white_medium_square::black_medium_square::cheese::black_medium_square:
:black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square::black_medium_square:

元の記事の解説にあるように左上と右下がちゃんと繋がっていることがわかります。