2016/11/03
「こおりのぬけみち」は,ポケモン金銀シリーズに出てきた迷路で,小学生だった筆者は解けなかった. この迷路については,ニコニコ大百科の当該項目 を参照してほしい. そして操作の再現ページ から遊んでほしい.
あれから10年以上がたち,筆者はアルゴリズムを学んだので,今回はコンピュータの力で「こおりのぬけみち」を解く. 以下がそのウェブページである.ちなみに自分で迷路を変更できるようにもしてみた.
本当はもっとまじめに幅優先探索しようかと思ったけど,面倒なので深さ優先探索した. あと若干雑である (番兵の存在,ゴールの向こうが壁であることを仮定など). ソースコードは github に置いておくので,幅優先探索を実装する課題としても良いかもしれない.
const dy = [-1, 0, 1, 0];
const dx = [ 0, 1, 0, -1];
function solve(map){
let alreadyVisited = new Set();
function serializePoint(y, x){
return x + "," + y;
}
let route = [];
function dfs(y, x){
// ゴールを発見したら,例外で脱出する
if(map[y][x] == GOAL){
throw new Route(route);
}
// もうそれより先にいってたら,中断
if(alreadyVisited.has(serializePoint(y, x))){
return;
}
alreadyVisited.add(serializePoint(y, x));
// 4方向試す
for(var i=0; i<4; i++){
// 氷に乗っていけるところまでいってみる
const ddy = dy[i];
const ddx = dx[i];
let ny = y;
let nx = x;
// 岩にあたるまでまっすぐ.(本当は g とかも考慮しないといけんね.)
while(map[ny][nx] != WALL){
ny += ddy;
nx += ddx;
}
// いきすぎなのでもどる
ny -= ddy;
nx -= ddx;
// 動けたらそれより先に dfs
if(!(y == ny && x == nx)){
// あとで表示するように今の場所をいれる
route.push([ny, nx]);
dfs(ny, nx);
// どうやらこれより先には答はなかったみたいなので
// 今の場所を破棄
route.pop();
}
}
}
function findStart(){
let ret = [];
for(let y=0; y<map.length; y++){
for(let x=0; x<map[y].length; x++){
if(map[y][x] == START){
ret.push([y, x]);
}
}
}
return ret;
}
try {
// スタートは複数あるかも?
for(let start of findStart()){
route = [start];
dfs(start[0], start[1]);
}
} catch(e) { // found
if(e instanceof Route){
console.log(e.route);
console.log("found");
return e.route;
}
throw(e);
}
return null;
}