Prologで4ナイト入れ替え問題を実装
4 Knights Problem?
4 Knights Problemという問題があります。「ナイト 入れ替え チェス」などとググれば、簡単にヒットしますが、簡単に説明すると、3x3のマスの四隅に白・黒のチェスのナイトが2つずつ置かれていて、それを動かして、白と黒の位置を交換するという問題です。本来のナイトの動きと異なる移動をしたり、盤面ごと回転させるというのはダメ。
今回私は必要に迫られて、このパズルを実装せざるを得なくなりました(ソルバではなく、パズルを人間が遊ぶための対話環境!)。
以下のような初期状態からはじまり、
♘ | ♘ | |
♞ | ♞ |
以下のような終了状態に移動させるのが目標。
♞ | ♞ | |
♘ | ♘ |
Java Appletで実装されたゲームがたくさんあるので、それでチャレンジしてみると面白いです。
Prologによる実装
諸事情により、Prologでこのゲームを実装しました。短いコードですので以下にそのまま書きます。
遊び方
[From, To]:
というプロンプトが出ますので、移動元のマスの番号と、移動先のマスの番号を以下のように入力してください。
[Fromt, To]: [0, 1].
なお、マスの番号は、左上から右下へ、0, 1, 2, ... 8 というように振られています。
0 | 1 | 2 |
3 | 4 | 5 |
6 | 7 | 8 |
ソースコード
% start game chess :- game_loop([w, x, b, x, x, x, w, x, b]). show([A, B, C, D, E, F, G, H, I]) :- write(A), write(' '), write(B), write(' '), write(C), nl, nl, write(D), write(' '), write(E), write(' '), write(F), nl, nl, write(G), write(' '), write(H), write(' '), write(I), nl, nl. % replace(idx, NewValue, List1, List2) % List1のidx番目の要素をNewValueに置き換えたものはList2である。 replace(0, NewValue, [_|Tail], [NewValue|Tail]) :- !. replace(Idx, NewValue, [Head|Tail], [Head|Tail2]) :- Idx2 is Idx - 1, replace(Idx2, NewValue, Tail, Tail2). % Board上のFromにある駒を、Toに移動させたら、NewBoardになる。 move(From, To, Board, NewBoard) :- nth0(From, Board, Elem1), nth0(To, Board, Elem2), Elem1 \= x, Elem2 = x, replace(From, Elem2, Board, PreNewBoard), replace(To, Elem1, PreNewBoard, NewBoard). % can_move(X, Y) % 盤面上の位置Xにいるナイトは、位置Yへ移動できる。 can_move(0, 5). can_move(0, 7). can_move(1, 6). can_move(1, 8). can_move(2, 3). can_move(2, 7). can_move(3, 2). can_move(3, 8). can_move(5, 0). can_move(5, 6). can_move(6, 1). can_move(6, 5). can_move(7, 0). can_move(7, 2). can_move(8, 1). can_move(8, 3). can_move(_, _) :- write('cannot move'), nl, fail. % 終了条件 game_loop([b, x, w, x, x, x, b, x, w]) :- !, write('finish!'), nl. % ゲームのループ game_loop(Board) :- show(Board), write('[From, To]: '), read(FromTo), [From, To] = FromTo, % 移動して、次の入力へ can_move(From, To), (move(From, To, Board, NewBoard), !, game_loop(NewBoard)); game_loop(Board).
実行例
実行例をなんとなく書いておきます。ピリオドを忘れないのがポイントです。wは白、bは黒、xは空のマスの意味です。
$ swipl ?- [fourknights]. ?- chess. w x b x x x w x b [From, To]: [0,5]. x x b x x w w x b [From, To]: