~ubuntu-branches/ubuntu/oneiric/dds/oneiric

« back to all changes in this revision

Viewing changes to DDS_alg_descr-revE1.txt

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Berg
  • Date: 2008-10-30 23:19:27 UTC
  • mfrom: (1.1.3 upstream) (7.2.1 squeeze)
  • Revision ID: james.westby@ubuntu.com-20081030231927-yvf4f2tmezkl45av
Tags: 1.1.9+ddd105-2
Update watch file with correct dversionmangling.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
Bo Haglund
2
 
Rev. E1,  2008-03-21
3
 
 
4
 
 
5
 
Search Algorithms for a Bridge Double Dummy Solver 
6
 
 
7
 
This description is intended for anyone interested in the inner workings of a bridge double dummy solver (DDS). It describes my solver implemented in the Win32 environment as a DLL.  
8
 
 
9
 
DDS algorithm descriptions already exist, see reference list at the end. However, to my knowledge, no document exists that gives an in depth description of all algorithms covered in this document.
10
 
 
11
 
 
12
 
 
13
 
 
14
 
1.The basic search algorithm
15
 
 
16
 
 The search is based on the zero window search [Pearl 1980]. 
17
 
 Pseudo code for its application on DD solver search is given.
18
 
 Cards searched are described as ”moves” in contrast to cards that are really played.
19
 
 
20
 
 int  Search(posPoint, target, depth) {
21
 
    if (depth==0) {
22
 
        tricks=Evaluate;
23
 
        if  (tricks >= target) 
24
 
            value=TRUE;
25
 
        else
26
 
            value=FALSE;
27
 
        return value;
28
 
    }
29
 
    else {
30
 
        GenerateMoves;
31
 
        if  (player_side_to_move) {
32
 
            value=FALSE;   moveExists=TRUE;
33
 
            while (moveExists) {
34
 
                Make;
35
 
                value=Search(posPoint, target, depth-1);
36
 
                Undo;
37
 
                if  (value==TRUE) 
38
 
        goto searchExit;        /* Cutoff, current move recorded as ”best move” */
39
 
                moveExists=NextMove;
40
 
            }
41
 
        }       /* Opponents to move */
42
 
        else {      
43
 
             value=TRUE;   moveExists=TRUE;
44
 
             while (moveExists) {
45
 
                 Make;
46
 
                 value=Search(posPoint, target, depth-1);
47
 
                 Undo;
48
 
                 if  (value==FALSE) 
49
 
        goto searchExit;        /* Cutoff, current move recorded as ”best move” */
50
 
                 moveExists=NextMove;
51
 
             }
52
 
        }
53
 
    }
54
 
 
55
 
    searchExit:
56
 
    return  value;
57
 
}
58
 
 
59
 
 
60
 
The Search  parameters are:
61
 
posPoint -  a pointer to a structure containing state information for the position (deal) to be searched, e.g. leading hand,  hand-to-play, cards yet to play etc.
62
 
target -  the number of tricks the player must take. 
63
 
depth -  the current search depth.
64
 
 
65
 
Search returns TRUE if the target is reached, otherwise FALSE.
66
 
 
67
 
When Search is called, depth is set to the number of cards left to play minus 4. 
68
 
GenerateMoves generates a list of alternative moves (=cards) that can be played in the initial position whose state data is pointed to by posPoint. For cards that are equivalent (e.g. AK) only the card with highest rank is generated. Card equivalence is reanalyzed after each trick. 
69
 
E.g. if the hand-to-play has AQ in a suit where K was played in a previous trick, then A and Q become equivalents.
70
 
 
71
 
If the side of the player has the move, Search tries to find a move that meets the target, i.e that evaluates to TRUE. If such a move is found, search returns TRUE, and saves the move as ”best”.
72
 
If the other side has the move, Search tries to find a move that defies meeting the target, i.e. that evaluates to FALSE. If such a move is found, search returns FALSE, and saves the move as ”best”.
73
 
 
74
 
Each move in the generated move list is handled by first calling Make, which removes the card from the position state information. Search is then recursively called with a position state that now has excluded the played card, depth has been decremented by one. For each new recursive call to Search, a card is removed from the position state information and depth is decremented. This goes on until depth equals 0 in which case only one trick remains. The outcome of this trick is calculated by Evaluate. If the total number of tricks won by the side of the player then reaches target, Search returns TRUE, otherwise FALSE. This result propagates upwards as Search returns for each level, Undo is called which reinstalls the searched card on this level.
75
 
Finally, Search returns for the top level.
76
 
 
77
 
This basic search algorithm is not powerful enough to terminate the search of a typical 52 cards deal in a reasonable time. To accomplish this, a number of search algorithm enhancements are required, which will be described in the following chapters.  
78
 
 
79
 
The described search algorithm only tells if a predefined target can be reached. It does not tell how many tricks that the side of the player can get. This is accomplished by repeated calls to Search:
80
 
 
81
 
g = guessed number of tricks for side of the player
82
 
iniDepth = number of cards to play minus 4
83
 
upperbound = 13;
84
 
lowerbound = 0;
85
 
do  {
86
 
    if  (g==lowerbound)
87
 
        tricks=g+1;
88
 
    else
89
 
        tricks=g;
90
 
    if  ((Search(posPoint, tricks, iniDepth)==FALSE)  {
91
 
        upperbound=tricks-1;
92
 
        g=upperbound;
93
 
    }
94
 
    else  {
95
 
        lowerbound=tricks;
96
 
        g=lowerbound;
97
 
    }
98
 
}
99
 
while (lowerbound < upperbound);
100
 
g=maximum tricks to be won by side of player.
101
 
 
102
 
 
103
 
 
104
 
2.Overview of the search algorithms used in the DD solver 
105
 
 
106
 
The additional functions in the pseudo code for supporting the search speed enhancements are given in italics.  
107
 
 
108
 
int  Search(posPoint, target, depth) {
109
 
      if (no_move_yet_in_trick)  {
110
 
          TargetTooLowOrHigh;
111
 
          if (target_already_obtained)
112
 
              return TRUE;
113
 
          else if (target_can_no_longer_be_obtained)
114
 
              return FALSE; 
115
 
          QuickTricks;
116
 
          LaterTricks;
117
 
          if  (cutoff_for_player_side) 
118
 
             return TRUE;
119
 
          else if  (cutoff_for_opponent_side)
120
 
             return FALSE;
121
 
          RetrieveTTresult;
122
 
          if (transposition_table_entry_match) {
123
 
             if  (target_reached)
124
 
                return TRUE;
125
 
            else
126
 
                return FALSE;
127
 
         }
128
 
     }
129
 
 
130
 
      if (depth==0) {
131
 
          evalRes=Evaluate;
132
 
          if  (evalRes.tricks >= target) 
133
 
             value=TRUE;
134
 
         else
135
 
             value=FALSE;
136
 
         return value;
137
 
      }
138
 
      else {
139
 
          GenerateMoves; 
140
 
          MoveOrdering;
141
 
          CheckMovesForCutoff;   /* For pseudo-code, see chapter 6 */  
142
 
          if  (player_side_to_move) {
143
 
              value=FALSE;    moveExists=TRUE;
144
 
              while (moveExists) {
145
 
                 Make;
146
 
                 value=Search(posPoint, target, depth-1);       
147
 
                 Undo;
148
 
                 if  (value==TRUE)  {
149
 
        MergeMoveData; 
150
 
        goto searchExit;        /* Cutoff, current move recorded as ”best move” */
151
 
                 }
152
 
                 MergeAllMovesData;
153
 
                 moveExists=NextMove;
154
 
             }
155
 
         }      /* Opponents to move */
156
 
         else {     
157
 
             value=TRUE;   moveExists=TRUE;
158
 
             while (moveExists) {
159
 
                 Make;
160
 
                 value=Search(posPoint, target, depth-1);       
161
 
                 Undo;
162
 
                 if  (value==FALSE)  { 
163
 
        MergeMoveData;
164
 
        goto searchExit;        /* Cutoff, current move recorded as ”best move” */
165
 
                 }
166
 
                 MergeAllMovesData;
167
 
                 moveExists=NextMove;
168
 
             }
169
 
         }
170
 
     }
171
 
     searchExit:
172
 
     AddNewTTentry;
173
 
     return  value;
174
 
 }
175
 
 
176
 
 
177
 
TargetTooLowOrHigh  checks the target value against the number of tricks currently won by      side of the player and against number of tricks left to play.
178
 
It is executed at the beginning of each trick, before any card has been played.
179
 
If number of currently won tricks by player’s side equals or exceeds target, Search returns TRUE.
180
 
If number of currently won tricks by player’s side plus tricks left to play is less than target Search returns FALSE.
181
 
Since possible winning cards for the remaining tricks are irrelevant, no winning cards are backed up at cutoff termination.
182
 
 
183
 
TargetTooLowOrHigh  search enhancement is described e.g. in [Chang].
184
 
 
185
 
QuickTricks determines if the side to move can take one or more sure tricks. E.g. if the hand to move has an Ace in an NT contract, at least one sure trick can be taken.
186
 
It is executed at the beginning of each trick, before any card has been played. A simple quick trick is also executed after the leading card of the trick is played.
187
 
Assuming that the sure tricks are won by the side to move, then the conditions for search cutoff in TargetTooLowOrHigh are again tested to produce further search cutoffs.
188
 
The detailed conditions for determination of sure tricks are described in Chapter 3.
189
 
When quicktricks win by rank, they are backed up at cutoff termination. 
190
 
 
191
 
The idea of QuickTricks is described e.g. in [Chang].
192
 
 
193
 
LaterTricks determines if the opponents of the side to move can take one or more tricks at their turn or later in the play. It is also executed at the beginning of each trick and uses similar criteria for search cutoff as Quicktricks. 
194
 
When quicktricks win by rank, they are backed up at cutoff termination.
195
 
For a detailed description, see Chapter 4.
196
 
 
197
 
RetrieveTTresult scans the set of positions in the transposition table to see if there is a match against the current position. 
198
 
It is executed at the beginning of each trick, before any card has been played. After detection of a  transposition table entry match, the winning ranks necessary in the remaining cards are backed up. 
199
 
For details, see Chapter 7.
200
 
 
201
 
Evaluate  returns evalResult which updates the position state information. evalResult contains:
202
 
evalResult.tricks, the number of tricks won by the side of the player, and
203
 
evalResult.winRank which includes the card in the last trick that won by rank.  
204
 
E.g. if the last trick includes the spades A, Q, 9 and 3, evalResult.winRank returns spade A. But
205
 
if the last trick was won without a win by rank as for spade 5 (leading and winning card), heart A, heart Q, heart 5, no winning rank is returned. 
206
 
 
207
 
Keeping record of cards that win by ranks and subsequently using this information to ignore ranks for other cards is discussed in the Partition Search concept invented by Matthew Ginsberg and described in his paper [Ginsberg]. 
208
 
 
209
 
MoveOrdering. The alternative cards created by MoveGenerate are sorted, with the cards most likely to terminate the search fastest being sorted first in the move list.The allocation of card weights are described in detail in Chapter 5.
210
 
 
211
 
CheckMovesForCutoff checks if any of the moves just generated will lead to a position that can be found in the transposition table. If so, an immediate Search return can be done, saving unnecessary search effort. This is further described in Chapter 6.
212
 
 
213
 
To my knowledge this is not described anywhere for usage in a DDS. It is described in [Plaat et al.] and named Enhanced Transposition Cutoffs.
214
 
 
215
 
At move search cutoff, MergeMoveData collects the union of the backed up accumulated winning ranks and the rank of the made move, assuming it did win by rank. The state data of the position is updated with the collected information.
216
 
 
217
 
MergeAllMovesData collects the union of the backed up accumulated winning ranks, the previous accumulated winning ranks of the alternative moves generated on this depth, and the rank of the made move, assuming it did win by rank. When all alternative moves have been searched without a cutoff, the state data of the position is updated with the collected information.
218
 
 
219
 
The information from MergeMoveData and MergeAllMovesData is later stored in the transposition table and determines which ranks that are essential when RetrieveTTresult scans the set of positions in the transposition table. A match of ranks with the current position is only needed for winning ranks. See Chapter 7.
220
 
 
221
 
AddNewTTentry adds the evaluated position as a new entry in the transposition table. See Chapter 7.
222
 
 
223
 
NextMove filters out all ”small” cards except one per hand/suit combination. A ”small” card is a backed up card that is shown to never win by rank. The rest of the ”small” card moves for the hand/suit combination are never searched, leading to a smaller search tree.
224
 
This search enhancement was suggested by Hans Kuijf [Kuijf]. 
225
 
 
226
 
 
227
 
 
228
 
3.The Quick Tricks cutoff algorithm
229
 
 
230
 
The number of tricks that can immediately be taken by the side to play the leading card of the trick consists of:
231
 
a)The number of tricks that can be taken by the hand-to-play, and
232
 
b)The number of tricks that can be taken by the partner of the hand-to-play
233
 
At return by QuickTricks, the position state information is updated with the winning ranks found. 
234
 
 
235
 
Of course, in order to add b), there must be an entry from the hand-to-play to the partner’s hand.
236
 
 
237
 
For each ”s” (suit) the following is calculated:
238
 
 
239
 
If the hand-to-play is the only hand having cards in s, and the opponents have no trumps (when s is not trumps), the number of quick tricks for s is the suit length of the hand-to-play.
240
 
 
241
 
If the opponents have no trumps, a check is made to see if quick tricks equal to the maximum of the trumps length for leading hand and the partner causes a search cutoff.
242
 
 
243
 
If the hand-to-play has a card in a suit where the partner has a winning rank, and partner is the only hand having cards in s:
244
 
The number of quick tricks for s is the suit length of partner.
245
 
 
246
 
Else:
247
 
If the winning rank is in hand-to-play, and the opponents cannot ruff, the number of quick tricks is incremented by one. Further, if the second best rank is also in hand-to-play, and the opponents cannot still ruff, the quick tricks is again incremented by one.
248
 
 
249
 
Else:
250
 
If the winning rank is in partner and partner has winning rank as entry, the same applies for the partner as for the hand-to-play described above.
251
 
 
252
 
If it is a trump contract, the first suit to be investigated is the trump suit. Then if there are trump suit quick tricks for the side to play, those are cashed and quick tricks incremented accordingly.
253
 
 
254
 
When the other suits are investigated for quick tricks, only the remaining opponent trump cards need to be considered.
255
 
 
256
 
The quick tricks are then summarized from each suit, and the total calculated.
257
 
 
258
 
A simple Quick Tricks algorithm is also executed after the leading card of the trick has been played:
259
 
 
260
 
A quick trick is gained either if the hand-to-play or the partner can win the current trick with the card having the highest rank of the suit played, or if hand-to-play or the partner can win the trick by ruffing.
261
 
 
262
 
The idea to also execute Quick Tricks after the leading card has been played was given by Hans Kuijf [Kuijf].
263
 
 
264
 
 
265
 
 
266
 
4.The Later Tricks cutoff algorithm 
267
 
 
268
 
Check for search cutoff if the opponents to the trick leading hand have at least a sure trick later. 
269
 
 
270
 
If not trump contract:
271
 
 
272
 
1)The opponents have at least a sure trick if for all suits where the trick leading hand has a card, the side of the leading hand does not have the highest rank.
273
 
More than one sure trick can be taken by the opponents if they possess the winning rank for more than one suit, or
274
 
 
275
 
2)Assume that all suits where the side of the trick leading hand has the winning rank give maximum possible number of tricks, i.e. that the sure trick number is the sum of the
276
 
maximum lengths of these suits.
277
 
If this still cannot cause a cutoff for the trick leading side, allocate one sure trick for the opponents side.   
278
 
 
279
 
If trump contract:
280
 
 
281
 
Quick tricks for the opponents of the leading hand are added when the opponents have one or more winning trumps. This idea was given by Pedja Stanojevic [Stanojevic].
282
 
 
283
 
1)   If the opponent side have all the trumps, the number of sure tricks is the maximum suit length
284
 
      length, or
285
 
 
286
 
2)   If the opponent side has the highest trump, they have 1 sure trick. If they also have the second
287
 
      highest trump, they have 2 sure tricks, or
288
 
 
289
 
3)  If the opponent side has the second highest trump plus at least one trump more behind the 
290
 
     hand with the highest trump, the opponent side has 1 sure trick.
291
 
 
292
 
5.The Move Ordering algorithm
293
 
 
294
 
The weight of a card in the move list is affected by the suit and the rank of the card and by the other cards in the same trick.
295
 
The weights of the cards in the move list are used to sort them, with the cards having the highest weight being sorted first in the list. 
296
 
There are 2 exceptions though:
297
 
If the hand-to-play is trick leading hand, cards having the suit decided as best by the move ordering algorithm will occupy the first two positions in the move list, provided that there are at least 2 cards with that suit.
298
 
If the hand-to-play is void in the suit played by leading hand, a card having the suit decided as best by the move ordering algorithm will occupy the first position in the move list.
299
 
 
300
 
A "best move" is maintained for each searched depth. At a search (alpha-beta) cutoff, the move causing the cutoff overwrites the present "best move" for the current depth. When a Transposition Table entry is created, the current best move is stored in that entry if:
301
 
The target is met and the leading hand belongs to the player’s side, or target is not met and the leading hand belongs to the other side. Otherwise the best move is not stored in the Transposition Table entry. 
302
 
At a Transposition Table entry match, its stored best move will be best move for the current search depth.
303
 
 
304
 
By ”card move” in the following pseudo code is meant the card by the hand-to-play that is getting a weight in the move list. The ”card rank” is a value in the range 2-14, corresponding to the card ranks 2 to the Ace. 
305
 
 
306
 
For the determination of the weight, it is calculated whether or not the current card move is a card that wins the current trick for the side of the hand-to-play, assuming that both sides play their optimum cards. 
307
 
 
308
 
 
309
 
Hand-to-play is trick leading hand
310
 
 
311
 
The contribution of the suit to the weight:
312
 
 
313
 
suitWeightDelta = suitBonus - (countLH+countRH) * 2
314
 
 
315
 
If trump contract, and the suit is not trump, then there is a (negative) suitBonus of –12  if 
316
 
LHO is void and LHO has trump card(s), or
317
 
RHO is void and RHO has trump card(s)
318
 
 
319
 
Otherwise, suitBonus = 0.
320
 
 
321
 
countLH = (suit length of LHO) * 4, if LHO is not void in the suit,
322
 
countLH = (depth + 4), if LHO is void in the suit
323
 
 
324
 
countRH = (suit length of RHO) * 4, if RHO is not void in the suit,
325
 
countRH = (depth + 4), if RHO is void in the suit
326
 
 
327
 
Suits are thus favoured where the opponents have as few move alternatives as possible. 
328
 
 
329
 
 
330
 
if (trick winning card move) {
331
 
    if (one of the opponents has a singleton highest rank in the suit)
332
 
        weight = suitWeightDelta + 40 – (rank of card move)
333
 
    else if (hand-to-play has highest rank in suit)  {
334
 
        if (partner has second highest rank in suit)
335
 
            weight = suitWeightDelta + 50 – (rank of card move)
336
 
        else if  (the card move is the card with highest rank in the suit)
337
 
            weight = suitWeightDelta + 31
338
 
        else
339
 
            weight = suitWeightDelta + 19 – (rank of card move)
340
 
    }
341
 
    else if  (partner  has highest rank in suit)  {
342
 
        if (hand-to-play has second highest rank in suit)
343
 
            weight = suitWeightDelta + 50 – (rank of card move)
344
 
        else
345
 
            weight = suitWeightDelta + 35 – (rank of card move)  
346
 
    }
347
 
    else if (card move include equivalent card(s) in the suit)
348
 
        weight = suitWeightDelta + 40 – (rank of card move)
349
 
    else
350
 
        weight = suitWeightDelta + 30 – (rank of card move)
351
 
    if  (the card move  is ”best move” as obtained at search cutoff)
352
 
        weight = weight + 50;
353
 
}
354
 
else {  /* Not a trick winning move */
355
 
    if  (either LHO or RHO has singleton in suit which has highest rank)
356
 
        weight = suitWeightDelta + 20 – (rank of card move)
357
 
    else if (hand-to-play has highest rank in suit)  {
358
 
        if (partner has second highest rank in suit)
359
 
            weight = suitWeightDelta + 35 – (rank of card move)
360
 
        else if  (the card move is the card with highest rank in the suit)
361
 
            weight = suitWeightDelta + 16
362
 
        else
363
 
            weight = suitWeightDelta + 4 – (rank of card move)
364
 
    }
365
 
    else if  (partner  has highest rank in suit)  {
366
 
        if (hand-to-play has second highest rank in suit)
367
 
            weight = suitWeightDelta + 35 – (rank of card move)
368
 
        else
369
 
            weight = suitWeightDelta + 20 – (rank of card move)  
370
 
    }
371
 
    else if  (hand-to-play has second highest rank together with equivalent card(s) in suit)
372
 
        weight = suitWeightDelta + 20 – (rank of card move)
373
 
    else
374
 
        weight = suitWeightDelta + 4 – (rank of card move)
375
 
    if  (the card move  is ”best move” as obtained at search cutoff)
376
 
        weight = weight + 30;
377
 
}
378
 
 
379
 
 
380
 
 Hand-to-play is left hand opponent (LHO) to leading hand
381
 
 
382
 
if (trick winning card move) {
383
 
    if  (hand-to-play void in the suit played by the leading hand)  {
384
 
        if  (trump contract and trump is equal to card move suit)
385
 
            weight = 30 - (rank of card move) + 2 * (suit length for card move suit)
386
 
        else
387
 
            weight = 60 - (rank of card move) + 2 * (suit length for card move suit)
388
 
    }
389
 
    else if (lowest card for partner to leading hand is higher than LHO played card)
390
 
        weight = 45 - (rank of card move)
391
 
    else if (RHO has a card in the leading suit that is higher than the trick leading card
392
 
               but lower than the highest rank of the leading hand)
393
 
        weight = 60 - (rank of card move)
394
 
    else if (LHO played card is higher than card played by the leading hand) {
395
 
        if (played card by LHO is lower than any card for RHO in the same suit)
396
 
            weight = 75 - (rank of card move)
397
 
        else if (played card by LHO is higher than any card in the same suit for the leading hand)
398
 
            weight = 70 - (rank of card move)
399
 
        else  {
400
 
            if  (LHO move card has at least one equivalent card) {
401
 
                weight = 60 - (rank of card move) 
402
 
            else
403
 
                weight = 45 - (rank of card move)
404
 
        }
405
 
    }
406
 
    else if (RHO is not void in the suit played by the leading hand) {
407
 
        if  (LHO move card has at least one equivalent card)    
408
 
            weight = 50 - (rank of card move)
409
 
        else
410
 
            weight = 45 - (rank of card move)
411
 
    }
412
 
    else
413
 
        weight = 45 - (rank of card move)
414
 
}
415
 
else {  /* card move is not trick winning */
416
 
    if  (hand-to-play void in the suit played by the leading hand)  {
417
 
        if  (trump contract and trump is equal to card move suit)
418
 
            weight = 15 - (rank of card move) + 2 * (suit length for card move suit)
419
 
        else
420
 
            weight = - (rank of card move) + 2 * (suit length for card move suit)
421
 
    }
422
 
    else if (lowest card for partner to leading hand or for RHO in the suit played is higher 
423
 
               than played card for LHO) 
424
 
        weight = - (rank of card move) 
425
 
    else if (LHO played card is higher than card played by the leading hand) { 
426
 
        if  (LHO move card has at least one equivalent card)
427
 
            weight = 20 - (rank of card move) 
428
 
         else 
429
 
             weight = 10 - (rank of card move)
430
 
     }  
431
 
     else 
432
 
         weight = - (rank of card move)
433
 
}      
434
 
 
435
 
 
436
 
 
437
 
Hand-to-play is partner to trick leading hand
438
 
 
439
 
if (trick winning card move) {
440
 
    if  (hand-to-play void in the suit played by the leading hand)  {
441
 
        if (card played by the leading hand is highest so far) {
442
 
            if (card by hand-to-play is trump and the suit played by the leading hand is not trump) 
443
 
                weight = 30 - (rank of card move) + 2 * (suit length for card move suit)
444
 
            else
445
 
                weight = 60 - (rank of card move) + 2 * (suit length for card move suit)
446
 
        }
447
 
        else if (hand-to-play is on top by ruffing)
448
 
            weight = 70 - (rank of card move) + 2 * (suit length for card move suit)
449
 
        else if (hand-to-play discards a trump but still loses)
450
 
            weight = 15 - (rank of card move) + 2 * (suit length for card move suit) 
451
 
        else       
452
 
            weight = 30 - (rank of card move) + 2 * (suit length for card move suit)
453
 
    }
454
 
    else 
455
 
        weight = 60 - (rank of card move) 
456
 
}
457
 
else {               /* card move is not trick winning */
458
 
    if  (hand-to-play void in the suit played by the leading hand)  {
459
 
        if (hand-to-play is on top by ruffing)
460
 
            weight = 40 - (rank of card move) + 2 * (suit length for card move suit)
461
 
        else if (hand-to-play underruffs */
462
 
            weight = -15 - (rank of card move) + 2 * (suit length for card move suit)
463
 
        else
464
 
            weight = - (rank of card move) + 2 * (suit length for card move suit)
465
 
    }
466
 
    else {
467
 
         if (the card by hand-to-play is highest so far) {
468
 
             if (rank of played card is second highest in the suit)
469
 
                weight = 25  
470
 
             else if (hand-to-play card has at least one equivalent card)
471
 
                 weight = 20 - (rank of card move)
472
 
             else
473
 
                 weight = 10 - (rank of card move)
474
 
         }
475
 
         else
476
 
             weight = -10 - (rank of card move)
477
 
    }
478
 
}
479
 
 
480
 
Hand-to-play is right hand opponent (RHO) to leading hand
481
 
 
482
 
if  (hand-to-play is void in leading suit)  {
483
 
    if  (LHO has current highest rank of the trick)  {
484
 
       if  (card move ruffs)
485
 
          weight = 14- (rank of card move) + 2 * (suit length for card move)
486
 
       else
487
 
          weight = 30- (rank of card move) + 2 * (suit length for card move) 
488
 
     }
489
 
    else if  (hand-to-play ruffs and wins) 
490
 
         weight = 30- (rank of card move) + 2 * (suit length for card move)
491
 
    else if  (card move suit is trump, but not winning)
492
 
        weight = - (rank of card move)
493
 
    else
494
 
        weight = 14- (rank of card move) + 2 * (suit length for card move)
495
 
}
496
 
else if  (LHO has current winning move)  {
497
 
    if  (RHO ruffs LHO’s winner)
498
 
        weight = 24 - (rank of card move) 
499
 
    else
500
 
        weight = 30- (rank of card move) 
501
 
}
502
 
else if  (card move superior to present winning move not by LHO)  {        
503
 
     weight = 30- (rank of card move)
504
 
else  {
505
 
    if  (card move ruffs but still losing)
506
 
        weight = - (rank of card move)
507
 
    else
508
 
        weight = 14- (rank of card move)
509
 
}
510
 
 
511
 
 
512
 
 
513
 
6.Algorithm to try early cutoff for generated moves
514
 
 
515
 
After generating moves at the end of a trick, they are each checked to see if one of them will lead to a position that already is stored in the Transposition Table.
516
 
Due to the processing overhead, this check is only made if the depth is 29 or more (i.e there are at least 33 cards in the position). 
517
 
Pseudo code:
518
 
 
519
 
moveExists = TRUE;
520
 
while  (moveExists)  {
521
 
     Make;
522
 
     depth = depth –1;
523
 
     RetrieveTTresult;
524
 
     if (hit in the transposition table)  {
525
 
          Search returnsTRUE if value of the position is TRUE and player side to move, or 
526
 
          FALSE if value of the position is FALSE and opponents side to move.  
527
 
          Else: Increment weight of move with 100.  
528
 
      }
529
 
       depth = depth +1;
530
 
       Undo;
531
 
       moveExists = NextMove;
532
 
}
533
 
    
534
 
The performance improvement for this enhancement is less than for the other enhancements. The number of generated nodes is roughly decreased by 10% and the search time is slighly decreased.
535
 
 
536
 
 
537
 
 
538
 
7.Storage and retrieval of position state data in the Transposition Table
539
 
 
540
 
Positions stored in the Transposition Table always consist of completed tricks. Positions stored start at depth=4, then 8,12, and so on. The information stored is information on won cards, the suit lengths of the hands, the hand to play the leading card in the position and upper and lower bounds for the number of future tricks to be taken by the side of the player.
541
 
 
542
 
Starting from issue 1.1.8, each ”winning cards node” contain all winning cards for one suit after an idea by Joël Bradmetz. This new solution is faster.
543
 
 
544
 
7.1   Transposition Table storing winning card ranks
545
 
 
546
 
For the outcome of played tricks, only card ranks that are winning due to their ranks matter:
547
 
Assume that the last two tricks of a deal without trumps looks like the following:
548
 
Trick 12: Leading hand North plays heart A, East, South and West follow by hearts Q, 9 and 7 respectively. 
549
 
Trick 13: North then leads spade A, the other hands plays diamonds  J, 8,3 in that order.
550
 
 
551
 
In trick 12, heart A wins by rank. In trick 13, spade A wins but not by rank.
552
 
The sequence of cards could have been the following without  changing the outcome:
553
 
Trick 12:  heart A, heart x, heart x, heart x
554
 
Trick 13:  spade x, diamond x, diamond x, diamond x
555
 
where x is any rank below lowest winning rank.
556
 
 
557
 
The cards that win by rank are recorded during the search and backed up similarly to the search value. If a card wins by rank and there are equivalent cards, e.g. only spade A is searched from a sequence of AKQ, then also the other cards K and Q must be recorded as having won by rank.
558
 
 
559
 
The cards winning by rank are stored in the Transposition Table as relative ranks, however any rank larger than the lowest winning rank in the suit are also stored as ”winning ranks”. Using relative ranks rather than absolute ranks considerably increases the number of positions that match this Transposition Table entry:
560
 
As an example, assume that there are only 4 cards left in a suit, A, Q, 9, 7 where each hand has one card in the suit. Then any combination of ranks, e.g. 8, 6, 3, 2 that preserves the relative order of ranks between hands will cause a match.
561
 
 
562
 
In the state position information absolute ranks are used, it is only in the Transposition Table where the ranks are stored as relatives. 
563
 
 
564
 
 
565
 
7.2  Backing up the winning ranks
566
 
 
567
 
At the search termination, either at the last trick or at a cutoff, the cards that have won by rank are backed up in the search tree together with the search value.
568
 
As this information propagates upwards, it is aggregated  with backed up information from other tree branches.
569
 
At a search cutoff, MergeMoveData merges the information (V is a union):
570
 
   
571
 
(winning ranks of all suits for current depth) = (winning ranks of all suits for depth - 1)  V  (possible winning rank for the current move causing the cutoff)
572
 
 
573
 
For each new move not causing cutoff, MergeAllMovesData merges:
574
 
 
575
 
(winning ranks of all suits for current depth) = (winning ranks of all suits for current depth)  V (winning ranks of all suits for depth - 1)  V  (possible winning rank for the current move) 
576
 
 
577
 
 
578
 
7.3     Checking the current position for a Transposition Table entry match 
579
 
 
580
 
The "Transposition Table" has a tree structure rather than a table, consisting of 2 interconnected trees. 
581
 
For deciding if there is a match, input is the position state data, including the cards left to play and the current leading hand. 
582
 
There are ”root pointers” per number of tricks left and per leading hand which  each points to the root of a tree of  ”suit lengths combination” nodes. Each such node includes a 64-bit code that uniquely defines one combination of suit lengths for the hands. The nodes are ordered such that the value of the 64-bit code in a parent node is higher than the 64-bit code of its left child but lower than the 64-bit code of its right child. So to find the node with the suit lengths combination for the actual position, a binary search is made. The basic binary search algorithm is described in [Knuth]. 
583
 
Each ”suit length combination node” points to the root of a tree consisting of ”winning cards nodes”, ie. cards that win by rank. (So the Transposition Table is really a number of trees, a forest.)
584
 
When a position is checked for a possible Transposition Table match, a tree branch is selected consisting of 4 subsequent ”winning cards nodes”, each ”winning cards node” includes an aggregate of all winning cards for one suit. This branch is followed as long as the ”winning cards” also can be found in the current position. (Note that the ranks of the ”winning card nodes” are relative, so the ranks of the current position must first be translated from absolute to relative ranks.)  When the ”winning cards node” no longer matches with the current position and there is no other alternative ”winning cards node” that fits, then the search backs up and tries an alternative ”winning cards node” on a higher level. 
585
 
 
586
 
When the last of the 4 subsequent ”winning cards nodes” containing clubs is reached, it points to a ”set of positions node”. Its stored upper and lower value bounds are checked against the number of tricks won so far by the player’s side and the target value. The following conditions are then checked, assuming that it is the North/South side that is the player’s side: 
587
 
 
588
 
If the sum of the stored lower value bound and the number of tricks won so far for the player’s side is equal or larger than target, then target can be reached for the player’s side in the current position. Search on this depth is terminated and TRUE is returned.
589
 
 
590
 
If the sum of the stored upper value bound and the number of tricks won so far for the player’s side is less than target, then reaching target can be prevented by the opponents to the player’s side in the current position. Search on this depth is terminated and FALSE is returned.
591
 
 
592
 
If instead it is East/West that is the player’s side, the following conditions apply:
593
 
 
594
 
If the sum of number of tricks remaining and the number of tricks won so far for the player’s side minus the upper value bound is equal or larger than target, then target can be reached for the player’s side in the current position. Search on this depth is terminated and TRUE is returned.
595
 
 
596
 
If the sum of number of tricks remaining and the number of tricks won so far for the player’s side minus the lower value bound is less than target, then reaching target can be prevented by the opponents to the player’s side in the current position. Search on this depth is terminated and FALSE is returned.
597
 
 
598
 
For all other cases, the search continues for the current depth.
599
 
 
600
 
For example, take the previous example with 2 tricks remaining with spade rank order 1 at North. (Rank order 1 is highest rank.) The hearts have rank orders 1 at North, 2 at East, 3 at South and 4 at West.  The diamond rank orders are orders 1 at East, 2 at South and 3 at West.  North is leading hand.
601
 
The ”root pointer” is now defined by the number of tricks remaining (=2) and North as leading hand.
602
 
The ”root pointer” points to the root node of its ”suit lengths combination” tree. The 64-bit integer coded from the suit lengths for all suits and hands is now searched within the tree. When the node is found with matching 64-bit suit lengths code, this node will point to the root of its ”winning card” tree.
603
 
This pointer points to a "winning cards node" containing spade rank order 1 at North which fits with the current position. This ”winning cards node” points to another "winning cards node" containing hearts rank orders 1 at North and 2 at East  which also fits the current position. Next ”winning cards node” pointed to contains diamonds order 1 at South, which does not match the current position. However, there is an alternative ”winning cards node” that has diamonds order 1 at East, which fits. (If there had been no alternative ”winning cards node” which fitted, the search had backed up to the previous ”winning cards node” to see if there was an alternative ”winning cards node” on this level which also fitted.) The next ”winning cards node” pointed to is for clubs. This node is empty, which fits the current position which have no clubs. 
604
 
This ”winning cards node” points to a "set of positions node” which have upper and lower value bounds defined. The conditions for these bounds are assumed to be fulfilled causing search termination on this depth, as described earlier. 
605
 
 
606
 
The usage of upper and lower value bounds in transposition tables is described in [Chang] and [Kupferschmid, Helmert].
607
 
 
608
 
 
609
 
 
610
 
The ”suit lengths combination” node includes:
611
 
      The suit lengths combination as a 64-bit integer.
612
 
      A pointer to the top ”winning cards node”.
613
 
      A pointer to next left ”suit lengths combination node” in the binary tree.
614
 
      A pointer to next right ”suit lengths combination node” in the binary tree.
615
 
 
616
 
 
617
 
The ”winning cards node” includes:
618
 
The hands of the relative ranks for each winning card of the actual suit.
619
 
A pointer to the next winning cards node required to achieve a Transposition Table match for this branch of the tree.
620
 
A pointer to the previous winning cards node.
621
 
A pointer to the next alternative winning cards node that leads to a Transposition Table match in an alternative tree branch.
622
 
      A pointer to the "set of positions node". 
623
 
 
624
 
 
625
 
The "set of positions node” includes:
626
 
An upper and a lower bound for the winning tricks of the North/South side. These values
627
 
are used to determine whether or not a search cutoff can be done.
628
 
The lowest winning rank per suit, expressed as relative rank.
629
 
The suit and rank for the currently best move.
630
 
 
631
 
 
632
 
After a Transposition Table match, the current position may later be part of a position that will be stored in the Transposition Table. Therefore, the stored winning ranks in the Transposition Table must be included in the state information of the current position. However, the winning ranks cannot be taken as is, because they are stored as relative ranks which now must be converted to absolute ranks in the current position.
633
 
This is done using the lowest winning relative rank for each suit that is stored in the ”set of positions” node that gave the Transposition Table match:
634
 
The aggregated set of (absolute) ranks for each suit in the current position is filtered using the stored information on the lowest winning relative rank. The winning ranks for each suit is then the aggregated set with only the number of highest ranks implied by the stored lowest winning relative rank in the ”set of positions” node.
635
 
E.g. if the aggregated rank set for spades is A J 9 4 2 and lowest winning relative rank is order=2, then winning ranks are A J.
636
 
 
637
 
 
638
 
7.4  Building a new entry in the Transposition Table
639
 
 
640
 
When the value of the current position is known and it is the end of a trick (except the last),  position state information is collected for storage in the Transposition Table. 
641
 
The ranks of the backed up winning cards are converted from absolute to relative.
642
 
For each suit, it is determined which winning rank that is lowest. The relative ranks then stored in the new Transposition Table entry are all ranks above and including the lowest rank, filling out any ”holes” in the ranks that might have been present.
643
 
The trees of the Transposition Table are searched starting from the ”root pointer” and additional nodes are inserted corresponding to the current position. 
644
 
First, the suit lengths of the current position are used to find a ”suit lengths combination node” or to create a new such node if it does not exist already.
645
 
The next step is to search for a ”winning card node” that has the ”suit length combination node” as parent. This ”winning card node” has then winning cards for spades.
646
 
If no such node yet exists, ”winning card nodes”, one for each suit, are created using the winning cards of the current position. Each such node includes all winning cards for one of the suits. Then, a ”set of positions” node is created. This node is pointed to from the last created ”winning card node” created for the winning cards of clubs. 
647
 
Otherwise, if there already exists a matching ”winning card node” with the ”suit length combination node” as parent, it is checked whether or not the ”winning card nodes” in a subsequent tree branch already created for hearts, diamonds and clubs also are matched with the current position.
648
 
If such a sequence of nodes can be found, the upper or lower bound in the connected ”set of positions node” may be updated to allow for an increased number of cutoffs:
649
 
 
650
 
If the current  position upper value bound is less than the stored upper value bound, the stored value is updated with the current position value.   
651
 
If the current  position lower value bound is larger than the stored lower value bound, the stored value is updated with the current position value.
652
 
 
653
 
In case a matching ”winning card node” cannot be found, a new ”winning card node” is created and linked to the last matching node. E.g. if existing ”winning card nodes” for spades and hearts match the current position, but no node match for diamonds, then a ”winning cards node” for diamonds is created and linked to the previous ”winning cards node” for hearts. Then a clubs ”winning cards node” and a ”set of positions node” are created.
654
 
 
655
 
 
656
 
 
657
 
 
658
 
 
659
 
References
660
 
 
661
 
James Dow Allen:
662
 
Source code for a simple DDS.
663
 
http://freepages.genealogy.rootsweb.com/~jamesdow/Tech/dbldum.htm
664
 
 
665
 
Matthias Brill:
666
 
DDS algorithms description (in German) and DDS source code.
667
 
 http://linux.softpedia.com/get/Science-and-Engineering/Artificial-Intelligence/cddsolve-20055.shtml 
668
 
 
669
 
Ming-Sheng Chang:
670
 
DDS algorithms description.
671
 
cs.nyu.edu/web/Research/TechReports/TR1996-725/TR1996-725.ps.gz
672
 
 
673
 
 
674
 
Ed Colley:
675
 
DDS source code and DDS executable.
676
 
http://freefinesse.sourceforge.net/
677
 
 
678
 
Matthew L. Ginsberg:
679
 
DDS algorithms description.
680
 
http://www.cs.cmu.edu/afs/cs/project/jair/pub/volume14/ginsberg01a.pdf
681
 
 
682
 
Dan Hirschberg:
683
 
DDS algorithms description and DDS executable (MS DOS, cannot run in XP?)
684
 
http://www.ics.uci.edu/~dan/bridge/index.html
685
 
 
686
 
Alexey Slovesnov:
687
 
DDS source code and DDS executable.
688
 
http://slovesnov.narod.ru/bridge/en/index.html
689
 
 
690
 
Judea Pearl: Asymptotic properties of minimax trees and game search precedures.
691
 
   Artificial Intelligence 14(2):113-138. [Pearl 1980]
692
 
 
693
 
Aske Plaat, Jonathan Schaeffer, Wim Pijls and Arie de Bruin:  Exploiting graph properties of game trees. In Proceedings of the Thirteenth National Conference on Artificial Intelligence, pages 234-239, 1996  [Plaat et al.]
694
 
 
695
 
Hans Kuijf, personal communication.
696
 
 
697
 
Pedja Stanojevic, personal communication.
698
 
 
699
 
Knuth: The art of computer programming, Vol. 3, Searching and Sorting, chapter 6.2.2, Algorithm T.
700
 
 
701
 
Sebastian Kupferschmid, Malte Helmert: A Skat Player Based on Monte Carlo Simulation.
702
 
 
703
 
Joël Bradmetz, personal communication.
704
 
http://jibe-bridge.perso.cegetel.net/
 
 
b'\\ No newline at end of file'