Rubyで迷路問題を解いた
スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般
話題になっていた迷路問題をRubyで解いてみた。
スポンサーリンク
人生を書き換える者すらいた。: 人材獲得作戦・4 試験問題ほか
情報系でもないしアルゴリズムも知らないので、Google先生で調べながら。
幅優先探索というアルゴリズムが分かりやすかったので、これで解いてみましたが、調べつつで2時間くらいかかった。だめだ・・・orz。でも、面白かったです。
幅優先探索は、候補をキューに貯めていき、一個一個キューから取り出して、チェックしていくとこがポイント。
あと、チェック済み(訪問済み)のノードには、フラグ立てておいて2回以上行かないようにする。
迷路問題を解くのに書いたコード
require "pp"
$debug = false # デバッグ用。trueにすると@visiteded, ゴール位置など出力
class Maze
attr_reader :w, :h, :visited, :maze, :prev
def initialize(maze)
# 二次元配列データでmazeを渡す
@maze = maze
@w = @maze[0].length
@h = @maze.length
# 訪問済みかどうかのフラグをfalseで初期化。@visited[h][w] = true
@visited = Array.new(h){ Array.new(w){ false } }
# 直前の地点(x, y)を[-1, -1]で初期化。@prev[h][w] = [-1, -1]
@prev = Array.new(h){ Array.new(w){ Array.new(2){ -1 } } }
end
def find_start
h.times do |y|
w.times do |x|
return [x, y] if @maze[y][x] == "S"
end
end
end
def goal_search
queue = Array.new
start = find_start
queue.push(start)
@visited[start[1]][start[0]] = true
# 右左下上の探索
dx = [+1,-1, 0, 0];
dy = [ 0, 0,+1,-1];
# bfsで探索
while !queue.empty?
pt = queue.first
queue.shift
dx.length.times do |i|
x = pt[0] + dx[i]
y = pt[1] + dy[i]
# 訪問してなくて、壁じゃない地点の場合、直前の地点を記録
if @visited[y][x] == false && @maze[y][x] != "*"
@prev[y][x][0] = pt[0]
@prev[y][x][1] = pt[1]
# ゴールの場合、終了
if @maze[y][x] == "G"
goal = [x, y]
return goal
else
# スペース(枝)の場合、キューに入れて訪問済みに
queue.push([x, y])
@visited[y][x] = true
end
end
end
end
end
def solve
# ゴール地点を取得し、@prevを遡る
pt = goal_search
print "ゴール位置:[#{pt[0]}, #{pt[1]}]\n\n" if $debug
while true
pt = [@prev[pt[1]][pt[0]][0], @prev[pt[1]][pt[0]][1]]
break if @maze[pt[1]][pt[0]] != " "
@maze[pt[1]][pt[0]] = "$"
end
puts "答えの出力:" if $debug
display
end
def display
@maze.each do |y|
y.each do |x|
print x
end
print "\n"
end
end
end
m = Maze.new( DATA.read.split("\n").map{ |line| line.split("") } )
m.solve
if $debug
print "\n訪問済みの位置[x, y]:\n"
m.h.times do |y|
m.w.times do |x|
pp [x, y] if m.visited[y][x]
end
end
end
__END__
**************************
*S* * *
* * * * ************* *
* * * ************ *
* * *
************** ***********
* *
** ***********************
* * G *
* * *********** * *
* * ******* * *
* * *
**************************
出力結果
************************** *S* * $$$$ * *$* *$$* $************* * *$* $$* $$************ * *$$$$* $$$$$ * **************$*********** * $$$$$$$$$$$$$ * **$*********************** * $$$$$* $$$$$$$$$$$$$G * * * $$$$*********** * * * * ******* * * * * * **************************
それにしても、Rubyは多次元配列(もどき)の初期化の書き方は、いまいちだるいなぁ。数少ないRubyの不満点。
hoge[][] ってふうに書きたい。
hoge[5][5] = { 0 } ってふうに書きたい、C言語みたいに。
なんか、もっと簡単な書き方あるのでしょうか。
rubyでの多次元配列について参考
rubyで多次元配列確保 – octech
初めてのおもちゃ箱 [ruby]ゲーム用に,多次元配列を作って初期化する方法
スポンサーリンク
>> 次の記事 : 麻雀の手牌に対する待ちを出力する問題をRubyで解いた
スポンサーリンク