Reputation: 101
In Prolog, how do I randomly pick 2 values from a set of 52 cards? i.e. Player1(X,Y), X will be any card out of 52 and Y another and they have to be different. Player2 will get the same treatment and his cards must differ from the ones already picked. Thanks.
Upvotes: 0
Views: 209
Reputation: 5034
If you want to draw random cards repeatedly, it is probably best to shuffle the whole list in one go, and then simply pick cards from the front. Some Prologs (e.g. ECLiPSe) have a library predicate for this, so you could just call
lists:shuffle(Cards, [Card1,Card2|Rest]).
If you want to write the shuffling yourself, here is a neat trick:
shuffle(Xs, Rs) :-
add_random_keys(Xs, KXs), % add random key to each list element
keysort(KXs, KRs), % sort by keys, perturbing original order
strip_keys(KRs, Rs). % remove the keys again
add_random_keys([], []).
add_random_keys([X|Xs], [K-X|KXs]) :-
random(K),
add_random_keys(Xs, KXs).
strip_keys([], []).
strip_keys([_K-X|KXs], [X|Xs]) :-
strip_keys(KXs, Xs).
Upvotes: 1
Reputation: 7229
Basically, the task is to sample 4 different elements from set [1..52].
In Python, for example, there is a standard function for this, and the code would be
random.sample(range(1, 52 + 1), 4)
I've looked how Python implements random.sample(range(1..N + 1), K)
- it's basically just K steps of generating random numbers [1..N], and on every step just keep trying generate random numbers while currently generated number already selected in a previous step. It looks inefficient, but it's probably needed to archive uniformity of the distribution.
Let's create a similar (but simplified) sample
predicate for Prolog.
sample(N, K, Sample) :-
sample(N, K, [], Sample).
sample(_, 0, _, []).
sample(N, K, Selected, [X | Rest]) :-
K > 0,
new_random_index(N, Selected, X),
NewSelected = [X | Selected],
NewK is K - 1,
sample(N, NewK, NewSelected, Rest).
new_random_index(N, Selected, X) :-
(
% Can be implementation-specific. Works in B-Prolog and ECLiPSe CLP.
X is (random mod N) + 1,
\+ membchk(X, Selected)
;
new_random_index(N, Selected, X)
).
Couple of test runs:
| ?- sample(52, 4, Sample).
sample(52, 4, Sample).
Sample = [40,23,38,44] ?
yes
| ?- sample(52, 4, Sample).
sample(52, 4, Sample).
Sample = [2,28,39,17] ?
yes
Efficiency of the program can be greatly improved if membchk
test changed to a predicate that test membership using sets, not lists. But this is very implementation-specific.
Upvotes: 1
Reputation: 60034
In SWI-Prolog I would do a pick_card/3
pick_card(Cards, Card, Rest) :-
length(Cards, N), random_between(1, N, R), nth1(R, Cards, Card, Rest).
and apply it 2 times
pick_two_cards(Cards, C1,C2, Rest) :-
pick_card(Cards, C1, R1),
pick_card(R1, C2, Rest).
Upvotes: 1