|
| 1 | +path(SS):- |
| 2 | + width(W),height(H),empty_grid(W,H,Grid), |
| 3 | + setx_hor(5/3,4,Grid),setx_hor(7/6,2,Grid),setx_hor(3/9,4,Grid), |
| 4 | + setx_ver(2/5,3,Grid),setx_ver(4/4,3,Grid),setx_ver(7/7,2,Grid), |
| 5 | + search(Grid,SS),show_grid(Grid). |
| 6 | + |
| 7 | +width(8). height(10). |
| 8 | +entrance(1/1). exit(8/5). %exit(3/5). |
| 9 | + |
| 10 | +/* |
| 11 | + * Search predicates |
| 12 | + * move/2 enumerates all possible moves, ignoring whether cell is free |
| 13 | + * search/2 searches grid with various search strategies |
| 14 | + */ |
| 15 | + |
| 16 | +:-use_module(library(lists)). |
| 17 | + |
| 18 | +moves(X/Y,Moves):- |
| 19 | + bagof(X1/Y1,move(X/Y,X1/Y1),Moves). |
| 20 | + |
| 21 | +move(X/Y,X/Y1):- |
| 22 | + height(H), |
| 23 | + H>Y,Y1 is Y+1. % down |
| 24 | +move(X/Y,X1/Y):- |
| 25 | + width(W), |
| 26 | + W>X,X1 is X+1. % right |
| 27 | +move(X/Y,X/Y1):- |
| 28 | + Y>1,Y1 is Y-1. % up |
| 29 | +move(X/Y,X1/Y):- |
| 30 | + X>1,X1 is X-1. % left |
| 31 | + |
| 32 | +% for A* search: generates moves with increasing F-value |
| 33 | +moves_h(F/G:X/Y,Moves):- |
| 34 | + setof(F1/G1:X1/Y1,move_h(F/G:X/Y,F1/G1:X1/Y1),Moves). |
| 35 | + |
| 36 | +move_h(_/G:X/Y,F1/G1:X1/Y1):- |
| 37 | + move(X/Y,X1/Y1), |
| 38 | + h(X1/Y1,H1), |
| 39 | + G1 is G+1, % increase G-value from previous |
| 40 | + F1 is G1+H1. % add new H-value |
| 41 | + |
| 42 | +h(X/Y,H):- |
| 43 | + exit(XE/YE), |
| 44 | + H is abs(XE-X)+abs(YE-Y). % Manhattan distance |
| 45 | + |
| 46 | + |
| 47 | +search(Grid,SS):- |
| 48 | + entrance(X/Y), |
| 49 | + ( SS=bt -> search_bt(0,X/Y,Grid) % backtracking search |
| 50 | + ; SS=df -> search_df(0,[X/Y],Grid) % agenda-based depth-first |
| 51 | + ; SS=bf -> search_bf(0,[X/Y],Grid) % agenda-based breadth-first |
| 52 | + ; SS=as -> h(X/Y,H),search_as(0,[H/0:X/Y],Grid) % A* search |
| 53 | + ). |
| 54 | + |
| 55 | +% backtracking search |
| 56 | +% only path found is indicated in grid |
| 57 | +search_bt(N,X/Y,Grid):- |
| 58 | + exit(X/Y), |
| 59 | + set(X/Y,Grid,N). |
| 60 | +search_bt(N,X/Y,Grid):- |
| 61 | + N1 is N+1, |
| 62 | + set(X/Y,Grid,N), |
| 63 | + move(X/Y,X1/Y1), |
| 64 | + search_bt(N1,X1/Y1,Grid). |
| 65 | + |
| 66 | +% agenda-based depth-first search |
| 67 | +% grid is used for loop-detection: if set/3 fails, it is either an obstacle |
| 68 | +% or we've been there already |
| 69 | +search_df(N,[X/Y|_],Grid):- |
| 70 | + exit(X/Y), |
| 71 | + set(X/Y,Grid,N). |
| 72 | +search_df(N,[X/Y|Agenda],Grid):- |
| 73 | + ( set(X/Y,Grid,N) -> moves(X/Y,Moves),N1 is N+1, |
| 74 | + append(Moves,Agenda,NewAgenda) |
| 75 | + ; otherwise -> NewAgenda=Agenda,N1=N % ignore: can't go there |
| 76 | + ),search_df(N1,NewAgenda,Grid). |
| 77 | + |
| 78 | +% agenda-based breadth-first search |
| 79 | +search_bf(N,[X/Y|_],Grid):- |
| 80 | + exit(X/Y), |
| 81 | + set(X/Y,Grid,N). |
| 82 | +search_bf(N,[X/Y|Agenda],Grid):- |
| 83 | + ( set(X/Y,Grid,N) -> moves(X/Y,Moves),N1 is N+1, |
| 84 | + append(Agenda,Moves,NewAgenda) |
| 85 | + ; otherwise -> NewAgenda=Agenda,N1=N % ignore: can't go there |
| 86 | + ),search_bf(N1,NewAgenda,Grid). |
| 87 | + |
| 88 | +% A* search: agenda contains F/G:X/Y with F=G+H evaluation of position X/Y |
| 89 | +search_as(N,[_:X/Y|_],Grid):- |
| 90 | + exit(X/Y), |
| 91 | + set(X/Y,Grid,N). |
| 92 | +search_as(N,[F/G:X/Y|Agenda],Grid):- |
| 93 | + ( set(X/Y,Grid,N) -> moves_h(F/G:X/Y,Moves),N1 is N+1, |
| 94 | + merge_h(Moves,Agenda,NewAgenda) |
| 95 | + ; otherwise -> NewAgenda=Agenda,N1=N % ignore: can't go there |
| 96 | + ),search_as(N1,NewAgenda,Grid). |
| 97 | + |
| 98 | +merge_h([],Agenda,Agenda). |
| 99 | +merge_h(Moves,[],Moves). |
| 100 | +merge_h([F/G:X/Y|Moves],[F1/G1:X1/Y1|Agenda],[F/G:X/Y|NewAgenda]):- |
| 101 | + F1>F, |
| 102 | + merge_h(Moves,[F1/G1:X1/Y1|Agenda],NewAgenda). |
| 103 | +merge_h([F/G:X/Y|Moves],[F1/G1:X1/Y1|Agenda],[F1/G1:X1/Y1|NewAgenda]):- |
| 104 | + F>=F1, |
| 105 | + merge_h([F/G:X/Y|Moves],Agenda,NewAgenda). |
| 106 | + |
| 107 | + |
| 108 | +/* |
| 109 | + * Grid datastructure: list of H rows of length W. |
| 110 | + * A variable indicates cell is empty, so it can be |
| 111 | + * instantiated once with 'x' to indicate an obstacle |
| 112 | + * or with a number to indicate at which search iteration |
| 113 | + * it was explored. |
| 114 | + */ |
| 115 | + |
| 116 | +empty_grid(W,H,Grid):- |
| 117 | + length(Grid,H), % generate a list of variables of length H |
| 118 | + empty_rows(W,Grid). % and instantiate each variable to an empty row |
| 119 | + |
| 120 | +empty_rows(_,[]). |
| 121 | +empty_rows(W,[Row|Rows]):- |
| 122 | + length(Row,W), % generate a list of variables of length W |
| 123 | + empty_rows(W,Rows). |
| 124 | + |
| 125 | +get_row(N,Grid,Row):- |
| 126 | + nth1(N,Grid,Row). |
| 127 | + |
| 128 | +get_col(_,[],[]). |
| 129 | +get_col(N,[Row|Rows],[X|Col]):- |
| 130 | + nth1(N,Row,X), |
| 131 | + get_col(N,Rows,Col). |
| 132 | + |
| 133 | +% Create horizontal/vertical obstacles at position X/Y with length L |
| 134 | +setx_hor(X/Y,L,Grid):- |
| 135 | + get_row(Y,Grid,Row), |
| 136 | + setx(X,L,Row). |
| 137 | + |
| 138 | +setx_ver(X/Y,L,Grid):- |
| 139 | + get_col(X,Grid,Col), |
| 140 | + setx(Y,L,Col). |
| 141 | + |
| 142 | +setx(_,0,_). |
| 143 | +setx(X,L,Row):- |
| 144 | + L>0,L1 is L-1,X1 is X+1, |
| 145 | + nth1(X,Row,x), |
| 146 | + setx(X1,L1,Row). |
| 147 | + |
| 148 | +% Set cell X/Y to N; fails if cell has been set already |
| 149 | +set(X/Y,Grid,N):- |
| 150 | + get_row(Y,Grid,Row), |
| 151 | + nth1(X,Row,C), |
| 152 | + var(C), |
| 153 | + C=N. |
| 154 | + |
| 155 | +show_grid(Grid):- |
| 156 | + nl, |
| 157 | + show_rows(Grid), |
| 158 | + nl. |
| 159 | + |
| 160 | +show_rows([]). |
| 161 | +show_rows([Row|Rows]):- |
| 162 | + show_row(Row), |
| 163 | + show_rows(Rows). |
| 164 | + |
| 165 | +show_row([]):-nl. |
| 166 | +show_row([H|T]):- |
| 167 | + ( var(H) -> format('~|~` t~w~3+ ',['.']) |
| 168 | + ; H=x -> format('~|~` t~w~3+ ',[H]) |
| 169 | + ; otherwise -> format('~|~` t~d~3+ ',[H]) |
| 170 | + ),show_row(T). |
| 171 | + |
| 172 | + |
| 173 | +/** <examples> |
| 174 | +
|
| 175 | +?- member(SS,[df,bf,as,bt]),path(SS). |
| 176 | +
|
| 177 | +*/ |
0 commit comments