迷路を文字列で記述して、それを読み込みたい。例えば、次のような記述。
...#####....... ...# #....... .### #######. .# @ #. .## ## # ###. ..# # #... ..# #### #... ..# #... ..##########...
'.'は部屋の外、'#'は壁、' 'は床、'@'はプレイヤー。
どう指定するか
読み込み関数へ渡すとき、一つの文字列で渡すか、文字列の配列で渡すか。
一つの文字列で渡す場合、行毎の区切り文字を決めるか、別途迷路の幅(桁数)を指定する必要がある。
var maze = loadMaze(" |...#####....... |...# #....... |.### #######. |.# @ #. |.## ## # ###. |..# # #... |..# #### #... |..# #... |..##########...");
文字列の配列で渡す場合、ちょっと記述量が増える。メモリ効率も悪い。
var maze = loadMaze( ["...#####.......", "...# #.......", ".### #######.", ".# @ #.", ".## ## # ###.", "..# # #...", "..# #### #...", "..# #...", "..##########..."]);
古い人間なのでついつい上を考えてしまうのだけど、上の方法は区切り文字を入れ忘れたり、幅の指定を間違えたりといったミスを誘いやすい。効率が問題となる場面ではないのだから、後者を選んだ方が無難だと思う。
1行の文字数が一定ではない場合があり得る。例えば、次のように指定したって良いじゃないか、という話だ。
var maze = loadMaze( ["...#####", "...# #", ".### #######.", ".# @ #", ".## ## # ###", "..# # #", "..# #### #", "..# #", "..##########"]);
これについては、読み込むときに最大の幅で揃えても良いし、揃えずに読み込んで、アクセスするときにチェックしても良い。詳しくは後で考えることにする。
読み込んでみる
Cell = { OUTSIDE: 0, FLOOR: 1, WALL: 2 }; function loadMaze(strs) { var assert = function(cond) { if(!cond){ throw "Failed to load maze.";}}; var player = null; var cells = []; for(var y in strs){ var cellsRow = []; for(var x = 0; x < strs[y].length; ++x){ // for/inではダメだった。Firefoxは動いたけど。 switch(strs[y].charAt(x)){ //strs[y][x]と書いたらIEで動かなかった。 case '#': cellsRow.push(Cell.WALL); break; case '@': cellsRow.push(Cell.FLOOR); assert(!player); player = {x:x, y:y}; break; case ' ': cellsRow.push(Cell.FLOOR); break; default: cellsRow.push(Cell.OUTSIDE); break; } } cells.push(cellsRow); } return {cells:cells, player:player}; }
出来るだけ平易に書いてみた。以下言い訳。
- 内外判定の自動化について。本当は'.'と書かなくても、部屋の外の' 'ならOUTSIDEだと判定したかったのだが、'#'で構成される線が閉じていないので、あまり簡単には書けそうもなかったのでやめた(閉じていれば簡単な交差回数で分かるんだけど)。ごちゃっとした不完全なコードを書くよりは、とりあえず手動で指定しておくことにした。
- playerのまわりにはOUTSIDEがあってはならないと思うのだけど、面倒なのでチェックしなかった。
- 行の長さはそろっていなければならないと思うのだけど、面倒なのでそのままにしておいた。
- ループじゃなくて、mapメソッドみたいなのって無かったっけ? と思ったけど、よく知らないのでやめた。ArrayのmapはJavaScript1.6から? 1.6ってどのブラウザで使えるんだ?
- switch文を連想配列たるオブジェクト{'#': Cell.WALL, '@': [Cell.FLOOR, function(){player = {x:x, y:y}], ' ': Cell.FLOOR, '.': Cell.OUTSIDE}で置き換えるような手も浮かんだんだけど、今日記に書くことではないかなと思ったのでやめた。
- 読んだ後のデータ構造をどうするか、これまた非常に迷うところなのだが、まあ、今回はこんなところで。動かない物はcellsの中で表現し、動く物はオブジェクトを作ってみた。動かない物とは何か、とか、本当に動かないのとか、とか突き詰めていくと正直よく分からない。
出す
とりあえず読んだ結果をテキストで出してみる。
// 読んでみるのコード ... // どう指定するかの2番目のコード ... document.open(); document.write("<pre>"); for(var y in maze.cells){ var line = ""; for(var x in maze.cells[y]){ switch(maze.cells[y][x]){ case Cell.OUTSIDE: line += '.'; break; case Cell.FLOOR: line += ' '; break; case Cell.WALL: line += '#'; break; } } document.writeln(line); } document.writeln("player:(" + maze.player.x + "," + maze.player.y + ")"); document.write("</pre>"); document.close();
次はグラフィカルに表示してみたい。