I’m using: SWI-Prolog version ???
SWI-Prolog version 7.6.4 for amd64
I want the code to:
Run plunit test without cause the ERROR shown in Topic.
But what I’m getting is:
evansl@lje-HP-ubuntu:~/dwnlds/bridge/pybridge/launchpad/oldlady/mylady/mylady-code/oldlady$ swipl -s handeval-test.plt -t “run_tests([handeval:ntp1])” ; cat handeval-test.plt
ERROR: /home/evansl/dwnlds/bridge/pybridge/launchpad/oldlady/mylady/mylady-code/oldlady/handeval-test.plt:16:
PL-Unit: incompatible test-options: [true(_1398=@=14),true(_1414=@=1)]
My code looks like this:
% your code here
% Old Lady
% Copyright (C) 2008 Paul Kuliniewicz <paul@kuliniewicz.org>
%
% PlUnit tests for handeval.pl.
:- use_module(library(plunit)).
:- use_module(handeval).
:- begin_tests(handeval).
evaluate_hand(Spades, Hearts, Diamonds, Clubs, HandInfo) :-
expand_all_suits(Spades, Hearts, Diamonds, Clubs, AllCards),
default_knowledge(HandInfo),
interpret_hand(AllCards, HandInfo).
test(ntp1, [true(HCP =@= 14), true(LCP =@= 1)]) :-
evaluate_hand([ace, king, 9], [ace, jack, 10, 8, 6], [7, 4], [queen, 7, 5], HandInfo),
high_card_points(HandInfo, HCP),
long_card_points(HandInfo, LCP).
:- end_tests(handeval).
The handeval.pl is:
% Old Lady
% Copyright (C) 2008 Paul Kuliniewicz <paul@kuliniewicz.org>
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2, or (at your option)
% any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA.
% Basic hand evaluation framework, which should be usable for implementing
% any bidding system. Based on Chapters 1 and 7 of The ABCs of Bridge,
% William Root, % 1998.
:- module(handeval, [
default_knowledge/1, % ?HandInfo
default_everyone/4, % ?SelfInfo, ?LeftInfo, ?PartnerInfo, ?RightInfo
interpret_hand/2, % +Cards, +HandInfo
expand_suit/3, % ?Suit, ?ListOfRanks, ?ListOfCards
expand_all_suits/5, % +SpadeRanks, +HeartRanks, +DiamondRanks, +ClubRanks, +ListOfCards
points/2, % +HandInfo, ?Points
points/3, % +HandInfo, +TrumpSuit, ?Points
high_card_points/2, % +HandInfo, ?HCP
high_card_points/3, % +HandInfo, +Suit, ?HCP
long_card_points/2, % +HandInfo, ?LCP
trump_points/3, % +HandInfo, +TrumpSuit, ?TP
demi_quick_tricks/2, % +HandInfo, ?DQT
demi_quick_tricks/3, % +HandInfo, +Suit, ?DQT
suit_length/3, % +HandInfo, +Suit, ?Length
count_cards/3, % +HandInfo, +Rank, ?Count
tricks/2, % +HandInfo, ?Tricks
tricks/3, % +HandInfo, +Suit, ?Tricks
supp_tricks/3, % +HandInfo, +PartnerSuit, ?Tricks
supp_demi_tricks/4, % +HandInfo, +PartnerSuit, +Suit, ?DemiTricks
distribution/2, % +HandInfo, +Distributions
deny_distribution/2, % +HandInfo, +Distributions
balanced/1, % +HandInfo
balanced_or_semi/1, % +HandInfo
unbalanced/1, % +HandInfo
strong_suit/2, % +HandInfo, +Suit
not_strong_suit/2, % +HandInfo, +Suit
very_strong_suit/2, % +HandInfo, +Suit
not_very_strong_suit/2, % +HandInfo, +Suit
stopper/2, % +HandInfo, +Suit
no_stopper/2, % +HandInfo, +Suit
game/2, % ?Level, ?Suit
unfavorable_vuln/1, % ?Vuln
equal_vuln/1, % ?Vuln
favorable_vuln/1, % ?Vuln
preempt_level/3, % +Vuln, ?Tricks, ?Level
suit_lt/2, % ?SuitA, ?SuitB
suit_le/2, % ?SuitA, ?SuitB
rank_lt/2 % ?RankA, ?RankB
]).
:- use_module(library(clpfd)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Notes and notation
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Cards are represented with the tuple:
% card(Suit, Rank)
% where Suit = spades, hearts, diamonds, or clubs
% Rank = ace, king, queen, jack, 10, 9, ..., 2
% Bids are represented with the tuple:
% bid(Level, Trumps)
% where Trumps = no_trump, spades, hearts, diamonds, or clubs
% Level = 7, 6, ..., 1
% The following tuples are also bids:
% bid(pass)
% bid(double)
% bid(redouble)
% A player's hand is represented by a list of cards.
% Incomplete knowledge of a player's hand is represented with the tuple:
% dealt(Spades, Hearts, Diamonds, Clubs)
% where each subterm is a 13-element list, one element for each card of
% that suit (from ace down to 2). Each element is a CLP variable in the
% range 0..1, specifying whether the hand has that card in it.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Hand structure
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% default_knowledge(?HandInfo)
% Initializes HandInfo with the bare minimum knowledge of the structure
% of a hand.
default_knowledge(dealt(Spades, Hearts, Diamonds, Clubs)) :-
length(Spades, 13),
length(Hearts, 13),
length(Diamonds, 13),
length(Clubs, 13),
append([Spades, Hearts, Diamonds, Clubs], AllCards),
AllCards ins 0..1,
sum(AllCards, #=, 13).
% default_everyone(?SelfInfo, ?LeftInfo, ?PartnerInfo, ?RightInfo)
% Initializes a HandInfo for each player's hand, and assures that
% their individual holdings are disjoint.
default_everyone(SelfInfo, LeftInfo, PartnerInfo, RightInfo) :-
maplist(default_knowledge, [SelfInfo, LeftInfo, PartnerInfo, RightInfo]),
maplist(all_cards, [SelfInfo, LeftInfo, PartnerInfo, RightInfo],
[AllSelf, AllLeft, AllPartner, AllRight]),
disjoint_holdings(AllSelf, AllLeft, AllPartner, AllRight).
% disjoint_holdings(?AllSelf, ?AllLeft, ?AllPartner, ?AllRight)
% Given four lists of all-cards, assures that each card's CLP variable
% is 1 in exactly one of the lists.
disjoint_holdings([], [], [], []) :- !.
disjoint_holdings([SelfH | SelfT], [LeftH | LeftT], [PartnerH | PartnerT], [RightH | RightT]) :-
SelfH + LeftH + PartnerH + RightH #= 1,
disjoint_holdings(SelfT, LeftT, PartnerT, RightT).
% fully_determined(+HandInfo)
% Succeeds if all CLP variables in the hand have been fully determined.
fully_determined(HandInfo) :-
all_cards(HandInfo, AllCards),
forall(member(CardVar, AllCards),
integer(CardVar)).
% not_fully_determined(+HandInfo)
% Succeeds if there is at least one CLP variable in the hand that has
% not yet been fully determined.
not_fully_determined(HandInfo) :-
all_cards(HandInfo, AllCards),
member(CardVar, AllCards),
fd_var(CardVar).
% rank_index(+Rank, ?Index)
% Looks up the sublist index for a card of the specified rank; this is
% where the card's CLP variable will be in the proper suit's sublist in
% a hand information term.
rank_index(ace, 0) :- !.
rank_index(king, 1) :- !.
rank_index(queen, 2) :- !.
rank_index(jack, 3) :- !.
rank_index(Rank, Index) :-
Rank =< 10,
Rank >= 2,
Index is 14 - Rank.
% all_cards(+HandInfo, ?AllCards)
% Concatenates the sublists for each suit into a single list of all
% 52 CLP variables, in the order spades, hearts, diamonds, clubs.
all_cards(dealt(Spades, Hearts, Diamonds, Clubs), AllCards) :-
append([Spades, Hearts, Diamonds, Clubs], AllCards).
% suit_sublist(+Suit, +HandInfo, ?Sublist)
% Looks up the sublist in a hand description that corresponds to the
% specified suit.
suit_sublist(spades, dealt(Spades, _, _, _), Spades).
suit_sublist(hearts, dealt(_, Hearts, _, _), Hearts).
suit_sublist(diamonds, dealt(_, _, Diamonds, _), Diamonds).
suit_sublist(clubs, dealt(_, _, _, Clubs), Clubs).
% card_flag(+HandInfo, +Card, ?Flag)
% Looks up the CLP variable in a hand that corresponds to the given card.
card_flag(HandInfo, card(Suit, Rank), Flag) :-
suit_sublist(Suit, HandInfo, Sublist),
rank_index(Rank, Index),
nth0(Index, Sublist, Flag).
% interpret_hand(+Cards, +HandInfo)
% Assigns concrete values to HandInfo's CLP variables to match the list
% of known cards belonging to that hand.
interpret_hand([], _) :- !.
interpret_hand([Card | Cards], HandInfo) :-
card_flag(HandInfo, Card, Flag),
Flag #= 1,
interpret_hand(Cards, HandInfo).
% expand_suit(?Suit, ?ListOfRanks, ?ListOfCards)
% Takes a suit and a list of card ranks and converts that into a list
% of card(Suit, Rank) terms. This predicate is primarily for
% convenience when writing test cases, to mimimize the verbosity of
% test inputs.
expand_suit(_, [], []) :- !.
expand_suit(Suit, [Rank | Ranks], [card(Suit, Rank) | Cards]) :-
expand_suit(Suit, Ranks, Cards).
% expand_all_suits(+SpadeRanks, +HeartRanks, +DiamondRanks, +ClubRanks, ?ListOfCards)
% Takes four lists of ranks, one for each suit, and creates a list
% of card(Suit, Rank) terms that correspond to them. This predicate
% is primarily for convenience when writing test cases, to minimize
% the verbosity of test inputs.
expand_all_suits(SpadeRanks, HeartRanks, DiamondRanks, ClubRanks, ListOfCards) :-
expand_suit(spades, SpadeRanks, SpadeCards),
expand_suit(hearts, HeartRanks, HeartCards),
expand_suit(diamonds, DiamondRanks, DiamondCards),
expand_suit(clubs, ClubRanks, ClubCards),
append([SpadeCards, HeartCards, DiamondCards, ClubCards], ListOfCards).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Basics of hand evaluation
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% points(+HandInfo, ?Points)
% Tallies the total points in a hand, if no trump suit exists.
points(HandInfo, Points) :-
high_card_points(HandInfo, HCP),
long_card_points(HandInfo, LCP),
Points #= HCP + LCP.
% points(+HandInfo, +TrumpSuit, ?Points)
% Tallies the total points in a hand, in a trump contract.
points(HandInfo, TrumpSuit, Points) :-
points(HandInfo, NTP),
trump_points(HandInfo, TrumpSuit, TP),
Points #= NTP + TP.
% high_card_points(+HandInfo, ?HCP)
% Tallies the high-card points in a hand.
high_card_points(HandInfo, HCP) :-
maplist(high_card_points(HandInfo), [spades, hearts, diamonds, clubs], SuitHCPs),
sum(SuitHCPs, #=, HCP).
% high_card_points(+HandInfo, +Suit, ?HCP)
% Tallies the high-card points within a single suit in a hand.
high_card_points(HandInfo, Suit, HCP) :-
suit_sublist(Suit, HandInfo, [Ace, King, Queen, Jack | _]),
HCP #= Ace * 4 + King * 3 + Queen * 2 + Jack.
% long_card_points(+HandInfo, ?LCP)
% Tallies the long-card points in a hand.
long_card_points(HandInfo, LCP) :-
maplist(long_card_points(HandInfo), [spades, hearts, diamonds, clubs], SuitLCPs),
sum(SuitLCPs, #=, LCP).
% long_card_points(+HandInfo, +Suit, ?LCP)
% Tallies the long-card points within a single suit in a hand.
long_card_points(HandInfo, Suit, LCP) :-
suit_length(HandInfo, Suit, Length),
LCP #= max(Length - 4, 0).
% trump_points(+HandInfo, +TrumpSuit, ?TP)
% Tallies points from short non-trump suits if playing in trumps.
trump_points(HandInfo, TrumpSuit, TP) :-
maplist(trump_points(HandInfo, TrumpSuit), [spades, hearts, diamonds, clubs], SuitTPs),
sum(SuitTPs, #=, TP).
% trump_points(+HandInfo, +TrumpSuit, +InSuit, ?TP)
% Tallies points from short non-trump suits in a single suit, if
% playing in trumps.
trump_points(_, TrumpSuit, TrumpSuit, 0) :- !.
trump_points(HandInfo, _, InSuit, TP) :-
suit_length(HandInfo, InSuit, Length),
TP #= max(3 - Length, 0).
% demi_quick_tricks(+HandInfo, ?DQT)
% Tallies the demi-quick-tricks in a hand -- i.e., quick tricks,
% measured in units of 1/2 quick tricks, since the CLP solver only
% handles integers, not fractional values. For example, DQT = 3
% means 1 1/2 quick tricks.
demi_quick_tricks(HandInfo, DQT) :-
maplist(demi_quick_tricks(HandInfo), [spades, hearts, diamonds, clubs], SuitDQTs),
sum(SuitDQTs, #=, DQT).
% demi_quick_tricks(+HandInfo, +Suit, ?DQT)
% Tallies the demi-quick-tricks in a single suit.
demi_quick_tricks(HandInfo, Suit, DQT) :-
suit_sublist(Suit, HandInfo, SuitedCards),
SuitedCards = [Ace, King, Queen | _],
suit_length(HandInfo, Suit, Length),
DQT in 0..4,
DQT #= 4 #<==> (Ace #= 1 #/\ King #= 1), % AK = 2 qt
DQT #= 3 #<==> (Ace #= 1 #/\ King #= 0 #/\ Queen #= 1), % AQ = 1.5 qt
DQT #= 2 #<==> ((Ace #= 1 #/\ King #= 0 #/\ Queen #= 0) #\/ % A = 1 qt
(Ace #= 0 #/\ King #= 1 #/\ Queen #= 1)), % KQ = 1 qt
DQT #= 1 #<==> (Ace #= 0 #/\ King #= 1 #/\ Queen #= 0 #/\ Length #>= 2), % Kx = 0.5 qt
DQT #= 0 #<==> (Ace #= 0 #/\ King #= 0). % else = 0 qt
% suit_length(+HandInfo, +Suit, ?Length)
% Determines the length of a suit in the hand.
suit_length(HandInfo, Suit, Length) :-
suit_sublist(Suit, HandInfo, SuitedCards),
sum(SuitedCards, #=, Length).
% count_cards(+HandInfo, +Rank, ?Count)
% Count the number of cards of a given rank held in the hand.
count_cards(HandInfo, Rank, Count) :-
findall(Flag, card_flag(HandInfo, card(_, Rank), Flag), Flags),
sum(Flags, #=, Count).
% tricks(+HandInfo, ?Tricks)
% Count the tricks a hand is expected to get.
%
% FIXME: Constrain HandInfo based on information provided by Tricks.
tricks(HandInfo, Tricks) :-
fully_determined(HandInfo),
maplist(tricks(HandInfo), [spades, hearts, diamonds, clubs], SuitTricks),
sum(SuitTricks, #=, Tricks).
tricks(HandInfo, _) :-
not_fully_determined(HandInfo).
% tricks(+HandInfo, +Suit, ?Tricks)
% Count the tricks within a suit a hand is expected to get.
%
% FIXME: Constrain HandInfo based on information provided by Tricks.
tricks(HandInfo, Suit, Tricks) :-
fully_determined(HandInfo),
suit_sublist(Suit, HandInfo, SuitedCards),
sublist_to_holdings(SuitedCards, Held, NotHeld),
held_tricks(Held, NotHeld, Tricks).
tricks(HandInfo, _, _) :-
not_fully_determined(HandInfo).
% sublist_to_holdings(+SuitedCards, ?Held, ?NotHeld)
% Convert a list of CLP variables into a list of the card ranks held and
% not held.
sublist_to_holdings(SuitedCards, Held, NotHeld) :-
sublist_to_holdings(SuitedCards, [ace, king, queen, jack, 10, 9, 8, 7, 6, 5, 4, 3, 2], Held, NotHeld).
% sublist_to_holdings(+SuitedCards, +Ranks, ?Held, ?NotHeld)
% Convert a list of CLP variables into a list of the card ranks held and
% not held. Ranks is the name/value of each rank listed in SuitedCards.
sublist_to_holdings([], [], [], []).
sublist_to_holdings([0 | SuitedCards], [Rank | Ranks], Held, [Rank | NotHeld]) :-
sublist_to_holdings(SuitedCards, Ranks, Held, NotHeld).
sublist_to_holdings([1 | SuitedCards], [Rank | Ranks], [Rank | Held], NotHeld) :-
sublist_to_holdings(SuitedCards, Ranks, Held, NotHeld).
% held_tricks(+Held, +NotHeld, ?Tricks)
% Determine the number of tricks that can be gotten in a suit, by
% simulating "expected" play in that suit. The two lists of cards
% must be sorted from greatest to least. This relaxes the pessimism
% of held_tricks/4 a bit.
held_tricks(Held, NotHeld, Winners) :-
held_tricks(Held, NotHeld, Winners, Losers),
Losers #< 3 #\/ Winners + Losers #< 7.
held_tricks(Held, NotHeld, Tricks) :-
held_tricks(Held, NotHeld, Winners, Losers),
Losers #>= 3,
Winners + Losers #>= 7,
Tricks #= Winners + 1.
% held_tricks(+Held, +NotHeld, ?Winners, ?Losers)
% Determine the number of tricks that will be won and lost in a suit,
% by simulating "expected" play in that suit. The two lists of cards
% must be sorted from greatest to least. Winners + Losers will be
% the length of Held. The simulation is pessimistic, assuming the
% enemies will always play a higher card if they can.
held_tricks([], _, 0, 0).
held_tricks(Held, [], Winners, 0) :-
length(Held, Winners).
held_tricks([Held | RestHeld], [NotHeld], Winners, Losers) :-
rank_lt(Held, NotHeld),
held_tricks(RestHeld, [], Winners, RestLosers),
Losers #= RestLosers + 1.
held_tricks([Held | RestHeld], [NotHeld], Winners, Losers) :-
rank_lt(NotHeld, Held),
held_tricks(RestHeld, [], RestWinners, Losers),
Winners #= RestWinners + 1.
held_tricks([Held | RestHeld], [NotHeld | RestNotHeld], Winners, Losers) :-
rank_lt(Held, NotHeld),
append(Remaining, [_], RestNotHeld),
held_tricks(RestHeld, Remaining, Winners, RestLosers),
Losers #= RestLosers + 1.
held_tricks([Held | RestHeld], [NotHeld | RestNotHeld], Winners, Losers) :-
rank_lt(NotHeld, Held),
append(Remaining, [_, _], [NotHeld | RestNotHeld]),
held_tricks(RestHeld, Remaining, RestWinners, Losers),
Winners #= RestWinners + 1.
% supp_tricks(+HandInfo, +PartnerSuit, ?Tricks)
% Determine the number of tricks that can be gotten with a hand after
% partner has made a preemptive opening in the given suit. Basically,
% quick tricks in the off suits, ace and/or king in partner's suit,
% and if 3-card support for partner's suit, voids/singletons in off
% suits.
supp_tricks(HandInfo, PartnerSuit, Tricks) :-
maplist(supp_demi_tricks(HandInfo, PartnerSuit), [spades, hearts, diamonds, clubs], SuitDemiTricks),
sum(SuitDemiTricks, #=, DemiTricks),
Tricks #= DemiTricks / 2.
% supp_demi_tricks(+HandInfo, +PartnerSuit, +Suit, ?DemiTricks)
% Determine the number of tricks that can be gotten with a single suit
% after partner has made a preemptive opening. "Demi" in order to
% represent half tricks with integer CLP variables.
%
% FIXME: Represent in a more CLP-way instead of having two cases for
% the off-suit condition.
supp_demi_tricks(HandInfo, PartnerSuit, PartnerSuit, DemiTricks) :-
suit_sublist(PartnerSuit, HandInfo, [Ace, King | _]),
DemiTricks #= 2 * (Ace + King).
supp_demi_tricks(HandInfo, PartnerSuit, Suit, 2) :-
dif(PartnerSuit, Suit),
suit_length(HandInfo, PartnerSuit, SupportLength),
SupportLength #>= 3,
suit_length(HandInfo, Suit, Length),
Length #=< 1.
supp_demi_tricks(HandInfo, PartnerSuit, Suit, DemiTricks) :-
dif(PartnerSuit, Suit),
suit_length(HandInfo, PartnerSuit, SupportLength),
suit_length(HandInfo, Suit, Length),
SupportLength #< 3 #\/ Length #> 1,
demi_quick_tricks(HandInfo, Suit, DemiTricks).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% More sophisticated hand evaluation
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% distribution(+HandInfo, +Distributions)
% Succeeds if the hand's distribution matches Distributions, which is one
% or more A-B-C-D values. Distribution can either be a single item or
% a list of candidates.
distribution(HandInfo, A-B-C-D) :-
distribution(HandInfo, [A-B-C-D]),
!.
distribution(HandInfo, Distributions) :-
maplist(suit_length(HandInfo), [spades, hearts, diamonds, clubs], SuitLengths),
maplist(expand_distribution, Distributions, PermSets),
union_permutation_sets(PermSets, PermSet),
tuples_in([SuitLengths], PermSet).
% deny_distribution(+HandInfo, +Distributions)
% Succeeds if the hand's distribution does *not* match Distributions,
% which is one or more A-B-C-D values. Distribution can either be a
% single item or a list of candidates.
deny_distribution(HandInfo, A-B-C-D) :-
deny_distribution(HandInfo, [A-B-C-D]),
!.
deny_distribution(HandInfo, Distributions) :-
maplist(suit_length(HandInfo), [spades, hearts, diamonds, clubs], SuitLengths),
maplist(expand_distribution, Distributions, PermSets),
union_permutation_sets(PermSets, PermSet),
all_distributions(AllPerm),
ord_subtract(AllPerm, PermSet, AllowedPermSet),
tuples_in([SuitLengths], AllowedPermSet).
% expand_distribution(?Distribution, ?PermSet)
% Converts between a distribution in A-B-C-D format (used in the
% interface) and an ordered set of permutations of [A, B, C, D].
expand_distribution(A-B-C-D, PermSet) :-
findall(Perm, permutation([A, B, C, D], Perm), PermList),
list_to_ord_set(PermList, PermSet).
% union_permutation_sets(+PermSets, -PermSet)
% union_permutation_sets(+PermSets, +Acc, -PermSet)
% Computes the set-union of a list of permutation sets. The
% non-accumulator version is a wrapper.
union_permutation_sets(PermSets, PermSet) :-
union_permutation_sets(PermSets, [], PermSet).
union_permutation_sets([], Acc, Acc) :- !.
union_permutation_sets([Head | Tail], Acc, PermSet) :-
ord_union(Head, Acc, NewAcc),
union_permutation_sets(Tail, NewAcc, PermSet).
% all_distributions(-AllPerms)
% Computes a list of all possible distribution permutations.
all_distributions(AllPerms) :-
length(Dist, 4),
Dist ins 0..13,
sum(Dist, #=, 13),
findall(Dist, label(Dist), AllPermsList),
list_to_ord_set(AllPermsList, AllPerms).
% balanced(+HandInfo)
% Succeeds if the hand's distribution is balanced: 4-3-3-3, 4-4-3-2, or
% 5-3-3-2. In other words, no voids, no singletons, and at most one
% doubleton. Another horrid implementation.
balanced(HandInfo) :-
distribution(HandInfo, [4-3-3-3, 4-4-3-2, 5-3-3-2]).
% balanced(+HandInfo)
% Succeeds if the hand's distribution is balanced (4-3-3-2, 4-4-3-2, or
% 5-3-3-2) or semi-balanced (5-4-2-2 or 6-3-2-2).
balanced_or_semi(HandInfo) :-
distribution(HandInfo, [4-3-3-3, 4-4-3-2, 5-3-3-2, 5-4-2-2, 6-3-2-2]).
% unbalanced(+HandInfo)
% Succeeds if the hand's distribution is unbalanced (x-x-1-1 or x-x-2-1).
unbalanced(HandInfo) :-
distribution(HandInfo, [
10-1-1-1, 9-2-1-1, 8-3-1-1, 7-4-1-1, 6-5-1-1,
8-2-2-1, 7-3-2-1, 6-4-2-1, 5-5-2-1]).
% strong_suit(+HandInfo, +Suit)
% Succeeds if the suit's cards can be considered "strong". This isn't
% well-defined, so we'll consider a suit strong if it has high HCP.
strong_suit(HandInfo, Suit) :-
high_card_points(HandInfo, Suit, HCP),
HCP #>= 6.
% not_strong_suit(+HandInfo, +Suit)
% Succeeds if strong_suit would fail.
not_strong_suit(HandInfo, Suit) :-
high_card_points(HandInfo, Suit, HCP),
HCP #< 6.
% very_strong_suit(+HandInfo, +Suit)
% Succeeds if the suit's cards can be considered "very strong". This isn't
% well-defined, so we'll consider a suit strong if it has very high HCP.
very_strong_suit(HandInfo, Suit) :-
high_card_points(HandInfo, Suit, HCP),
HCP #>= 7.
% not_very_strong_suit(+HandInfo, +Suit)
% Succeeds if very_strong_suit would fail.
not_very_strong_suit(HandInfo, Suit) :-
high_card_points(HandInfo, Suit, HCP),
HCP #< 7.
% stopper(+HandInfo, +Suit)
% Succeeds if a hand has a stopper in the specified suit.
stopper(HandInfo, Suit) :-
suit_sublist(Suit, HandInfo, [Ace, King, Queen | _]),
Ace + King + Queen #>= 1.
% no_stopper(+HandInfo, +Suit)
% Succeeds if a hand has no stoppers in the specified suit.
no_stopper(HandInfo, Suit) :-
suit_sublist(Suit, HandInfo, [Ace, King, Queen | _]),
Ace + King + Queen #= 0.
% game(?Level, ?Suit)
% Succeeds if bid(Level, Suit) is a game-level contract.
game(5, clubs).
game(5, diamonds).
game(4, hearts).
game(4, spades).
game(3, no_trump).
% unfavorable_vuln(?Vuln)
% Succeeds if the vulnerability is unfavorable.
unfavorable_vuln(vuln(yes, no)).
% equal_vuln(?Vuln)
% Succeeds if the vulnerability is equal.
equal_vuln(vuln(no, no)).
equal_vuln(vuln(yes, yes)).
% favorable_vuln(?Vuln)
% Succeeds if the vulnerability is favorable.
favorable_vuln(vuln(no, yes)).
% preempt_level(+Vuln, ?Tricks, ?Level)
% Succeeds if a preemptive bid at Level is possible with the given number
% of Tricks and the current vulnerability.
preempt_level(Vuln, Tricks, Level) :-
preempt_overbid(Vuln, Overbid),
Tricks + Overbid #= Level + 6.
% preempt_overbid(+Vuln, ?Overbid)
% Determine how many tricks to overbid for picking the level of a
% preemptive opening.
preempt_overbid(Vuln, 2) :-
unfavorable_vuln(Vuln).
preempt_overbid(Vuln, 3) :-
equal_vuln(Vuln).
preempt_overbid(Vuln, Overbid) :-
favorable_vuln(Vuln),
Overbid in 3..5.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Suit ordering
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% suit_lt(?SuitA, ?SuitB)
% Succeeds if SuitA < SuitB.
suit_lt(SuitA, SuitB) :-
suit_lt_imm(SuitA, SuitB).
suit_lt(SuitA, SuitB) :-
suit_lt_imm(SuitA, SuitC),
suit_lt(SuitC, SuitB).
% suit_lt_imm(?SuitA, ?SuitB)
% Succeeds if SuitA < SuitB and there is no SuitC such that
% SuitA < SuitC < SuitB.
suit_lt_imm(clubs, diamonds).
suit_lt_imm(diamonds, hearts).
suit_lt_imm(hearts, spades).
% suit_le(?SuitA, ?SuitB)
% Succeeds if SuitA <= SuitB.
suit_le(Suit, Suit).
suit_le(SuitA, SuitB) :-
suit_lt(SuitA, SuitB).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Rank ordering
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% rank_lt(?RankA, ?RankB)
% Succeeds if RankA < RankB.
rank_lt(RankA, RankB) :-
rank_lt_imm(RankA, RankB).
rank_lt(RankA, RankB) :-
rank_lt_imm(RankA, RankC),
rank_lt(RankC, RankB).
% rank_lt_imm(?RankA, ?RankB)
% Succeeds if RankA < RankB and there is no RankC such that
% RankA < RankC < RankB.
rank_lt_imm(2, 3).
rank_lt_imm(3, 4).
rank_lt_imm(4, 5).
rank_lt_imm(5, 6).
rank_lt_imm(6, 7).
rank_lt_imm(7, 8).
rank_lt_imm(8, 9).
rank_lt_imm(9, 10).
rank_lt_imm(10, jack).
rank_lt_imm(jack, queen).
rank_lt_imm(queen, king).
rank_lt_imm(king, ace).
% vim: ft=prolog