-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeck.pl
313 lines (294 loc) · 9.01 KB
/
deck.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/** <module> Predicates for handling a deck of card
This module contains configuration and predicates responsible for handling
deck(s) of cards through the game.
@author Kacper Sokol
@license GPL
*/
:- module( deck,
[decks/1,
shuffleMode/1,
deck/1,
shuffle/3,
initDeal/3,
score/2,
getPiles/3,
addCard/5]
).
:- use_module(blackJack). % players
%% decks(-N) is det.
%
% Defines the number of decks used in the game.
%
% @param N Number of decks used in the game.
%
decks(1).
%% shuffleMode(-Type) is det.
%
% Defines the type of deck shuffling used in the game; can be either:
% * deterministic, or
% * random.
%
% The deterministic shuffling splits the pile in half randomly selects the half
% and merge halves interchangeably. The random shuffling also splits the deck
% but then tosses a coin on each card merge which card should go first.
%
% @param Type Type of shuffling used in the game.
%
shuffleMode(random).
%% suits(-Type) is det.
%
% Defines suits of cards:
% * clubs(♣),
% * diamonds(♦),
% * hearts(♥) and
% * spades(♠).
%
% @param Type Type of card's suit.
%
suits(♦).
suits(♥).
suits(♠).
suits(♣).
%% ranks(-Rank) is det.
%
% Defines the possible ranks of card: ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, jack,
% queen, king.
%
% @param Rank Rank of card.
%
ranks( ace).
ranks( 2).
ranks( 3).
ranks( 4).
ranks( 5).
ranks( 6).
ranks( 7).
ranks( 8).
ranks( 9).
ranks( 10).
ranks( jack).
ranks(queen).
ranks( king).
%% card(+Suit, +Rank) is det.
%% card(-Suit, -Rank) is nondet.
%
% Represents a card.
%
% @param Suit Suit of card.
% @param Rank Rank of card.
%
card( Suit, Rank ) :-
suits( Suit ), ranks( Rank ).
%% value(-Value, :Card) is det.
%
% Converts card predicate to its value. The `Card` variable is given by
% `card(+S, +R)` predicate with: `S` - Suit of card; `R` - Rank of card.
%
% @param Value Value of given card.
% @param Card The card.
%
value( Value, card( S, R ) ) :-
( S = ♣; S = ♦; S = ♥; S = ♠ ), !,
( R = ace -> ( Value is 11 ; Value is 1 )
; R = jack -> Value is 10
; R = queen -> Value is 10
; R = king -> Value is 10
; otherwise -> Value is R
).
%% score(-Score, +Hand) is det.
%
% Calculates value of player's hand.
%
% @param Score Value of given hand.
% @param Hand A list of `card` predicates.
%
score( Score, Hand ) :- % Hand
score( Score, Hand, 0 ).
score( Score, [Card|Hand], V ) :-
value(Q, Card),
Vn is V + Q,
score(Score, Hand, Vn).
score( Score, [], V ) :-
Score is V.
%% deck(-Deck) is det.
%
% Generates deck(s) based on their predefined number (`decks` parameter in
% `deck` module).
%
% @param Deck Deck(s) generated based on system parameters.
%
deck(Deck) :-
decks(No),
deck(Deck, [], No).
deck(Deck, Holder, 0) :-
append(Holder, [], Deck),
!.
deck(Deck, Holder, No) :-
Np is No - 1,
findall(card(S,R), card(S, R), New),
append(Holder, New, NewHolder),
deck(Deck, NewHolder, Np).
%% woN(-Out, +E, +In) is det.
%
% Returns the `In` list without element `E`.
%
% @param Out The list with specified element removed.
% @param E An element ot be removed from the input list.
% @param In List with the element to be removed.
%
woN(Out, E, In) :-
select(E, In, Out), !.
%% initDeal(-Table, -NewDeck, +Deck) is det.
%
% Deals the cards from the `Deck` to all players defined by `players` predicate
% defined in `blackJack` module. The initial deal consists of two cards given
% to each player & two cards for the house. This predicate returns the new deck
% without just dealt cards and the table i.e. list of lists containing cards of
% each player.
%
% @param Table List of cards held by all player (list of lists).
% @param NewDeck Deck(s) of card with removed cards that were used for initial
% deal.
% @param Deck Deck(s) of cards used for initial deal.
%
initDeal(Table, NewDeck, Deck) :-
players(P),
userPlayer(Q),
AtTable is P + 1 + Q,
%% CardDeals is AtTable * 2, % deal 2 cards for each player
initDeal(Table, NewDeck, [], Deck ,Deck, AtTable, 1).
%
initDeal(Table, Deck, Table, Deck, _, AtTable, Current) :-
AtTable is Current - 1, !.
initDeal(Table, NewDeck, TemporaryTable, DissortingD, Deck, AtTable, Current) :-
nth1(Current, Deck, C1), % draw element N
Current1 is Current + AtTable, % get new index
nth1(Current1, Deck, C2), % draw next card
woN(Deck1, C1, DissortingD), % delete first card
woN(Deck2, C2, Deck1), % delete first card
Next is Current + 1, % new current player
append([C1], [C2], Person), % create one hand
append(TemporaryTable, [Person], Table2),% generate i-th player
initDeal(Table, NewDeck, Table2, Deck2, Deck, AtTable, Next).
%% shuffle(-Shuffled, +Deck, +N) is det.
%
% Manages shuffling: decides which shuffling algorithm to use based on type
% defined by `shuffleMode` predicate defined in `deck` package.
%
% @param Shuffled The shuffled deck (list of `card` predicate).
% @param Deck The deck to be shuffled (list of `card` predicate).
% @param N Number of shuffles.
%
shuffle(Shf, Shf, 0) :-
!.
shuffle(Shuffled, Deck, N) :-
shuffleMode(Mode),
proper_length(Deck, Len),
Half is Len / 2,
getPiles(A, _, Half),
A1 is A + 1,
split(P1, P2, Deck, A1), % get two piles
% random or deterministic
( Mode = random -> rifleRan(Forward, P1, P2)
; Mode = deterministic -> rifleDet(Forward, P1, P2)
),
N1 is N - 1,
shuffle( Shuffled, Forward, N1 ).
%% rifleDet(-Out, +A, +B) is det.
%
% Rifle-shuffle two piles of cards deterministically: split the deck into two
% parts and merge them one by one. The only random bit here is whether the
% shuffle starts with pile A or pile B.
%
% @param Out Merged deck (list of predicates `card`).
% @param A 1st part of deck to be merged (list of predicates `card`).
% @param B 2nd part of deck to be merged (list of predicates `card`).
%
rifleDet(Out, A, B) :-
random(0, 2, Rand), % decide whether left pile goes on top or bottom,
( Rand = 0 -> rifleDet(Out, [], A, B)
; Rand = 1 -> rifleDet(Out, [], B, A)
).
rifleDet(Out, Em, [A1|A2], [B1|B2]) :-
append(Em, [A1], O1),
append(O1, [B1], O2),
rifleDet(Out, O2, A2, B2).
rifleDet(Out, Em, [], [B1|B2]) :-
append(Em, [B1], O),
rifleDet(Out, O, [], B2).
rifleDet(Out, Em, [A1|A2], []) :-
append(Em, [A1], O),
rifleDet(Out, O, A2, []).
rifleDet(Out, Out, [], []) :-
!.
%% rifleRan(-Out, +A, +B) is det.
%
% Rifle-shuffle two piles of cards with *random* card selection i.e. split the
% deck into two parts and merge them by tossing a coin for each card to decide
% whether it goes on the top or bottom of the merged pile.
%
% @param Out Merged deck (list of predicates `card`).
% @param A 1st part of deck to be merged (list of predicates `card`).
% @param B 2nd part of deck to be merged (list of predicates `card`).
%
rifleRan(Out, A, B) :-
random(0, 2, Rand), % decide whether left pile goes on top or bottom
rifleRan(Out, [], A, B, Rand).
rifleRan(Out, Em, [A1|A2], [B1|B2], 0) :-
append(Em, [A1], O1),
append(O1, [B1], O2),
random(0, 2, Rand),
rifleRan(Out, O2, A2, B2, Rand).
rifleRan(Out, Em, [A1|A2], [B1|B2], 1) :-
append(Em, [B1], O1),
append(O1, [A1], O2),
random(0, 2, Rand),
rifleRan(Out, O2, A2, B2, Rand).
rifleRan(Out, Em, [], [B1|B2], Rand) :-
append(Em, [B1], O),
rifleRan(Out, O, [], B2, Rand).
rifleRan(Out, Em, [A1|A2], [], Rand) :-
append(Em, [A1], O),
rifleRan(Out, O, A2, [], Rand).
rifleRan(Out, Out, [], [], _) :-
!.
%% getPiles(-A, -B, +Half) is det.
%
% Generates two piles counters: if the number of cards in the deck is **odd**
% then it is impossible to split it equally. This predicate takes a float
% number and outputs two numbers one a half bigger and one a half smaller to
% create two integers. In general 2*`Half`=`A`+`B`.
%
% @param A Number rounded to an integer based on above rule.
% @param B Number rounded to an integer based on above rule.
% @param Half A number to be rounded based on above rule.
%
getPiles(A, B, Half) :-
float(Half),
random(0,2,Rand), % decide whether round up left or right pile
( Rand = 0 -> A is Half - 0.5, B is Half + 0.5
; Rand = 1 -> A is Half + 0.5, B is Half - 0.5
),
!.
getPiles(Half, Half, Half) :-
integer(Half).
%% addCard(-NewDeal, -NewDeck, +OldDeck, +OldDeal, +PlayerNo) is det.
%
% Deals additional card to player with sequential number `PlayerNo`. After the
% deal it returns new deck and new hand of given player.
%
% @param NewDeal List of cards held by all player (list of lists) **after**
% adding a new card.
% @param NewDeck **New** list of cards remaining in the deck after current
% round.
% @param OldDeck Current list of cards in the deck after.
% @param OldDeal List of cards held by all player (list of lists) **before**
% adding a new card.
% @param PlayerNo An id of the player counting from 1 which is to be dealt a
% new card.
%
addCard(NewDeal, NewDeck, [Card|NewDeck], OldDeal, PlayerNo) :-
split(A, [X|C], OldDeal, PlayerNo), % get sublist
append(X, [Card], Y), % extend sublist
append(A, [Y], Alpha), % put at the same place new list
append(Alpha, C, NewDeal). % put at the same place new list