「はてなブックマーク - 人生を書き換える者すらいた。: 人材獲得作戦・4 試験問題ほか」の「startから距離で塗りつぶして、あとはgoalから距離が減る方向に辿る(koizuka)」を参考に解いてみました。
maze_string = "**************************\n" + "*S* * *\n" + "* * * * ************* *\n" + "* * * ************ *\n" + "* * *\n" + "************** ***********\n" + "* *\n" + "** ***********************\n" + "* * G *\n" + "* * *********** * *\n" + "* * ******* * *\n" + "* * *\n" + "**************************\n" class String def chr_array array = [] self.each_byte{|c| array.push(c.chr)} array end end class Maze def initialize(maze_string) @array = maze_string.split("\n").map{|line| line.chr_array} # 配列に変換 @st_goal = start_goal_postion @array[@st_goal["start"]["y"]][@st_goal["start"]["x"]] = 0 @max_y = @array.size - 1 @max_x = @array[0].size - 1 end # スタート、ゴールの座標取得 def start_goal_postion y = 0 start = goal = nil @array.each do |line| x = 0 line.each do |p| start = {"x" => x, "y" => y} if p=="S" goal = {"x" => x, "y" => y} if p=="G" x += 1 end y += 1 end {"start" => start, "goal" => goal} end def start; @st_goal["start"] end def goal; @st_goal["goal"] end def mark mark_(start["y"]-1,start["x"]) mark_(start["y"]+1,start["x"]) mark_(start["y"], start["x"]-1) mark_(start["y"], start["x"]+1) end # 再帰的にスタートからの距離を記入 def mark_(y,x) if in_frame?(y,x) && (@array[y][x]==" " || @array[y][x].is_a?(Fixnum)) # 数字のときは元の数字より小さいときは入れ替え if @array[y][x].is_a?(Fixnum) if @array[y][x] > minimum_from4points(y,x) @array[y][x] = minimum_from4points(y,x) else return end else @array[y][x] = minimum_from4points(y,x) end # このコメントをはずすと数値を書き込んでいる様子が見える。 # show; sleep(0.5) mark_(y-1,x) mark_(y+1,x) mark_(y,x-1) mark_(y,x+1) end end # 上下左右4点から最小の数字を選択し、それに1を加えた数字を取得 def minimum_from4points(y,x) if @array[y][x]!="*" work = [] work.push(@array[y-1][x]) if number?(y-1,x) work.push(@array[y+1][x]) if number?(y+1,x) work.push(@array[y][x-1]) if number?(y,x-1) work.push(@array[y][x+1]) if number?(y,x+1) minimum = work.sort.first minimum.is_a?(Fixnum) ? minimum + 1 : @array[y][x] else "*" end end # 数字かどうか判定 def number?(y,x) if in_frame?(y,x) @array[y][x].is_a?(Fixnum) else false end end # 座標の枠内判定 def in_frame?(y,x); y >= 0 && y <= @max_y && x >= 0 && x <= @max_x end # ゴールから、上下左右4点最小の座標を取得、"$"を書いてスタートまで再帰。 def write_route; write_route_(goal["y"],goal["x"]) end def write_route_(y,x) if @array[y][x]==0 @array[y][x]="S" else @array[y][x]="$" unless @array[y][x]=="G" work = [] work.push([y-1,x]) if number?(y-1,x) work.push([y+1,x]) if number?(y+1,x) work.push([y,x-1]) if number?(y,x-1) work.push([y,x+1]) if number?(y,x+1) minimum = work.sort{|a, b| @array[a[0]][a[1]] <=> @array[b[0]][b[1]]}.first # このコメントをはずすと答えのルートを書き込んでいる様子が見える。 # show; sleep(0.5) write_route_(minimum[0],minimum[1]) end end def del_num @array = @array.map do |line| line.map{|c| c.is_a?(Fixnum)?" ":c} end end def show @array.each do |line| puts line.map{|c| sprintf("%2s ",c.to_s)}.join end end end m = Maze.new(maze_string) m.mark m.show =begin スタートからの距離を書き込む。 * * * * * * * * * * * * * * * * * * * * * * * * * * * 0 * 8 * 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 * * 1 * 7 * 9 10 * 14 15 * * * * * * * * * * * * * 29 30 * * 2 * 6 7 8 * 16 15 16 17 * * * * * * * * * * * * 30 31 * * 3 4 5 6 * 18 17 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 * * * * * * * * * * * * * * * 23 * * * * * * * * * * * * 37 36 35 34 33 32 31 30 29 28 27 26 25 24 25 26 27 28 29 30 31 32 33 34 * * * 37 * * * * * * * * * * * * * * * * * * * * * * * * 39 38 39 40 41 42 * 46 47 48 49 50 51 52 53 54 55 56 57 58 59 G 65 66 * * 40 39 * 41 42 43 44 45 46 * * * * * * * * * * * 60 * 64 65 * * 41 40 41 42 * 44 45 46 47 48 49 50 51 * * * * * * * 61 * 63 64 * * 42 41 42 43 44 45 46 * 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 * * * * * * * * * * * * * * * * * * * * * * * * * * * =end m.write_route m.show =begin ゴールから小さい数字をたどる。 * * * * * * * * * * * * * * * * * * * * * * * * * * * S * 8 * 10 $ $ $ $ 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 * * $ * 7 * $ $ * 14 $ * * * * * * * * * * * * * 29 30 * * $ * 6 $ $ * 16 15 $ $ * * * * * * * * * * * * 30 31 * * $ $ $ $ * 18 17 16 17 $ $ $ $ $ 23 24 25 26 27 28 29 30 31 32 * * * * * * * * * * * * * * * $ * * * * * * * * * * * * 37 $ $ $ $ $ $ $ $ $ $ $ $ $ 25 26 27 28 29 30 31 32 33 34 * * * $ * * * * * * * * * * * * * * * * * * * * * * * * 39 $ $ $ $ $ * 46 $ $ $ $ $ $ $ $ $ $ $ $ $ G 65 66 * * 40 39 * 41 42 $ $ $ $ * * * * * * * * * * * 60 * 64 65 * * 41 40 41 42 * 44 45 46 47 48 49 50 51 * * * * * * * 61 * 63 64 * * 42 41 42 43 44 45 46 * 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 * * * * * * * * * * * * * * * * * * * * * * * * * * * =end m.del_num m.show =begin * * * * * * * * * * * * * * * * * * * * * * * * * * * S * * $ $ $ $ * * $ * * $ $ * $ * * * * * * * * * * * * * * * $ * $ $ * $ $ * * * * * * * * * * * * * * $ $ $ $ * $ $ $ $ $ * * * * * * * * * * * * * * * $ * * * * * * * * * * * * $ $ $ $ $ $ $ $ $ $ $ $ $ * * * $ * * * * * * * * * * * * * * * * * * * * * * * * $ $ $ $ $ * $ $ $ $ $ $ $ $ $ $ $ $ $ G * * * $ $ $ $ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * =end