poker_eval 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,981 @@
1
+ #include "ruby.h"
2
+
3
+ /*
4
+ *
5
+ * Corbright (C) 2007, 2008 Loic Dachary <loic@dachary.org>
6
+ * Corbright (C) 2004, 2005, 2006 Mekensleep
7
+ *
8
+ * Mekensleep
9
+ * 24 rue vieille du temple
10
+ * 75004 Paris
11
+ * licensing@mekensleep.com
12
+ *
13
+ * This program is free software; you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License as published by
15
+ * the Free Software Foundation; either version 3 of the License, or
16
+ * (at your option) any later version.
17
+ *
18
+ * This program is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a corb of the GNU General Public License
24
+ * along with this program; if not, write to the Free Software
25
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26
+ *
27
+ * Authors:
28
+ * Loic Dachary <loic@dachary.org>
29
+ *
30
+ */
31
+
32
+
33
+ /* enumerate.c -- functions to compute pot equity by enumerating outcomes
34
+ Exports:
35
+ enumExhaustive() exhaustive enumeration of outcomes
36
+ enumGameParams() look up rule parameters by game type
37
+ enumResultClear() clear enumeration result object
38
+ enumResultPrint() print enumeration result object
39
+ enumResultPrintTerse() print enumeration result object, tersely
40
+ enumSample() monte carlo sampling of outcomes
41
+
42
+ Michael Maurer, Apr 2002
43
+ */
44
+
45
+ #include "poker_defs.h"
46
+ #include "inlines/eval.h"
47
+ #include "inlines/eval_low.h"
48
+ #include "inlines/eval_low8.h"
49
+ #include "inlines/eval_joker_low.h"
50
+ #include "inlines/eval_omaha.h"
51
+ #include "deck_std.h"
52
+ #include "rules_std.h"
53
+
54
+ #include "enumerate.h"
55
+ #include "enumdefs.h"
56
+
57
+ /* INNER_LOOP is executed in every iteration of the combinatorial enumerator
58
+ macros DECK_ENUMERATE_n_CARDS_D() and DECK_ENUMERATE_PERMUTATIONS_D. It
59
+ evaluates each player's hand based on the enumerated community cards and
60
+ accumulates statistics on wins, ties, losses, and pot equity.
61
+
62
+ Macro argument:
63
+ evalwrap -- code that evaluates pockets[i], board, sharedCards, and/or
64
+ unsharedCards[i] as a poker hand, then stores the result
65
+ in hival[i] and loval[i] and stores an error code in err
66
+ Loop variable: either of
67
+ StdDeck_CardMask sharedCards;
68
+ StdDeck_CardMask unsharedCards[];
69
+ Inputs:
70
+ StdDeck_CardMask pockets[];
71
+ StdDeck_CardMask board;
72
+ int npockets;
73
+ Outputs:
74
+ enum_result_t *result;
75
+ */
76
+
77
+ #define INNER_LOOP(evalwrap) \
78
+ do { \
79
+ int i; \
80
+ HandVal hival[ENUM_MAXPLAYERS]; \
81
+ LowHandVal loval[ENUM_MAXPLAYERS]; \
82
+ HandVal besthi = HandVal_NOTHING; \
83
+ LowHandVal bestlo = LowHandVal_NOTHING; \
84
+ int hishare = 0; \
85
+ int loshare = 0; \
86
+ double hipot, lopot; \
87
+ /* find winning hands for high and low */ \
88
+ for (i=0; i<sizeToDeal-1; i++) { \
89
+ int err; \
90
+ { evalwrap } \
91
+ if (err != 0) \
92
+ return 1000 + err; \
93
+ if (hival[i] != HandVal_NOTHING) { \
94
+ if (hival[i] > besthi) { \
95
+ besthi = hival[i]; \
96
+ hishare = 1; \
97
+ } else if (hival[i] == besthi) { \
98
+ hishare++; \
99
+ } \
100
+ } \
101
+ if (loval[i] != LowHandVal_NOTHING) { \
102
+ if (loval[i] < bestlo) { \
103
+ bestlo = loval[i]; \
104
+ loshare = 1; \
105
+ } else if (loval[i] == bestlo) { \
106
+ loshare++; \
107
+ } \
108
+ } \
109
+ } \
110
+ /* now award pot fractions to winning hands */ \
111
+ if (bestlo != LowHandVal_NOTHING && \
112
+ besthi != HandVal_NOTHING) { \
113
+ hipot = 0.5 / hishare; \
114
+ lopot = 0.5 / loshare; \
115
+ } else if (bestlo == LowHandVal_NOTHING && \
116
+ besthi != HandVal_NOTHING) { \
117
+ hipot = 1.0 / hishare; \
118
+ lopot = 0; \
119
+ } else if (bestlo != LowHandVal_NOTHING && \
120
+ besthi == HandVal_NOTHING) { \
121
+ hipot = 0; \
122
+ lopot = 1.0 / loshare; \
123
+ } else { \
124
+ hipot = lopot = 0; \
125
+ } \
126
+ for (i=0; i<sizeToDeal-1; i++) { \
127
+ double potfrac = 0; \
128
+ int H = 0, L = 0; \
129
+ if (hival[i] != HandVal_NOTHING) { \
130
+ if (hival[i] == besthi) { \
131
+ H = hishare; \
132
+ potfrac += hipot; \
133
+ if (hishare == 1) \
134
+ result->nwinhi[i]++; \
135
+ else \
136
+ result->ntiehi[i]++; \
137
+ } else { \
138
+ result->nlosehi[i]++; \
139
+ } \
140
+ } \
141
+ if (loval[i] != LowHandVal_NOTHING) { \
142
+ if (loval[i] == bestlo) { \
143
+ L = loshare; \
144
+ potfrac += lopot; \
145
+ if (loshare == 1) \
146
+ result->nwinlo[i]++; \
147
+ else \
148
+ result->ntielo[i]++; \
149
+ } else { \
150
+ result->nloselo[i]++; \
151
+ } \
152
+ } \
153
+ result->nsharehi[i][H]++; \
154
+ result->nsharelo[i][L]++; \
155
+ result->nshare[i][H][L]++; \
156
+ if (potfrac > 0.99) \
157
+ result->nscoop[i]++; \
158
+ result->ev[i] += potfrac; \
159
+ } \
160
+ result->nsamples++; \
161
+ } while (0);
162
+
163
+ #define INNER_LOOP_ANY_HIGH \
164
+ INNER_LOOP({ \
165
+ StdDeck_CardMask _hand; \
166
+ StdDeck_CardMask _finalBoard; \
167
+ StdDeck_CardMask_RESET(_hand); \
168
+ StdDeck_CardMask_RESET(_finalBoard); \
169
+ StdDeck_CardMask_OR(_finalBoard, board, cardsDealt[0]); \
170
+ StdDeck_CardMask_OR(_hand, pockets[i], _finalBoard); \
171
+ StdDeck_CardMask_OR(_hand, _hand, cardsDealt[i + 1]); \
172
+ hival[i] = StdDeck_StdRules_EVAL_N(_hand, 7); \
173
+ loval[i] = LowHandVal_NOTHING; \
174
+ err = 0; \
175
+ })
176
+
177
+ #define INNER_LOOP_ANY_HILO \
178
+ INNER_LOOP({ \
179
+ StdDeck_CardMask _hand; \
180
+ StdDeck_CardMask _finalBoard; \
181
+ StdDeck_CardMask_RESET(_hand); \
182
+ StdDeck_CardMask_RESET(_finalBoard); \
183
+ StdDeck_CardMask_OR(_finalBoard, board, cardsDealt[0]); \
184
+ StdDeck_CardMask_OR(_hand, pockets[i], _finalBoard); \
185
+ StdDeck_CardMask_OR(_hand, _hand, cardsDealt[i + 1]); \
186
+ hival[i] = StdDeck_StdRules_EVAL_N(_hand, 7); \
187
+ loval[i] = StdDeck_Lowball8_EVAL(_hand, 7); \
188
+ err = 0; \
189
+ })
190
+
191
+ #define INNER_LOOP_OMAHA \
192
+ INNER_LOOP({ \
193
+ StdDeck_CardMask _hand; \
194
+ StdDeck_CardMask _finalBoard; \
195
+ StdDeck_CardMask_RESET(_hand); \
196
+ StdDeck_CardMask_RESET(_finalBoard); \
197
+ StdDeck_CardMask_OR(_finalBoard, board, cardsDealt[0]); \
198
+ StdDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
199
+ err = StdDeck_OmahaHiLow8_EVAL(_hand, _finalBoard, \
200
+ &hival[i], NULL); \
201
+ loval[i] = LowHandVal_NOTHING; \
202
+ })
203
+
204
+ #define INNER_LOOP_OMAHA8 \
205
+ INNER_LOOP({ \
206
+ StdDeck_CardMask _hand; \
207
+ StdDeck_CardMask _finalBoard; \
208
+ StdDeck_CardMask_RESET(_hand); \
209
+ StdDeck_CardMask_RESET(_finalBoard); \
210
+ StdDeck_CardMask_OR(_finalBoard, board, cardsDealt[0]); \
211
+ StdDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
212
+ err = StdDeck_OmahaHiLow8_EVAL(_hand, _finalBoard, \
213
+ &hival[i], &loval[i]); \
214
+ })
215
+
216
+ #define INNER_LOOP_7STUDNSQ \
217
+ INNER_LOOP({ \
218
+ StdDeck_CardMask _hand; \
219
+ StdDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
220
+ hival[i] = StdDeck_StdRules_EVAL_N(_hand, 7); \
221
+ loval[i] = StdDeck_Lowball_EVAL(_hand, 7); \
222
+ err = 0; \
223
+ })
224
+
225
+ #define INNER_LOOP_RAZZ \
226
+ INNER_LOOP({ \
227
+ StdDeck_CardMask _hand; \
228
+ StdDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
229
+ hival[i] = HandVal_NOTHING; \
230
+ loval[i] = StdDeck_Lowball_EVAL(_hand, 7); \
231
+ err = 0; \
232
+ })
233
+
234
+ #define INNER_LOOP_5DRAW \
235
+ INNER_LOOP({ \
236
+ JokerDeck_CardMask _hand; \
237
+ JokerDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
238
+ hival[i] = JokerDeck_JokerRules_EVAL_N(_hand, 5); \
239
+ loval[i] = LowHandVal_NOTHING; \
240
+ err = 0; \
241
+ })
242
+
243
+ #define INNER_LOOP_5DRAW8 \
244
+ INNER_LOOP({ \
245
+ JokerDeck_CardMask _hand; \
246
+ JokerDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
247
+ hival[i] = JokerDeck_JokerRules_EVAL_N(_hand, 5); \
248
+ loval[i] = JokerDeck_Lowball8_EVAL(_hand, 5); \
249
+ err = 0; \
250
+ })
251
+
252
+ #define INNER_LOOP_5DRAWNSQ \
253
+ INNER_LOOP({ \
254
+ JokerDeck_CardMask _hand; \
255
+ JokerDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
256
+ hival[i] = JokerDeck_JokerRules_EVAL_N(_hand, 5); \
257
+ loval[i] = JokerDeck_Lowball_EVAL(_hand, 5); \
258
+ err = 0; \
259
+ })
260
+
261
+ #define INNER_LOOP_LOWBALL \
262
+ INNER_LOOP({ \
263
+ JokerDeck_CardMask _hand; \
264
+ JokerDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
265
+ hival[i] = HandVal_NOTHING; \
266
+ loval[i] = JokerDeck_Lowball_EVAL(_hand, 5); \
267
+ err = 0; \
268
+ })
269
+
270
+ #define INNER_LOOP_LOWBALL27 \
271
+ INNER_LOOP({ \
272
+ StdDeck_CardMask _hand; \
273
+ StdDeck_CardMask_OR(_hand, pockets[i], cardsDealt[i + 1]); \
274
+ hival[i] = HandVal_NOTHING; \
275
+ loval[i] = StdDeck_StdRules_EVAL_N(_hand, 5); \
276
+ err = 0; \
277
+ })
278
+
279
+ static int
280
+ rbenumExhaustive(enum_game_t game, StdDeck_CardMask pockets[],
281
+ int numToDeal[],
282
+ StdDeck_CardMask board, StdDeck_CardMask dead,
283
+ int sizeToDeal, enum_result_t *result) {
284
+ int totalToDeal = 0;
285
+ int i;
286
+ enumResultClear(result);
287
+ StdDeck_CardMask cardsDealt[ENUM_MAXPLAYERS + 1];
288
+ memset(cardsDealt, 0, sizeof(StdDeck_CardMask) * (ENUM_MAXPLAYERS + 1));
289
+ if (sizeToDeal - 1 > ENUM_MAXPLAYERS)
290
+ return 1;
291
+ for(i = 0; i < sizeToDeal; i++)
292
+ totalToDeal += numToDeal[i];
293
+
294
+ /*
295
+ * Cards in pockets or in the board must not be dealt
296
+ */
297
+ StdDeck_CardMask_OR(dead, dead, board);
298
+ for(i = 0; i < sizeToDeal - 1; i++) {
299
+ StdDeck_CardMask_OR(dead, dead, pockets[i]);
300
+ }
301
+
302
+ if (game == game_holdem) {
303
+ if(totalToDeal > 0) {
304
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
305
+ sizeToDeal, numToDeal,
306
+ dead, INNER_LOOP_ANY_HIGH);
307
+ } else {
308
+ INNER_LOOP_ANY_HIGH;
309
+ }
310
+ } else if (game == game_holdem8) {
311
+ if(totalToDeal > 0) {
312
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
313
+ sizeToDeal, numToDeal,
314
+ dead, INNER_LOOP_ANY_HILO);
315
+ } else {
316
+ INNER_LOOP_ANY_HILO;
317
+ }
318
+ } else if (game == game_omaha) {
319
+ if(totalToDeal > 0) {
320
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
321
+ sizeToDeal, numToDeal,
322
+ dead, INNER_LOOP_OMAHA);
323
+ } else {
324
+ INNER_LOOP_OMAHA;
325
+ }
326
+ } else if (game == game_omaha8) {
327
+ if(totalToDeal > 0) {
328
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
329
+ sizeToDeal, numToDeal,
330
+ dead, INNER_LOOP_OMAHA8);
331
+ } else {
332
+ INNER_LOOP_OMAHA8;
333
+ }
334
+ } else if (game == game_7stud) {
335
+ if(totalToDeal > 0) {
336
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
337
+ sizeToDeal, numToDeal,
338
+ dead, INNER_LOOP_ANY_HIGH);
339
+ } else {
340
+ INNER_LOOP_ANY_HIGH;
341
+ }
342
+ } else if (game == game_7stud8) {
343
+ if(totalToDeal > 0) {
344
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
345
+ sizeToDeal, numToDeal,
346
+ dead, INNER_LOOP_ANY_HILO);
347
+ } else {
348
+ INNER_LOOP_ANY_HILO;
349
+ }
350
+ } else if (game == game_7studnsq) {
351
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
352
+ sizeToDeal, numToDeal,
353
+ dead, INNER_LOOP_7STUDNSQ);
354
+ } else if (game == game_razz) {
355
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
356
+ sizeToDeal, numToDeal,
357
+ dead, INNER_LOOP_RAZZ);
358
+ } else if (game == game_lowball27) {
359
+ DECK_ENUMERATE_COMBINATIONS_D(StdDeck, cardsDealt,
360
+ sizeToDeal, numToDeal,
361
+ dead, INNER_LOOP_LOWBALL27);
362
+ } else {
363
+ return 1;
364
+ }
365
+
366
+ result->game = game;
367
+ result->nplayers = sizeToDeal - 1;
368
+ result->sampleType = ENUM_EXHAUSTIVE;
369
+ return 0;
370
+ }
371
+
372
+ static int
373
+ rbenumSample(enum_game_t game, StdDeck_CardMask pockets[],
374
+ int numToDeal[],
375
+ StdDeck_CardMask board, StdDeck_CardMask dead,
376
+ int sizeToDeal, int iterations, enum_result_t *result) {
377
+ int i;
378
+ enumResultClear(result);
379
+ StdDeck_CardMask cardsDealt[ENUM_MAXPLAYERS + 1];
380
+ memset(cardsDealt, 0, sizeof(StdDeck_CardMask) * (ENUM_MAXPLAYERS + 1));
381
+ if (sizeToDeal - 1 > ENUM_MAXPLAYERS)
382
+ return 1;
383
+
384
+ /*
385
+ * Cards in pockets or in the board must not be dealt
386
+ */
387
+ StdDeck_CardMask_OR(dead, dead, board);
388
+ for(i = 0; i < sizeToDeal - 1; i++) {
389
+ StdDeck_CardMask_OR(dead, dead, pockets[i]);
390
+ }
391
+
392
+ if (game == game_holdem) {
393
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
394
+ sizeToDeal, numToDeal,
395
+ dead, iterations, INNER_LOOP_ANY_HIGH);
396
+ } else if (game == game_holdem8) {
397
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
398
+ sizeToDeal, numToDeal,
399
+ dead, iterations, INNER_LOOP_ANY_HILO);
400
+ } else if (game == game_omaha) {
401
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
402
+ sizeToDeal, numToDeal,
403
+ dead, iterations, INNER_LOOP_OMAHA);
404
+ } else if (game == game_omaha8) {
405
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
406
+ sizeToDeal, numToDeal,
407
+ dead, iterations, INNER_LOOP_OMAHA8);
408
+ } else if (game == game_7stud) {
409
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
410
+ sizeToDeal, numToDeal,
411
+ dead, iterations, INNER_LOOP_ANY_HIGH);
412
+ } else if (game == game_7stud8) {
413
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
414
+ sizeToDeal, numToDeal,
415
+ dead, iterations, INNER_LOOP_ANY_HILO);
416
+ } else if (game == game_7studnsq) {
417
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
418
+ sizeToDeal, numToDeal,
419
+ dead, iterations, INNER_LOOP_7STUDNSQ);
420
+ } else if (game == game_razz) {
421
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
422
+ sizeToDeal, numToDeal,
423
+ dead, iterations, INNER_LOOP_RAZZ);
424
+ } else if (game == game_lowball27) {
425
+ DECK_MONTECARLO_PERMUTATIONS_D(StdDeck, cardsDealt,
426
+ sizeToDeal, numToDeal,
427
+ dead, iterations, INNER_LOOP_LOWBALL27);
428
+ } else {
429
+ return 1;
430
+ }
431
+
432
+ result->game = game;
433
+ result->nplayers = sizeToDeal - 1;
434
+ result->sampleType = ENUM_SAMPLE;
435
+ return 0;
436
+ }
437
+
438
+ #define NOCARD 255
439
+
440
+ static int rbList2CardMask(VALUE object, CardMask* cardsp)
441
+ {
442
+ CardMask cards;
443
+ int cards_size = 0;
444
+ int valid_cards_size = 0;
445
+
446
+ if (TYPE(object) != T_ARRAY)
447
+ {
448
+ rb_fatal("expected a list of cards");
449
+ }
450
+
451
+ valid_cards_size = cards_size = RARRAY_LENINT(object);
452
+ CardMask_RESET(cards);
453
+
454
+ int card;
455
+ int i;
456
+ for(i = 0; i < cards_size; i++) {
457
+ card = -1;
458
+ char* card_string = RSTRING_PTR(rb_ary_entry(object, i));
459
+
460
+ if(!strcmp(card_string, "__"))
461
+ card = 255;
462
+ else
463
+ if(Deck_stringToCard(card_string, &card) == 0)
464
+ rb_fatal("card %s is not a valid card name", card_string);
465
+
466
+ if(card == NOCARD)
467
+ valid_cards_size--;
468
+ else
469
+ CardMask_SET(cards, card);
470
+ }
471
+
472
+ *cardsp = cards;
473
+
474
+ return valid_cards_size;
475
+ }
476
+
477
+ static VALUE
478
+ CardMask2SortedRbList(CardMask hand, int low)
479
+ {
480
+ int i;
481
+ HandVal handval;
482
+ VALUE result = rb_ary_new();
483
+
484
+ if(StdDeck_CardMask_IS_EMPTY(hand)) {
485
+ rb_ary_push(result, rb_str_new2("Nothing"));
486
+ return result;
487
+ }
488
+
489
+ if(low) {
490
+ handval = Hand_EVAL_LOW8(hand, 5);
491
+ } else {
492
+ handval = Hand_EVAL_N(hand, 5);
493
+ }
494
+
495
+ int htype = HandVal_HANDTYPE(handval);
496
+ {
497
+ rb_ary_push(result, rb_str_new2(StdRules_handTypeNames[htype]));
498
+ }
499
+
500
+ if(!low || htype != LowHandVal_NOTHING) {
501
+ if (StdRules_nSigCards[htype] >= 1) {
502
+ int rank = HandVal_TOP_CARD(handval);
503
+ if(low) rank = LOWRANK2RANK(rank);
504
+
505
+ if(htype == HandType_STRAIGHT || htype == HandType_STFLUSH) {
506
+ for(i = rank; rank - i < 5; i--) {
507
+ int rank_modulo = i < 0 ? StdDeck_Rank_ACE : i;
508
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank_modulo)));
509
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank_modulo));*/
510
+ /* PyList_Append(result, pyvalue);*/
511
+ /* Py_DECREF(pyvalue);*/
512
+ }
513
+ } else {
514
+ int count;
515
+ switch(htype) {
516
+ case HandType_ONEPAIR:
517
+ case HandType_TWOPAIR:
518
+ count = 2;
519
+ break;
520
+ case HandType_TRIPS:
521
+ case HandType_FULLHOUSE:
522
+ count = 3;
523
+ break;
524
+ case HandType_QUADS:
525
+ count = 4;
526
+ break;
527
+ default:
528
+ count = 1;
529
+ break;
530
+ }
531
+ for(i = 0; i < count; i++) {
532
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank)));
533
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank));*/
534
+ /* PyList_Append(result, pyvalue);*/
535
+ /* Py_DECREF(pyvalue);*/
536
+ }
537
+ }
538
+ }
539
+ if (StdRules_nSigCards[htype] >= 2) {
540
+ int rank = HandVal_SECOND_CARD(handval);
541
+ int count = 1;
542
+ if(low) rank = LOWRANK2RANK(rank);
543
+ if(htype == HandType_TWOPAIR ||
544
+ htype == HandType_FULLHOUSE)
545
+ count = 2;
546
+
547
+ for(i = 0; i < count; i++) {
548
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank)));
549
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank));*/
550
+ /* PyList_Append(result, pyvalue);*/
551
+ /* Py_DECREF(pyvalue);*/
552
+ }
553
+ }
554
+
555
+ if (StdRules_nSigCards[htype] >= 3) {
556
+ int rank = HandVal_THIRD_CARD(handval);
557
+ if(low) rank = LOWRANK2RANK(rank);
558
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank)));
559
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank));*/
560
+ /* PyList_Append(result, pyvalue);*/
561
+ /* Py_DECREF(pyvalue);*/
562
+ }
563
+
564
+ if (StdRules_nSigCards[htype] >= 4) {
565
+ int rank = HandVal_FOURTH_CARD(handval);
566
+ if(low) rank = LOWRANK2RANK(rank);
567
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank)));
568
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank));*/
569
+ /* PyList_Append(result, pyvalue);*/
570
+ /* Py_DECREF(pyvalue);*/
571
+ }
572
+
573
+ if (StdRules_nSigCards[htype] >= 5) {
574
+ int rank = HandVal_FIFTH_CARD(handval);
575
+ if(low) rank = LOWRANK2RANK(rank);
576
+ rb_ary_push(result, INT2NUM(findanddelete(&hand, rank)));
577
+ /* PyObject* pyvalue = Py_BuildValue("i", findanddelete(&hand, rank));*/
578
+ /* PyList_Append(result, pyvalue);*/
579
+ /* Py_DECREF(pyvalue);*/
580
+ }
581
+
582
+ }
583
+
584
+ /*
585
+ * Append remaining cards, highest first
586
+ */
587
+ for(i = Deck_N_CARDS - 1; i >= 0; i--) {
588
+ if (StdDeck_CardMask_CARD_IS_SET(hand, i)) {
589
+ rb_ary_push(result, INT2NUM(i));
590
+ /* PyObject* pyvalue = Py_BuildValue("i", i);*/
591
+ /* PyList_Append(result, pyvalue);*/
592
+ /* Py_DECREF(pyvalue);*/
593
+ }
594
+ }
595
+
596
+ return result;
597
+ }
598
+
599
+ /*
600
+ * Find the card with highest suit matching rank in hand
601
+ * and remove it from hand. The removed card is returned.
602
+ */
603
+ int findanddelete(CardMask* hand, int rank)
604
+ {
605
+ int suit;
606
+ for(suit = StdDeck_Suit_LAST; suit >= StdDeck_Suit_FIRST; suit--) {
607
+ int card = StdDeck_MAKE_CARD(rank, suit);
608
+ if(CardMask_CARD_IS_SET(*hand, card)) {
609
+ CardMask_UNSET(*hand, card);
610
+ return card;
611
+ }
612
+ }
613
+ return -1;
614
+ }
615
+
616
+ static VALUE
617
+ t_card2Rbstring(VALUE self, VALUE args)
618
+ {
619
+ int card = 0;
620
+
621
+ card = NUM2INT(args);
622
+
623
+ /* if (!PyArg_ParseTuple(args, "i", &card))*/
624
+ /* return NULL;*/
625
+
626
+ if(card == 255) {
627
+ return rb_str_new2("__");
628
+ } else {
629
+ /*
630
+ * Avoid using GenericDeck_cardString as long as it insists
631
+ * on using the "static thread" hack (see lib/deck.c).
632
+ */
633
+ char tmp[16];
634
+ StdDeck.cardToString(card, tmp);
635
+ rb_str_new2(tmp);
636
+ }
637
+ }
638
+
639
+ static int
640
+ OmahaHiLow8_Best(StdDeck_CardMask hole, StdDeck_CardMask board,
641
+ HandVal *hival, LowHandVal *loval,
642
+ StdDeck_CardMask *hicards, StdDeck_CardMask *locards) {
643
+ StdDeck_CardMask allcards;
644
+ LowHandVal allval;
645
+ HandVal curhi, besthi;
646
+ LowHandVal curlo, bestlo;
647
+ StdDeck_CardMask hole1[OMAHA_MAXHOLE];
648
+ StdDeck_CardMask board1[OMAHA_MAXBOARD];
649
+ StdDeck_CardMask n1, n2, n3, n4, n5;
650
+ int nhole, nboard;
651
+ int eligible = 0;
652
+ int i, h1, h2, b1, b2, b3;
653
+
654
+ /* pluck out individual cards from hole and board masks, save in arrays */
655
+ nhole = nboard = 0;
656
+ for (i=0; i<StdDeck_N_CARDS; i++) {
657
+ if (StdDeck_CardMask_CARD_IS_SET(hole, i)) {
658
+ if (nhole >= OMAHA_MAXHOLE)
659
+ return 1; /* too many hole cards */
660
+ StdDeck_CardMask_RESET(hole1[nhole]);
661
+ StdDeck_CardMask_SET(hole1[nhole], i);
662
+ nhole++;
663
+ }
664
+ if (StdDeck_CardMask_CARD_IS_SET(board, i)) {
665
+ if (StdDeck_CardMask_CARD_IS_SET(hole, i)) /* same card in hole and board */
666
+ return 2;
667
+ if (nboard >= OMAHA_MAXBOARD)
668
+ return 3; /* too many board cards */
669
+ StdDeck_CardMask_RESET(board1[nboard]);
670
+ StdDeck_CardMask_SET(board1[nboard], i);
671
+ nboard++;
672
+ }
673
+ }
674
+
675
+ if (nhole < OMAHA_MINHOLE || nhole > OMAHA_MAXHOLE)
676
+ return 4; /* wrong # of hole cards */
677
+ if (nboard < OMAHA_MINBOARD || nboard > OMAHA_MAXBOARD)
678
+ return 5; /* wrong # of board cards */
679
+
680
+ /* quick test in case no low is possible with all 9 cards */
681
+ if (loval != NULL) {
682
+ StdDeck_CardMask_OR(allcards, hole, board);
683
+ allval = StdDeck_Lowball8_EVAL(allcards, nhole + nboard);
684
+ eligible = (allval != LowHandVal_NOTHING);
685
+ }
686
+
687
+ /* loop over all combinations of hole with board (60 for 4 hole cards
688
+ and 5 board cards). */
689
+ besthi = HandVal_NOTHING;
690
+ bestlo = LowHandVal_NOTHING;
691
+ /* {h1,h2} loop over all hole card combinations */
692
+ for (h1=0; h1<nhole-1; h1++) {
693
+ StdDeck_CardMask_RESET(n1);
694
+ StdDeck_CardMask_OR(n1, n1, hole1[h1]);
695
+ for (h2=h1+1; h2<nhole; h2++) {
696
+ StdDeck_CardMask_OR(n2, n1, hole1[h2]);
697
+ /* {b1,b2,b3} loop over all board card combinations */
698
+ for (b1=0; b1<nboard-2; b1++) {
699
+ StdDeck_CardMask_OR(n3, n2, board1[b1]);
700
+ for (b2=b1+1; b2<nboard-1; b2++) {
701
+ StdDeck_CardMask_OR(n4, n3, board1[b2]);
702
+ for (b3=b2+1; b3<nboard; b3++) {
703
+ if (hival != NULL) {
704
+ StdDeck_CardMask_OR(n5, n4, board1[b3]);
705
+ curhi = StdDeck_StdRules_EVAL_N(n5, 5);
706
+ if (curhi > besthi || besthi == HandVal_NOTHING) {
707
+ besthi = curhi;
708
+ *hicards = n5;
709
+ }
710
+ }
711
+ if (loval != NULL && eligible) {
712
+ curlo = StdDeck_Lowball8_EVAL(n5, 5);
713
+ if (curlo < bestlo || bestlo == LowHandVal_NOTHING) {
714
+ bestlo = curlo;
715
+ *locards = n5;
716
+ }
717
+ }
718
+ }
719
+ }
720
+ }
721
+ }
722
+ }
723
+ if (hival != NULL) *hival = besthi;
724
+ if (loval != NULL) *loval = bestlo;
725
+ return 0;
726
+ }
727
+
728
+ static VALUE
729
+ t_eval_hand(VALUE self, VALUE args)
730
+ {
731
+ VALUE result = 0;
732
+ VALUE rbboard = 0;
733
+ VALUE rbhand = 0;
734
+ char* hilow_string = 0;
735
+
736
+ hilow_string = RSTRING_PTR(rb_hash_aref(args, rb_str_new2("side")));
737
+ rbboard = rb_hash_aref(args, rb_str_new2("board"));
738
+ rbhand = rb_hash_aref(args, rb_str_new2("hand"));
739
+
740
+ int low = 0;
741
+ CardMask hand;
742
+ CardMask board;
743
+ int board_size = 0;
744
+ CardMask best;
745
+ HandVal best_handval;
746
+
747
+ StdDeck_CardMask_RESET(best);
748
+
749
+ if(!strcmp(hilow_string, "low")) {
750
+ low = 1;
751
+ }
752
+
753
+ if(rbList2CardMask(rbhand, &hand) < 0)
754
+ rb_fatal("empty hand given");
755
+
756
+ if( !NIL_P(rbboard))
757
+ {
758
+ board_size = rbList2CardMask(rbboard, &board);
759
+ }
760
+
761
+ if(board_size > 0) {
762
+ CardMask hicards;
763
+ CardMask locards;
764
+ HandVal hival = 0;
765
+ HandVal loval = 0;
766
+ StdDeck_CardMask_RESET(hicards);
767
+ StdDeck_CardMask_RESET(locards);
768
+ OmahaHiLow8_Best(hand, board, &hival, &loval, &hicards, &locards);
769
+ if(low) {
770
+ best_handval = loval;
771
+ if(best_handval != LowHandVal_NOTHING)
772
+ best = locards;
773
+ } else {
774
+ best = hicards;
775
+ best_handval = hival;
776
+ }
777
+ } else {
778
+ CardMask cards;
779
+ CardMask dead;
780
+
781
+ StdDeck_CardMask_RESET(best);
782
+
783
+ StdDeck_CardMask_RESET(dead);
784
+ StdDeck_CardMask_OR(dead, dead, hand);
785
+ StdDeck_CardMask_NOT(dead, dead);
786
+
787
+ if(low) {
788
+ best_handval = LowHandVal_NOTHING;
789
+ } else {
790
+ best_handval = HandVal_NOTHING;
791
+ }
792
+
793
+ ENUMERATE_N_CARDS_D(cards, 5, dead,
794
+ {
795
+ HandVal handval;
796
+
797
+ if(low) {
798
+ handval = Hand_EVAL_LOW8(cards, 5);
799
+ } else {
800
+ handval = Hand_EVAL_N(cards, 5);
801
+ }
802
+
803
+ if(low ? (handval < best_handval) : (handval > best_handval)) {
804
+ best = cards;
805
+ best_handval = handval;
806
+ }
807
+ }
808
+ );
809
+ }
810
+
811
+ if(StdDeck_CardMask_IS_EMPTY(best)) {
812
+ best_handval = low ? 0x0FFFFFFF : 0;
813
+ }
814
+
815
+ result = rb_hash_new();
816
+ rb_hash_aset(result, rb_str_new2("value"), INT2NUM(best_handval));
817
+ rb_hash_aset(result, rb_str_new2("combination"), CardMask2SortedRbList(best, low));
818
+
819
+ return result;
820
+ }
821
+
822
+ static VALUE
823
+ t_eval(VALUE self, VALUE args)
824
+ {
825
+ int i;
826
+ int pockets_size;
827
+ int iterations = 0;
828
+ VALUE rbpockets = 0;
829
+ VALUE rbboard = 0;
830
+ VALUE rbdead = 0;
831
+ VALUE rbiterations = 0;
832
+ char* game = 0;
833
+ enum_gameparams_t* params = 0;
834
+
835
+ game = RSTRING_PTR(rb_hash_aref(args, rb_str_new2("game")));
836
+ rbpockets = rb_hash_aref(args, rb_str_new2("pockets"));
837
+ rbboard = rb_hash_aref(args, rb_str_new2("board"));
838
+ rbdead = rb_hash_aref(args, rb_str_new2("dead"));
839
+ rbiterations = rb_hash_aref(args, rb_str_new2("iterations"));
840
+
841
+ if( !NIL_P(rbiterations))
842
+ {
843
+ iterations = FIX2INT(rbiterations);
844
+ }
845
+
846
+ StdDeck_CardMask pockets[ENUM_MAXPLAYERS];
847
+ int numToDeal[ENUM_MAXPLAYERS];
848
+ CardMask dead_cards;
849
+ CardMask board_cards;
850
+
851
+ VALUE result = 0;
852
+
853
+ if(!strcmp(game, "holdem")) {
854
+ params = enumGameParams(game_holdem);
855
+ } else if(!strcmp(game, "holdem8")) {
856
+ params = enumGameParams(game_holdem8);
857
+ } else if(!strcmp(game, "omaha")) {
858
+ params = enumGameParams(game_omaha);
859
+ } else if(!strcmp(game, "omaha8")) {
860
+ params = enumGameParams(game_omaha8);
861
+ } else if(!strcmp(game, "7stud")) {
862
+ params = enumGameParams(game_7stud);
863
+ } else if(!strcmp(game, "7stud8")) {
864
+ params = enumGameParams(game_7stud8);
865
+ } else if(!strcmp(game, "7studnsq")) {
866
+ params = enumGameParams(game_7studnsq);
867
+ } else if(!strcmp(game, "razz")) {
868
+ params = enumGameParams(game_razz);
869
+ } else if(!strcmp(game, "5draw")) {
870
+ params = enumGameParams(game_5draw);
871
+ } else if(!strcmp(game, "5draw8")) {
872
+ params = enumGameParams(game_5draw8);
873
+ } else if(!strcmp(game, "5drawnsq")) {
874
+ params = enumGameParams(game_5drawnsq);
875
+ } else if(!strcmp(game, "lowball")) {
876
+ params = enumGameParams(game_lowball);
877
+ } else if(!strcmp(game, "lowball27")) {
878
+ params = enumGameParams(game_lowball27);
879
+ }
880
+
881
+ if(params == 0)
882
+ rb_fatal("game %s is not a valid value (holdem, holdem8, omaha, omaha8, 7stud, 7stud8, 7studnsq, razz, 5draw, 5draw8, 5drawnsq, lowball, lowball27)", game);
883
+
884
+ if (TYPE(rbpockets) != T_ARRAY)
885
+ rb_fatal("pockets must be list");
886
+
887
+ pockets_size = RARRAY_LENINT(rbpockets);
888
+
889
+ {
890
+ for(i = 0; i < pockets_size; i++) {
891
+ int count;
892
+ CardMask_RESET(pockets[i]);
893
+ VALUE rbpocket = rb_ary_entry(rbpockets, i);
894
+
895
+ count = rbList2CardMask(rbpocket, &pockets[i]);
896
+
897
+ if(count < 0)
898
+ goto err;
899
+ if(count < RARRAY_LEN(rbpocket))
900
+ numToDeal[i + 1] = RARRAY_LENINT(rbpocket) - count;
901
+ else
902
+ numToDeal[i + 1] = 0;
903
+ }
904
+ }
905
+
906
+
907
+ {
908
+ int count;
909
+ count = rbList2CardMask(rbboard, &board_cards);
910
+ if(count < 0)
911
+ goto err;
912
+ if(count < RARRAY_LENINT(rbboard))
913
+ numToDeal[0] = RARRAY_LENINT(rbboard) - count;
914
+ else
915
+ numToDeal[0] = 0;
916
+ }
917
+
918
+ if(!NIL_P(rbdead) && RARRAY_LEN(rbdead) > 0) {
919
+ if(rbList2CardMask(rbdead, &dead_cards) < 0){
920
+ rb_fatal("dead cards error");
921
+ }
922
+ }
923
+ else {
924
+ CardMask_RESET(dead_cards);
925
+ }
926
+
927
+ {
928
+ enum_result_t cresult;
929
+ int err;
930
+ memset(&cresult, '\0', sizeof(enum_result_t));
931
+
932
+ if(iterations > 0) {
933
+ err = rbenumSample(params->game, pockets, numToDeal, board_cards, dead_cards, pockets_size + 1, iterations, &cresult);
934
+ } else {
935
+ err = rbenumExhaustive(params->game, pockets, numToDeal, board_cards, dead_cards, pockets_size + 1, &cresult);
936
+ }
937
+ if(err != 0) {
938
+ rb_fatal("poker-eval: rbenum returned error code %d", err);
939
+ }
940
+
941
+ result = rb_hash_new();
942
+
943
+ VALUE info = rb_hash_new();
944
+ rb_hash_aset(info, rb_str_new2("samples"), INT2NUM(cresult.nsamples));
945
+ rb_hash_aset(info, rb_str_new2("haslopot"), INT2NUM(params->haslopot));
946
+ rb_hash_aset(info, rb_str_new2("hashipot"), INT2NUM(params->hashipot));
947
+
948
+ rb_hash_aset(result, rb_str_new2("info"), info);
949
+
950
+ VALUE list = rb_ary_new();
951
+ for(i = 0; i < pockets_size; i++) {
952
+ VALUE tmp = rb_hash_new();
953
+ rb_hash_aset(tmp, rb_str_new2("scoop"), INT2NUM(cresult.nscoop[i]));
954
+ rb_hash_aset(tmp, rb_str_new2("winhi"), INT2NUM(cresult.nwinhi[i]));
955
+ rb_hash_aset(tmp, rb_str_new2("losehi"), INT2NUM(cresult.nlosehi[i]));
956
+ rb_hash_aset(tmp, rb_str_new2("tiehi"), INT2NUM(cresult.ntiehi[i]));
957
+ rb_hash_aset(tmp, rb_str_new2("winlo"), INT2NUM(cresult.nwinlo[i]));
958
+ rb_hash_aset(tmp, rb_str_new2("loselo"), INT2NUM(cresult.nloselo[i]));
959
+ rb_hash_aset(tmp, rb_str_new2("tielo"), INT2NUM(cresult.ntielo[i]));
960
+ rb_hash_aset(tmp, rb_str_new2("ev"), INT2NUM((cresult.ev[i] / cresult.nsamples) * 1000));
961
+ rb_ary_push(list, tmp);
962
+ tmp = 0;
963
+ }
964
+ rb_hash_aset(result, rb_str_new2("eval"), list);
965
+ }
966
+
967
+ err:
968
+ return result;
969
+ }
970
+
971
+ VALUE cPokerEval;
972
+
973
+ void
974
+ Init_poker_eval_api()
975
+ {
976
+ cPokerEval = rb_define_class("PokerEval", rb_cObject);
977
+ rb_define_singleton_method(cPokerEval, "eval", t_eval, 1);
978
+ rb_define_singleton_method(cPokerEval, "eval_hand", t_eval_hand, 1);
979
+ rb_define_singleton_method(cPokerEval, "card2string", t_card2Rbstring, 1);
980
+ }
981
+