適当にRustのクレート見て回ってたらBFSのクレートがあってお?ってなったので眺めてみた。
以下の形式で、
pub fn bfs<N, FN, IN, FS>( start: &N, successors: FN, success: FS, ) -> Option<Vec<N>> where N: Eq + Hash + Clone, FN: FnMut(&N) -> IN, IN: IntoIterator<Item = N>, FS: FnMut(&N) -> bool,
開始点(start
)と、現在いるノードからの到達可能先(successors
)と、ゴールかどうかチェックする関数(success
)を渡すことで、BFSによりスタートからゴールまで辿り着けるか探索し、結果の経路をOptionのベクタ型で返してくれるものらしい。
上記サイトに載っているチェスの例を、少し書き換えたもの(main関数などの追加)を以下に載せる。
use pathfinding::prelude::bfs; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct Pos(i32, i32); impl Pos { fn successors(&self) -> Vec<Pos> { let &Pos(x, y) = self; vec![ Pos(x + 1, y + 2), Pos(x + 1, y - 2), Pos(x - 1, y + 2), Pos(x - 1, y - 2), Pos(x + 2, y + 1), Pos(x + 2, y - 1), Pos(x - 2, y + 1), Pos(x - 2, y - 1), ] } } fn main() { static GOAL: Pos = Pos(4, 6); let result = bfs(&Pos(1, 1), |p| p.successors(), |p| *p == GOAL); println!("{:?}", result); }
チェス盤をイメージしたときに、スタート地点(1, 1)から、ゴール地点(4, 6)のマスに、Knightの動き(将棋でいうとこの桂馬の全方位可能版みたいなの)で辿り着けるかを判定するプログラムになっている。
bfsの第一引数にスタート地点を指定し、第二引数には、Knightが動ける場所をPos#successorsで指定している。第三引数は、ゴールに辿り着いたかをboolで返す関数を指定している。
これを実行すると、以下の結果となる。
Some([Pos(1, 1), Pos(2, 3), Pos(3, 5), Pos(2, 7), Pos(4, 6)])
今回のケースだと、(1, 1)から(4, 6)は辿り着けるので、その経路が得られている。
実際には盤外などの処置も必要だと思うが、中々面白そうなのでクレートのソースも今度見てみる。
ちなみに上記の出力結果を図にするとこんな感じ(KがKnightの位置)。
スタート(1, 1)
1手目(2, 3)
2手目(3, 5)
3手目(2, 7)
ゴール(4, 6)