我正在尝试编写一个谓词来分析常见的扑克手;例如,给定一个“卡片”列表,确定玩家是否有 4 个同类;3种;对等:我的想法是检查相似的排名,如果没有则删除:
这适用于四种([“A”,“J”,10,“Q”,“A”,“A”,“A”])
但不是所有情况;这里的逻辑有什么指导吗?
谢谢
问题是你只检查手中的第一张牌是否在该组中出现了四次。您需要对所有卡执行此操作。
我将引入一个辅助谓词来计算您看到的牌张数,并让主谓词遍历手中的牌,直到找到一组四张牌:
four([H|T]) :- four0(H,1,T), !. % find a set of four Hs
four([_|T]) :- four(T). % else continue with remaining set
four0(_,4,_) :- !. % found four cards: stop
four0(X,I,[X|T]) :- !,I1 is I+1,four0(X,I1,T). % found another card: inc counter
four0(X,I,[_|T]) :- four0(X,I,T). % else continue
如果不是为了短名单,您可以通过例如记住您已经检查过的卡片或删除它们来改进它。如果列表开始排序也会容易得多。
顺便说一句,您可以将原始第一个子句中的嵌套列表简化为[H,H,H,H]
,将第二个子句中的嵌套列表简化为[H1,H2|T]
。对眼睛更轻松!
考虑充分利用内置函数:当您对列表进行排序时,所有元素都会被分组,然后检查序列变得容易:
fourofakind(Hand) :- % not intersted to what card is
fourofakind(Hand, _).
fourofakind(Hand, C) :-
msort(Hand, Sorted),
append([_, [C,C,C,C], _], Sorted).
谓词有2种形式,后者还提供卡号。请使用msort调用:使用排序我们会丢失重复项...
正如 chac 指出的那样,为了再次引起我们在这篇文章中的辩论,您可以使用 append 成功地解析您的列表,一旦排序很容易。没有排序,你可以写:
fourofakind(Hand, X) :- append([_, [X], _, [X], _, [X], _, [X], _], Hand).
这基本上告诉序言:我希望我的手有 4 倍的子列表 [X],中间有任何东西。
或者,在他对另一个线程(DCGs)的回复中使用@false描述为一个非常吸引人的解决方案:
four --> ..., [X], ..., [X], ..., [X], ..., [X], ... .
... --> [] | [_], ... .
?- Xs = "bacada", phrase(four, Xs).
您也可以通过使用基本递归来避免使用太多内置函数:
three_of_a_kind(Item, [Item|Tail]) :- pair(Item, Tail).
three_of_a_kind(Item, [_Item|Tail]) :- three_of_a_kind(Item, Tail).
pair(Item, [Item|Tail]) :- one(Item, Tail).
pair(Item, [_NotItem|Tail]) :- pair(Item, Tail).
one(Item, [Item|_Tail]).
one(Item, [_NotItem|Tail]) :- one(Item, Tail).
请注意,这里one/2
等同于 的天真定义member/2
。我让你four_of_a_kind/1
通过看看如何添加任务three_of_a_kind/1
和pair/2
工作!使用 cut 来删除未使用的选择点也会很有趣。