【问题标题】:Prolog Card Game: Valid setsProlog 纸牌游戏:有效套装
【发布时间】:2016-04-27 19:31:11
【问题描述】:

我正在开发一个简单的纸牌游戏,其中涉及将特定组的纸牌放在桌子上。我想写一个谓词,试图找出一组卡片是否可以排列成有效的组。

有效组中的卡必须排他地: a) 相同花色和连续数字(红心 1、2、3)或 b) 不同花色和相同数量的花色(红桃 1、方块 1 和梅花 1) 允许的最小组是三个。

第一步是定义卡片是什么,显而易见的方法是以谓词形式定义它们:

%hearts:
card(1,h).
card(2,h).
% etc.....

%diamonds:
card(1,d).
card(2,d).
%etc...

等等。

其次,我定义了一系列谓词来指定规则。卡如何连接,以及如何测试有效组。例如,我定义了以下内容来检查两张牌是否有效并且是否具有相同的花色和相同的数字:

card_will_connect((N1,S1),(N2,S2)):-
    %firstly check that the cards exist:
    card(N1,S1),
    card(N2,S2),
    %same_suit
    S1=S2,
        consecutive_number(S1,S2).

我还写了一些谓词,通过一组卡片来查看它们之间的连接是否有效。

问题是,如果我有一组随机卡片 - 我如何尝试以有效方式对它们进行分组而不进行详尽搜索?

我的想法是: a) 找出是否有任何卡片在集合中的可能连接为零。如果失败,请不要继续 b) 找出是否有任何卡只有 1 个有效连接,将它们移动到组 c) 现在用剩余的卡,尝试通过使用我的卡连接谓词将它们分配到这些组,以满足所需的规则

【问题讨论】:

  • 不太清楚您希望谓词如何工作。它是否将卡片作为第一个参数,将组作为第二个参数?
  • S1=S2, consecutive_number(S1,S2) 很奇怪。如果 S1 和 S2 相等,则它们不能连续。你是说consecutive_number(N1,N2) 吗?
  • 是的,Thomas,我的意思是说我用卡号调用谓词来检查它们。
  • 是的,我要写的谓词有两个参数。第一个参数是要分组的卡片列表,第二个参数是有效组的列表(列表列表)。

标签: list prolog


【解决方案1】:

我会对序列进行排序,并会使用 findall/3 和 append/2 等内置函数。那么验证谓词更容易编写:

% build a bridge deck
bridge_deck(Cs) :-
    findall(card(S,V), (member(S,[♥,♦,♣,♠]),between(1,13,V)), Cs).

% shuffle
bridge_hands([S,W,N,E]) :-
    bridge_deck(Cs),
%setrand(rand(1,2,3)), % get a known random set, to ease debugging
    random_permutation(Cs, RCs),
    maplist([H]>>length(H,13), [S,W,N,E]),
    append([S,W,N,E], RCs).

% behaves like a bridge player :)
hands_sorted(Sorted) :-
    bridge_hands(Hands),
    maplist(sort, Hands, Sorted).

group_hand(Hand, Groups) :-
    findall([A,B,C|D],
      (append([_,[A,B,C|D],_],Hand), 'same suit and consecutive numbers'([B,C|D],A)), Groups).

'same suit and consecutive numbers'([card(S,Y)|R], card(S,X)) :-
    succ(X,Y),
    'same suit and consecutive numbers'(R, card(S,Y)).
'same suit and consecutive numbers'([], _).

?- hands_sorted(Ps), maplist(group_hand, Ps, Gs), maplist(writeln, Gs).
Ps = [[card('♠', 1), card('♠', 4), card('♠', 5), card('♠', 11), card('♣', 2), card('♣', 4), card('♣', 10), card(..., ...)|...], [card('♠', 2), card('♠', 6), card('♠', 9), card('♠', 12), ...
Gs = [[], [], [[card('♥', 10), card('♥', 11), card('♥', 12)], [card('♥', 10), card('♥', 11), card('♥', 12), card(..., ...)], [card('♥', 11), card('♥', 12), card(..., ...)]], []].

我为桥接逻辑使用了更合适的表示:card(Suit,Value)

编辑

SWI-Prolog 有一个很好的结构化输出工具。这个 sn-p 以可读的形式布局了一个桥接表:

:- use_module(library(http/html_write)).

bridge_cards :-
    hands_sorted(Hands),
    layout_table(Hands).

layout_table([S,W,N,E]) :-
    phrase(html([\css,
     table([
        tr([\empty,     \layout_hand(N),    \empty]),
        tr([\layout_hand(W),    \empty,     \layout_hand(E)]),
        tr([\empty,     \layout_hand(S),    \empty])
    ])]), Tokens),
    with_output_to(atom(X), print_html(Tokens)),
    win_html_write(X).

css --> html(style(type='text/css',
    ['.size{background-color:lightgrey;}'
    ,'.player{color:blue;}'
    ,'.value{text-align:right;background-color:lightgreen}'
    ])).

empty --> html(td([class=size],[])).

layout_hand(Cards) -->
    {findall(S-Vs, (
        member(S, [♣,♦,♥,♠]),
        findall(V, member(card(S,V),Cards), Vs)
    ), SuitesValues)},
    html(td([class=player], table(\layout_suits(SuitesValues)))).

layout_suits([]) --> [].
layout_suits([Suit-Values|SVs]) -->
    html(tr([td(Suit), \layout_values(Values)])),
    layout_suits(SVs).

layout_values([]) --> [].
layout_values([V|Vs]) --> html(td([class=value], \layout_value(V))), layout_values(Vs).

layout_value(V) --> {nth1(V,['A',2,3,4,5,6,7,8,9,'T','J','Q','K'],C)}, html(C).

我认为以 HTML 格式获取输出特别方便,因为它允许以最小的麻烦进行试验,然后 - 如果需要 - 将代码移植到 SWI-Prolog HTTP 服务器,以便在野外运行。

swipl-win 运行的示例,由 Qt 处理 HTML 呈现:

【讨论】:

    猜你喜欢
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多