地面に重みをつける — ダイクストラ
一歩のコストをマスごとに変える。沼は通れるが手間がかかる、舗装路は速い。一歩 1 で数える幅優先のままだと、コストの違うマスを一段ずつの輪では扱えない。未確定のマスのうち、いま分かっている中でいちばん距離の小さいものから順に確定していく。
ダイクストラ法。辺に重み(コスト)のあるグラフで始点からの最短距離を求める。Edsger Dijkstra が 1959 年に発表した。確定済みの集合を一つずつ広げ、毎回「未確定で距離最小の頂点」を確定する。確定した頂点の距離はそれ以上短くならない——重みが非負なら、より遠い頂点を経由して短くなることがないため。隣の距離は 現在の距離 + 入る辺の重み で更新する。すべての重みが 1 のとき、距離最小の頂点を選ぶ操作は同じ距離の輪を一斉に広げる幅優先探索に一致する。
地面の重みを場として作る。sin と cos の積でなだらかな帯を描き、しきい値を越えたマスを沼として 8、ほかを 1 にする。マスごとにコストが違う。
const swamp = Math.sin(x * 0.5) * Math.cos(y * 0.4) cost[idx(x, y)] = swamp > 0.45 ? 8 : 1 // 沼は1歩8ぶんの手間
波の進め方が変わる。コストが違うと、次に塗るべきマスが波面のどこにあるか定まらない。未確定のマスから距離が最小のものを一つ選んで確定し、その隣を更新する。隣を更新するとき足すのは 1 ではなく入る先のコスト。
done[best] = 1 // 最小距離のマスを確定
for (let k = 0; k < 4; k++) {
const ni = idx(nx, ny)
const nd = dist[best] + cost[ni] // 入る先のコストぶん遠い
if (nd < dist[ni]) dist[ni] = nd // より短ければ書き換える
}最小を選んで確定し、隣を見直す。この繰り返しで、距離が近い順に決まる。
塗り終えた濃淡は、沼の帯のところで等しいコストの輪がきゅっと詰まる。手間がかかる地面ほど少し進むだけで距離(明度)が一気に落ち、輪の間隔が狭くなる。cost を全部 1 に戻すと、距離最小を選ぶ操作が同じ距離の輪を一斉に広げる幅優先と一致して、同心円に戻る。