如何在prolog中打印迷宫

时间:2018-04-07 18:03:11

标签: prolog

我正在prolog中编写一个可以解决迷宫的程序,我需要一个能够用解决方案路径输出生成的迷宫的函数。打印的迷宫看起来像下面的'X'是障碍,'*'代表路径

+-------+
|***   x|
| x*x  x|
| x*xxxx|
| x*****|
+-------+

prolog中的迷宫由以下关系表示:

% 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).

上面显示的每个迷宫关系首先显示迷宫的大小,然后显示迷宫中每个点的两个坐标,然后显示“打开”或“屏障”。 “开放”是一个可以移动的空间,“障碍”是一个无法导航的地方。

到目前为止,我对printCell有几个定义,它们是帮助打印单个单元格的辅助规则,这些单元格要么是障碍要么是打开的。

printCell(Maze, _, Row, Column) :- maze(Maze, Row, Column, open), write(' ').
printCell(Maze, _, Row, Column) :- maze(Maze, Row, Column, barrier), write('x').

然后我如何定义一个递归循环迷宫关系的函数,并调用print cell来获得一个拼图,例如这个问题顶部显示的拼图?我是prolog的新手,正在努力掌握如何循环和打印迷宫,谢谢!

2 个答案:

答案 0 :(得分:1)

  

然后我如何定义一个递归循环迷宫关系并调用print cell

的函数

由于您要求递归解决方案,因此这是一个。在这个答案的最后有一个更短的,非递归的解决方案(或者更确切地说,一个隐藏递归的解决方案)。作为一个小而重要的术语,请注意Prolog只有谓词,而不是函数

要打印迷宫,我们需要做的就是逐个打印它的行:

print_maze(Maze) :-
    mazeSize(Maze, Rows, Columns),
    print_rows(Maze, 1, Rows, Columns).

print_rows/4的定义是递归第一次出现的地方。上面1参数的概念是计数器,从打印行开始,从1到行数计数。这是代码:

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).

有两种情况:一方面,我们完成计数的情况,迭代可以终止;另一方面,在我们尚未完成的情况下,我们实际上想要打印当前行然后继续。

要打印行,我们使用完全相同的迭代模式将给定行的列从1打印到总列数:

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).

同样,计数器参数在每次调用时递增,直到发现它刚好超过列数。与上面的一个小差异,我们通过打印换行符在行的末尾做了一些额外的工作。

顺便说一下,匿名变量printCell/4的第二个参数完全没用。我把它留在这里,但我建议你把它删除。

最后,这是我在实践中更有可能写的简短的非递归版本:

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)).

只要成功,forall/2谓词就会在第一个参数中调用目标,并在第一个参数中为第一个参数调用目标。有一些细节可能令人困惑,我建议您不要尝试使用forall/2,只要您是初学者。

答案 1 :(得分:1)

不需要递归谓词:

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).