迷路を文字列で記述して、それを読み込みたい。例えば、次のような記述。
...#####.......
...# #.......
.### #######.
.# @ #.
.## ## # ###.
..# # #...
..# #### #...
..# #...
..##########...
'.'は部屋の外、'#'は壁、' 'は床、'@'はプレイヤー。
どう指定するか
読み込み関数へ渡すとき、一つの文字列で渡すか、文字列の配列で渡すか。
一つの文字列で渡す場合、行毎の区切り文字を決めるか、別途迷路の幅(桁数)を指定する必要がある。
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();
次はグラフィカルに表示してみたい。