Reputation: 387
I'm writing a program in prolog that can solve mazes, I need a function that prints out the resulting maze with the solution path. A printed maze would look something like below where 'X' is a barrier and '*' represent the path
+-------+
|*** x|
| x*x x|
| x*xxxx|
| x*****|
+-------+
The maze in prolog are represented by the following relations:
% Maze size facts
mazeSize(small, 4, 7).
mazeSize(nobarrier, 7, 7).
mazeSize(unsolvable, 4, 4).
mazeSize(unknown, 4, 5).
% A small maze
maze(small, 1, 1, open).
maze(small, 1, 2, open).
maze(small, 1, 3, open).
maze(small, 1, 4, open).
maze(small, 1, 5, open).
maze(small, 1, 6, open).
maze(small, 1, 7, open).
maze(small, 2, 1, open).
maze(small, 2, 2, barrier).
maze(small, 2, 3, open).
maze(small, 2, 4, open).
maze(small, 2, 5, open).
maze(small, 2, 6, open).
maze(small, 2, 7, open).
maze(small, 3, 1, open).
maze(small, 3, 2, barrier).
maze(small, 3, 3, open).
maze(small, 3, 4, open).
maze(small, 3, 5, open).
maze(small, 3, 6, open).
maze(small, 3, 7, open).
maze(small, 4, 1, open).
maze(small, 4, 2, barrier).
maze(small, 4, 3, open).
maze(small, 4, 4, open).
maze(small, 4, 5, open).
maze(small, 4, 6, open).
maze(small, 4, 7, open).
Each maze relation shown above first shows the size of the maze, then the two coordinates of each point in the maze, then either says 'open' or 'barrier'. 'open' being a space where you can move, and 'barrier' being a place that can't be navigated to.
So far I have a couple definitions for printCell, which are helper rules to print individual cells which are either barriers or open.
printCell(Maze, _, Row, Column) :- maze(Maze, Row, Column, open), write(' ').
printCell(Maze, _, Row, Column) :- maze(Maze, Row, Column, barrier), write('x').
How would I then define a function that recursively loops through the maze relations and calls print cell to get a puzzle such as the one shown at the top of this question? I'm new to prolog and am struggling to grasp how to loop and print the maze, thanks!
Upvotes: 0
Views: 910
Reputation: 5615
No need of a recursive predicate :
getChar(open, ' ').
getChar(barrier, 'x').
% first and last lines of the maze
print_line(Width) :-
write('+'),
forall(between(1,Width, _), write('-')),
writeln('+').
% main predicate
print_maze :-
mazeSize(_, Height, Width),
print_line(Width),
% for each line of the maze
forall(between(1, Height, I),
( write('|'),
% for each cell of the line
forall(between(1, Width, J),
% What is the type of the corresponding cell
( maze(_, I, J, Type),
% What is the character of the type
getChar(Type, C),
write(C))),
writeln('|'))),
print_line(Width).
Upvotes: 1
Reputation: 9378
How would I then define a function that recursively loops through the maze relations and calls print cell
Since you asked for a recursive solution, here is one. There is a shorter, non-recursive solution (or rather, one that hides the recursion) at the end of this answer. As a small but important point of terminology, note that Prolog only has predicates, not functions.
To print a maze, all we need to do is to print its rows one by one:
print_maze(Maze) :-
mazeSize(Maze, Rows, Columns),
print_rows(Maze, 1, Rows, Columns).
The definition of print_rows/4
is where recursion first comes in. The idea of the 1
argument above is to be the counter that counts up from 1 to the number of rows as the rows are printed. Here is that code:
print_rows(_Maze, Row, Rows, _Columns) :-
% Once all rows are processed, there is nothing more to do.
Row =:= Rows + 1.
print_rows(Maze, Row, Rows, Columns) :-
% Continue if we are not yet past the last row.
Row =< Rows,
print_row(Maze, Row, 1, Columns),
Row1 is Row + 1,
print_rows(Maze, Row1, Rows, Columns).
There are two cases: on the one hand, the case when we are done counting, and iteration can terminate; and on the other hand, the case where we are not yet done, we actually want to print the current row and then continue.
To print a row, we use the exact same iteration pattern to print the given row's columns from 1 to the total number of columns:
print_row(_Maze, _Row, Column, Columns) :-
% Terminate with newline once all columns have been printed.
Column =:= Columns + 1,
nl.
print_row(Maze, Row, Column, Columns) :-
% Continue if we are not yet past the last column.
Column =< Columns,
printCell(Maze, _, Row, Column),
Column1 is Column + 1,
print_row(Maze, Row, Column1, Columns).
Again, a counter argument is incremented on each call until it is found to be just past the number of columns. As a small difference from above, we do some extra work at the end of the row by printing a newline.
As an aside, the second argument to printCell/4
, the anonymous variable, is completely useless. I left it in here, but I suggest you remove it.
Finally, here is the short, non recursive version that I would be more likely to write in practice:
print_row_short(Maze, Row) :-
mazeSize(Maze, _Rows, Columns),
forall(between(1, Columns, Column),
printCell(Maze, _, Row, Column)),
nl.
print_maze_short(Maze) :-
mazeSize(Maze, Rows, _Columns),
forall(between(1, Rows, Row),
print_row_short(Maze, Row)).
The forall/2
predicate calls the goal in its first argument as long as it succeeds, and calls the goal in the second argument for each success of the first. There are some details that can be confusing, and I would not recommend trying to use forall/2
as long as you are a beginner.
Upvotes: 1