转载自:https://mp.weixin.qq.com/s/PbopBAcQo9q37_sUanh4zw
给定一个填字游戏和一组单词的空的(或几乎空的)框架。问题是将这些词放入框架中。
特定的填字游戏在文本文件中指定,该文件首先以任意顺序列出单词(每行一个单词)。然后,在空行之后,定义填字游戏框架。在此框架规范中,空字符位置由点(.)表示。为了简化解决方案,字符位置还可以包含预定义的字符值。然而谜题在文件p7_09a.dat中定义,其它谜题示例为p7_09b.dat和p7_09d.dat。还有一个没有解的谜题示例(p7_09c.dat)。
单词是至少两个字符的字符串(字符列表)。纵和横字谜框架中字符位置的水平或垂直顺序称为网格。我们的问题是找到一种在网格上放置相容单词的方法。
提示:
1)问题不容易。您将需要一些时间来彻底了解它。因此,不要太早放弃!请记住,目标是一个清晰的解,而不仅仅是快速而肮脏的骇客!
(2)读取数据文件是一个棘手的问题,文件p7_09-readfile.pl中提供了解决方法。使用谓词read_lines/2。
(3)出于效率的考虑,至少对于较大的谜题,以特定顺序对单词和位置进行排序非常重要。对于这一部分问题,“Prolog 列表练习 3” 的解决方案可能会非常有帮助。
程序文件是:p7_09.pl
:- ensure_loaded('p7_09-readfile.pl'). % 用于读取数据文件
crossword :-
write('usage: crossword(File)'), nl,
write('or crossword(File,Opt) with Opt one of 0,1, or 2'), nl,
write('or crossword(File,Opt,debug) for extra output'), nl.
:- crossword.
% crossword/1 无需优化即可运行(建议不要用于大文件)
crossword(FileName) :-
crossword(FileName,0).
% crossword/2 以给定的优化运行,没有调试输出
crossword(FileName,Opt) :-
crossword(FileName,Opt,nodebug).
% crossword/3 以给定的优化和给定的调试方式运行
crossword(FileName,Opt,Debug) :-
read_lines(FileName,Lines), % 从文件p99-readfile.pl的谓词 read_lines/2 返回一个字符列表
separate(Lines,Words,FrameLines),
length(Words,NWords),
construct_squares(FrameLines,Squares,MaxRow,MaxCol),
debug_write(Debug,Squares),
construct_sites(Squares,MaxRow,MaxCol,Sites),
length(Sites,NSites),
check_lengths(NWords,NSites),
solve(Words,Sites,Opt,Debug), % 做实际的工作
show_result(Squares,MaxRow,MaxCol).
debug_write(debug,X) :-
!,
write(X), nl, nl.
debug_write(_,_).
check_lengths(N,N) :- !.
check_lengths(NW,NS) :-
NW \= NS,
write('Number of words does not correspond to number of sites.'), nl,
fail.
% 输入准备
% 解析数据文件并将单词列表与框架描述分离
separate(Lines,Words,FrameLines) :-
trim_lines(Lines,LinesT),
parse_non_empty_lines(LinesT-L1,Words), % 差异列表!
parse_empty_lines(L1-L2),
parse_non_empty_lines(L2-L3,FrameLines),
parse_empty_lines(L3-[]).
% 删除行尾的空白
trim_lines([],[]).
trim_lines([L|Ls],[LT|LTs]) :-
trim_line(L,LT),
trim_lines(Ls,LTs).
trim_line(L,LT) :-
reverse(L,RL),
rm_white_space(RL,RLT),
reverse(RLT,LT).
rm_white_space([X|Xs],L) :-
char_type(X,white),
!,
rm_white_space(Xs,L).
rm_white_space(L,L).
% 将单词行与框架行分开
parse_non_empty_lines([L|L1]-L2,[L|Ls]) :-
L \= [],
!,
parse_non_empty_lines(L1-L2,Ls).
parse_non_empty_lines(L-L,[]).
parse_empty_lines([[]|L1]-L2) :-
!,
parse_empty_lines(L1-L2).
parse_empty_lines(L-L).
% 一个方框是单个字符的位置。作为Prolog项,方框的形式为sq(Row,Col,X),其中X表示字符,Row和Col定义拼图框内的位置。方框只是所有sq/3项的列表。
construct_squares(FrameLines,Squares,MaxRow,MaxCol) :- %(+,-,+,+)
construct_squares(FrameLines,SquaresList,1),
flatten(SquaresList,Squares),
maxima(Squares,0,0