acpc_dealer 2.4.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Brickfile +6 -0
  3. data/README.md +7 -6
  4. data/Rakefile +30 -14
  5. data/acpc_dealer.gemspec +2 -1
  6. data/bin/acpc_dealer +4 -0
  7. data/ext/hand_evaluator/extconf.rb +10 -0
  8. data/lib/acpc_dealer/version.rb +1 -1
  9. data/lib/hand_evaluator.bundle +0 -0
  10. data/spec/dealer_runner_spec.rb +76 -72
  11. metadata +19 -82
  12. data/vendor/project_acpc_server/LICENCE +0 -23
  13. data/vendor/project_acpc_server/Makefile +0 -35
  14. data/vendor/project_acpc_server/README +0 -113
  15. data/vendor/project_acpc_server/README.submission +0 -42
  16. data/vendor/project_acpc_server/acpc_play_match.pl +0 -101
  17. data/vendor/project_acpc_server/bm_run_matches.c +0 -238
  18. data/vendor/project_acpc_server/bm_server.c +0 -1604
  19. data/vendor/project_acpc_server/bm_server.config +0 -78
  20. data/vendor/project_acpc_server/bm_widget.c +0 -230
  21. data/vendor/project_acpc_server/dealer.c +0 -1293
  22. data/vendor/project_acpc_server/evalHandTables +0 -4269
  23. data/vendor/project_acpc_server/example_player.c +0 -204
  24. data/vendor/project_acpc_server/example_player.limit.2p.sh +0 -3
  25. data/vendor/project_acpc_server/example_player.limit.3p.sh +0 -3
  26. data/vendor/project_acpc_server/example_player.nolimit.2p.sh +0 -3
  27. data/vendor/project_acpc_server/example_player.nolimit.3p.sh +0 -3
  28. data/vendor/project_acpc_server/game.c +0 -1792
  29. data/vendor/project_acpc_server/game.h +0 -253
  30. data/vendor/project_acpc_server/holdem.limit.2p.reverse_blinds.game +0 -13
  31. data/vendor/project_acpc_server/holdem.limit.3p.game +0 -13
  32. data/vendor/project_acpc_server/holdem.nolimit.2p.reverse_blinds.game +0 -12
  33. data/vendor/project_acpc_server/holdem.nolimit.3p.game +0 -12
  34. data/vendor/project_acpc_server/kuhn.limit.3p.game +0 -14
  35. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf1.sh +0 -3
  36. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf2.sh +0 -3
  37. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player.sf3.sh +0 -3
  38. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/LICENCE +0 -23
  39. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/Makefile +0 -127
  40. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/README.md +0 -35
  41. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/CExceptionConfig.h +0 -12
  42. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/dealer_connection.c +0 -49
  43. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/dealer_connection.h +0 -22
  44. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/kuhn_3p_equilibrium_player.c +0 -483
  45. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/kuhn_3p_equilibrium_player.h +0 -108
  46. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/main.c +0 -84
  47. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/player_config.c +0 -253
  48. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/src/player_config.h +0 -21
  49. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/support/test_helper.c +0 -45
  50. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/support/test_helper.h +0 -27
  51. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player.c +0 -698
  52. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_1.c +0 -324
  53. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_2.c +0 -262
  54. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/test/test_kuhn_3p_equilibrium_player_sub_family_3.c +0 -177
  55. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/docs/license.txt +0 -30
  56. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/docs/readme.txt +0 -242
  57. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/lib/CException.c +0 -43
  58. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/lib/CException.h +0 -86
  59. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/release/build.info +0 -2
  60. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/cexception/release/version.info +0 -2
  61. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/History.md +0 -27
  62. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/Makefile +0 -8
  63. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/Readme.md +0 -103
  64. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/package.json +0 -9
  65. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/src/commander.c +0 -250
  66. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/src/commander.h +0 -88
  67. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/commander.c/test.c +0 -34
  68. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/colour_prompt.rb +0 -94
  69. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/colour_reporter.rb +0 -39
  70. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_config.yml +0 -36
  71. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_module.rb +0 -202
  72. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/generate_test_runner.rb +0 -316
  73. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/test_file_filter.rb +0 -23
  74. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/auto/unity_test_summary.rb +0 -139
  75. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/docs/Unity Summary.txt +0 -216
  76. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/docs/license.txt +0 -31
  77. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/release/build.info +0 -2
  78. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/release/version.info +0 -2
  79. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity.c +0 -1146
  80. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity.h +0 -245
  81. data/vendor/project_acpc_server/kuhn_3p_equilibrium_player/vendor/unity/src/unity_internals.h +0 -546
  82. data/vendor/project_acpc_server/net.c +0 -218
  83. data/vendor/project_acpc_server/net.h +0 -62
  84. data/vendor/project_acpc_server/play_match.pl +0 -99
  85. data/vendor/project_acpc_server/protocol.md +0 -239
  86. data/vendor/project_acpc_server/protocol.odt +0 -0
  87. data/vendor/project_acpc_server/protocol.pdf +0 -0
  88. data/vendor/project_acpc_server/rng.c +0 -139
  89. data/vendor/project_acpc_server/rng.h +0 -63
  90. data/vendor/project_acpc_server/validate_submission.pl +0 -546
@@ -1,204 +0,0 @@
1
- /*
2
- Copyright (C) 2011 by the Computer Poker Research Group, University of Alberta
3
- */
4
-
5
- #include <stdlib.h>
6
- #include <stdio.h>
7
- #include <assert.h>
8
- #include <string.h>
9
- #include <unistd.h>
10
- #include <netdb.h>
11
- #include <sys/socket.h>
12
- #include <sys/time.h>
13
- #include <netinet/in.h>
14
- #include <netinet/tcp.h>
15
- #include <getopt.h>
16
- #include "game.h"
17
- #include "rng.h"
18
- #include "net.h"
19
-
20
- int main( int argc, char **argv )
21
- {
22
- int sock, len, r, a;
23
- int32_t min, max;
24
- uint16_t port;
25
- double p;
26
- Game *game;
27
- MatchState state;
28
- Action action;
29
- FILE *file, *toServer, *fromServer;
30
- struct timeval tv;
31
- double probs[ NUM_ACTION_TYPES ];
32
- double actionProbs[ NUM_ACTION_TYPES ];
33
- rng_state_t rng;
34
- char line[ MAX_LINE_LEN ];
35
-
36
- /* we make some assumptions about the actions - check them here */
37
- assert( NUM_ACTION_TYPES == 3 );
38
-
39
- if( argc < 4 ) {
40
-
41
- fprintf( stderr, "usage: player game server port\n" );
42
- exit( EXIT_FAILURE );
43
- }
44
-
45
- /* Define the probabilities of actions for the player */
46
- probs[ a_fold ] = 0.06;
47
- probs[ a_call ] = ( 1.0 - probs[ a_fold ] ) * 0.5;
48
- probs[ a_raise ] = ( 1.0 - probs[ a_fold ] ) * 0.5;
49
-
50
- /* Initialize the player's random number state using time */
51
- gettimeofday( &tv, NULL );
52
- init_genrand( &rng, tv.tv_usec );
53
-
54
- /* get the game */
55
- file = fopen( argv[ 1 ], "r" );
56
- if( file == NULL ) {
57
-
58
- fprintf( stderr, "ERROR: could not open game %s\n", argv[ 1 ] );
59
- exit( EXIT_FAILURE );
60
- }
61
- game = readGame( file );
62
- if( game == NULL ) {
63
-
64
- fprintf( stderr, "ERROR: could not read game %s\n", argv[ 1 ] );
65
- exit( EXIT_FAILURE );
66
- }
67
- fclose( file );
68
-
69
- /* connect to the dealer */
70
- if( sscanf( argv[ 3 ], "%"SCNu16, &port ) < 1 ) {
71
-
72
- fprintf( stderr, "ERROR: invalid port %s\n", argv[ 3 ] );
73
- exit( EXIT_FAILURE );
74
- }
75
- sock = connectTo( argv[ 2 ], port );
76
- if( sock < 0 ) {
77
-
78
- exit( EXIT_FAILURE );
79
- }
80
- toServer = fdopen( sock, "w" );
81
- fromServer = fdopen( sock, "r" );
82
- if( toServer == NULL || fromServer == NULL ) {
83
-
84
- fprintf( stderr, "ERROR: could not get socket streams\n" );
85
- exit( EXIT_FAILURE );
86
- }
87
-
88
- /* send version string to dealer */
89
- if( fprintf( toServer, "VERSION:%"PRIu32".%"PRIu32".%"PRIu32"\n",
90
- VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ) != 14 ) {
91
-
92
- fprintf( stderr, "ERROR: could not get send version to server\n" );
93
- exit( EXIT_FAILURE );
94
- }
95
- fflush( toServer );
96
-
97
- /* play the game! */
98
- while( fgets( line, MAX_LINE_LEN, fromServer ) ) {
99
-
100
- /* ignore comments */
101
- if( line[ 0 ] == '#' || line[ 0 ] == ';' ) {
102
- continue;
103
- }
104
-
105
- len = readMatchState( line, game, &state );
106
- if( len < 0 ) {
107
-
108
- fprintf( stderr, "ERROR: could not read state %s", line );
109
- exit( EXIT_FAILURE );
110
- }
111
-
112
- if( stateFinished( &state.state ) ) {
113
- /* ignore the game over message */
114
-
115
- continue;
116
- }
117
-
118
- if( currentPlayer( game, &state.state ) != state.viewingPlayer ) {
119
- /* we're not acting */
120
-
121
- continue;
122
- }
123
-
124
- /* add a colon (guaranteed to fit because we read a new-line in fgets) */
125
- line[ len ] = ':';
126
- ++len;
127
-
128
- /* build the set of valid actions */
129
- p = 0;
130
- for( a = 0; a < NUM_ACTION_TYPES; ++a ) {
131
-
132
- actionProbs[ a ] = 0.0;
133
- }
134
-
135
- /* consider fold */
136
- action.type = a_fold;
137
- action.size = 0;
138
- if( isValidAction( game, &state.state, 0, &action ) ) {
139
-
140
- actionProbs[ a_fold ] = probs[ a_fold ];
141
- p += probs[ a_fold ];
142
- }
143
-
144
- /* consider call */
145
- action.type = a_call;
146
- action.size = 0;
147
- actionProbs[ a_call ] = probs[ a_call ];
148
- p += probs[ a_call ];
149
-
150
- /* consider raise */
151
- if( raiseIsValid( game, &state.state, &min, &max ) ) {
152
-
153
- actionProbs[ a_raise ] = probs[ a_raise ];
154
- p += probs[ a_raise ];
155
- }
156
-
157
- /* normalise the probabilities */
158
- assert( p > 0.0 );
159
- for( a = 0; a < NUM_ACTION_TYPES; ++a ) {
160
-
161
- actionProbs[ a ] /= p;
162
- }
163
-
164
- /* choose one of the valid actions at random */
165
- p = genrand_real2( &rng );
166
- for( a = 0; a < NUM_ACTION_TYPES - 1; ++a ) {
167
-
168
- if( p <= actionProbs[ a ] ) {
169
-
170
- break;
171
- }
172
- p -= actionProbs[ a ];
173
- }
174
- action.type = (enum ActionType)a;
175
- if( a == a_raise ) {
176
-
177
- action.size = min + genrand_int32( &rng ) % ( max - min + 1 );
178
- }
179
-
180
- /* do the action! */
181
- assert( isValidAction( game, &state.state, 0, &action ) );
182
- r = printAction( game, &action, MAX_LINE_LEN - len - 2,
183
- &line[ len ] );
184
- if( r < 0 ) {
185
-
186
- fprintf( stderr, "ERROR: line too long after printing action\n" );
187
- exit( EXIT_FAILURE );
188
- }
189
- len += r;
190
- line[ len ] = '\r';
191
- ++len;
192
- line[ len ] = '\n';
193
- ++len;
194
-
195
- if( fwrite( line, 1, len, toServer ) != len ) {
196
-
197
- fprintf( stderr, "ERROR: could not get send response to server\n" );
198
- exit( EXIT_FAILURE );
199
- }
200
- fflush( toServer );
201
- }
202
-
203
- return EXIT_SUCCESS;
204
- }
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- THIS_DIR=$( cd "$( dirname "$0" )" && pwd )
3
- cd $THIS_DIR && ./example_player holdem.limit.2p.reverse_blinds.game $1 $2
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- THIS_DIR=$( cd "$( dirname "$0" )" && pwd )
3
- cd $THIS_DIR && ./example_player holdem.limit.3p.game $1 $2
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- THIS_DIR=$( cd "$( dirname "$0" )" && pwd )
3
- cd $THIS_DIR && ./example_player holdem.nolimit.2p.reverse_blinds.game $1 $2
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- THIS_DIR=$( cd "$( dirname "$0" )" && pwd )
3
- cd $THIS_DIR && ./example_player holdem.nolimit.3p.game $1 $2
@@ -1,1792 +0,0 @@
1
- /*
2
- Copyright (C) 2011 by the Computer Poker Research Group, University of Alberta
3
- */
4
-
5
- #include <stdlib.h>
6
- #include <stdio.h>
7
- #include <string.h>
8
- #include <ctype.h>
9
- #include <assert.h>
10
- #define __STDC_LIMIT_MACROS
11
- #include <stdint.h>
12
- #include "game.h"
13
- #include "rng.h"
14
-
15
- #include "evalHandTables"
16
-
17
-
18
- static enum ActionType charToAction[ 256 ] = {
19
- /* 0x0X */
20
- a_invalid, a_invalid, a_invalid, a_invalid,
21
- a_invalid, a_invalid, a_invalid, a_invalid,
22
- a_invalid, a_invalid, a_invalid, a_invalid,
23
- a_invalid, a_invalid, a_invalid, a_invalid,
24
- /* 0x1X */
25
- a_invalid, a_invalid, a_invalid, a_invalid,
26
- a_invalid, a_invalid, a_invalid, a_invalid,
27
- a_invalid, a_invalid, a_invalid, a_invalid,
28
- a_invalid, a_invalid, a_invalid, a_invalid,
29
- /* 0x2X */
30
- a_invalid, a_invalid, a_invalid, a_invalid,
31
- a_invalid, a_invalid, a_invalid, a_invalid,
32
- a_invalid, a_invalid, a_invalid, a_invalid,
33
- a_invalid, a_invalid, a_invalid, a_invalid,
34
- /* 0x3X */
35
- a_invalid, a_invalid, a_invalid, a_invalid,
36
- a_invalid, a_invalid, a_invalid, a_invalid,
37
- a_invalid, a_invalid, a_invalid, a_invalid,
38
- a_invalid, a_invalid, a_invalid, a_invalid,
39
- /* 0x4X */
40
- a_invalid, a_invalid, a_raise, a_call,
41
- a_invalid, a_invalid, a_fold, a_invalid,
42
- a_invalid, a_invalid, a_invalid, a_call,
43
- a_invalid, a_invalid, a_invalid, a_invalid,
44
- /* 0x5X */
45
- a_invalid, a_invalid, a_raise, a_invalid,
46
- a_invalid, a_invalid, a_invalid, a_invalid,
47
- a_invalid, a_invalid, a_invalid, a_invalid,
48
- a_invalid, a_invalid, a_invalid, a_invalid,
49
- /* 0x6X */
50
- a_invalid, a_invalid, a_raise, a_call,
51
- a_invalid, a_invalid, a_fold, a_invalid,
52
- a_invalid, a_invalid, a_invalid, a_call,
53
- a_invalid, a_invalid, a_invalid, a_invalid,
54
- /* 0x7X */
55
- a_invalid, a_invalid, a_raise, a_invalid,
56
- a_invalid, a_invalid, a_invalid, a_invalid,
57
- a_invalid, a_invalid, a_invalid, a_invalid,
58
- a_invalid, a_invalid, a_invalid, a_invalid,
59
- /* 0x8X */
60
- a_invalid, a_invalid, a_invalid, a_invalid,
61
- a_invalid, a_invalid, a_invalid, a_invalid,
62
- a_invalid, a_invalid, a_invalid, a_invalid,
63
- a_invalid, a_invalid, a_invalid, a_invalid,
64
- /* 0x9X */
65
- a_invalid, a_invalid, a_invalid, a_invalid,
66
- a_invalid, a_invalid, a_invalid, a_invalid,
67
- a_invalid, a_invalid, a_invalid, a_invalid,
68
- a_invalid, a_invalid, a_invalid, a_invalid,
69
- /* 0xAX */
70
- a_invalid, a_invalid, a_invalid, a_invalid,
71
- a_invalid, a_invalid, a_invalid, a_invalid,
72
- a_invalid, a_invalid, a_invalid, a_invalid,
73
- a_invalid, a_invalid, a_invalid, a_invalid,
74
- /* 0xBX */
75
- a_invalid, a_invalid, a_invalid, a_invalid,
76
- a_invalid, a_invalid, a_invalid, a_invalid,
77
- a_invalid, a_invalid, a_invalid, a_invalid,
78
- a_invalid, a_invalid, a_invalid, a_invalid,
79
- /* 0xCX */
80
- a_invalid, a_invalid, a_invalid, a_invalid,
81
- a_invalid, a_invalid, a_invalid, a_invalid,
82
- a_invalid, a_invalid, a_invalid, a_invalid,
83
- a_invalid, a_invalid, a_invalid, a_invalid,
84
- /* 0xDX */
85
- a_invalid, a_invalid, a_invalid, a_invalid,
86
- a_invalid, a_invalid, a_invalid, a_invalid,
87
- a_invalid, a_invalid, a_invalid, a_invalid,
88
- a_invalid, a_invalid, a_invalid, a_invalid,
89
- /* 0xEX */
90
- a_invalid, a_invalid, a_invalid, a_invalid,
91
- a_invalid, a_invalid, a_invalid, a_invalid,
92
- a_invalid, a_invalid, a_invalid, a_invalid,
93
- a_invalid, a_invalid, a_invalid, a_invalid,
94
- /* 0xFX */
95
- a_invalid, a_invalid, a_invalid, a_invalid,
96
- a_invalid, a_invalid, a_invalid, a_invalid,
97
- a_invalid, a_invalid, a_invalid, a_invalid,
98
- a_invalid, a_invalid, a_invalid, a_invalid
99
- };
100
-
101
- static char actionChars[ a_invalid +1 ] = "fcr";
102
-
103
- static char suitChars[ MAX_SUITS +1] = "cdhs";
104
- static char rankChars[ MAX_RANKS +1] = "23456789TJQKA";
105
-
106
-
107
- static int consumeSpaces( const char *string, int consumeEqual )
108
- {
109
- int i;
110
-
111
- for( i = 0; string[ i ] != 0
112
- && ( isspace( string[ i ] )
113
- || ( consumeEqual && string[ i ] == '=' ) );
114
- ++i ) {
115
- }
116
-
117
- return i;
118
- }
119
-
120
- /* reads up to numItems with scanf format itemFormat from string,
121
- returning item i in *items[ i ]
122
- ignore the '=' character if consumeEqual is non-zero
123
- returns the number of characters consumed doing this in charsConsumed
124
- returns the number of items read */
125
- static int readItems( const char *itemFormat, const int numItems,
126
- const char *string, const int consumeEqual,
127
- void *items, const size_t itemSize,
128
- int *charsConsumed )
129
- {
130
- int i, c, r;
131
- char *fmt;
132
-
133
- i = strlen( itemFormat );
134
- fmt = (char*)malloc( i + 3 );
135
- assert( fmt != 0 );
136
- strcpy( fmt, itemFormat );
137
- fmt[ i ] = '%';
138
- fmt[ i + 1 ] = 'n';
139
- fmt[ i + 2 ] = 0;
140
-
141
- c = 0;
142
- for( i = 0; i < numItems; ++i ) {
143
-
144
- c += consumeSpaces( &string[ c ], consumeEqual );
145
- if( sscanf( &string[ c ], fmt, items + i * itemSize, &r ) < 1 ) {
146
- break;
147
- }
148
- c += r;
149
- }
150
-
151
- free( fmt );
152
-
153
- *charsConsumed = c;
154
- return i;
155
- }
156
-
157
- Game *readGame( FILE *file )
158
- {
159
- int stackRead, blindRead, raiseSizeRead, boardCardsRead, c, t;
160
- char line[ MAX_LINE_LEN ];
161
- Game *game;
162
-
163
- game = (Game*)malloc( sizeof( *game ) );
164
- assert( game != 0 );
165
- stackRead = 4;
166
- for( c = 0; c < MAX_ROUNDS; ++c ) {
167
- game->stack[ c ] = INT32_MAX;
168
- }
169
- blindRead = 0;
170
- raiseSizeRead = 0;
171
- game->bettingType = limitBetting;
172
- game->numPlayers = 0;
173
- game->numRounds = 0;
174
- for( c = 0; c < MAX_ROUNDS; ++c ) {
175
- game->firstPlayer[ c ] = 1;
176
- }
177
- for( c = 0; c < MAX_ROUNDS; ++c ) {
178
- game->maxRaises[ c ] = UINT8_MAX;
179
- }
180
- game->numSuits = 0;
181
- game->numRanks = 0;
182
- game->numHoleCards = 0;
183
- boardCardsRead = 0;
184
-
185
- while( fgets( line, MAX_LINE_LEN, file ) ) {
186
-
187
- if( line[ 0 ] == '#' || line[ 0 ] == '\n' ) {
188
- continue;
189
- }
190
-
191
- if( !strncasecmp( line, "end gamedef", 11 ) ) {
192
-
193
- break;
194
- } else if( !strncasecmp( line, "gamedef", 7 ) ) {
195
-
196
- continue;
197
- } else if( !strncasecmp( line, "stack", 5 ) ) {
198
-
199
- stackRead = readItems( "%"SCNd32, MAX_PLAYERS, &line[ 5 ],
200
- 1, game->stack, 4, &c );
201
- } else if( !strncasecmp( line, "blind", 5 ) ) {
202
-
203
- blindRead = readItems( "%"SCNd32, MAX_PLAYERS, &line[ 5 ],
204
- 1, game->blind, 4, &c );
205
- } else if( !strncasecmp( line, "raisesize", 9 ) ) {
206
-
207
- raiseSizeRead = readItems( "%"SCNd32, MAX_PLAYERS, &line[ 9 ],
208
- 1, game->raiseSize, 4, &c );
209
- } else if( !strncasecmp( line, "limit", 5 ) ) {
210
-
211
- game->bettingType = limitBetting;
212
- } else if( !strncasecmp( line, "nolimit", 7 ) ) {
213
-
214
- game->bettingType = noLimitBetting;
215
- } else if( !strncasecmp( line, "numplayers", 10 ) ) {
216
-
217
- readItems( "%"SCNu8, 1, &line[ 10 ], 1, &game->numPlayers, 1, &c );
218
- } else if( !strncasecmp( line, "numrounds", 9 ) ) {
219
-
220
- readItems( "%"SCNu8, 1, &line[ 9 ], 1, &game->numRounds, 1, &c );
221
- } else if( !strncasecmp( line, "firstplayer", 11 ) ) {
222
-
223
- readItems( "%"SCNu8, MAX_ROUNDS, &line[ 11 ],
224
- 1, game->firstPlayer, 1, &c );
225
- } else if( !strncasecmp( line, "maxraises", 9 ) ) {
226
-
227
- readItems( "%"SCNu8, MAX_ROUNDS, &line[ 9 ],
228
- 1, game->maxRaises, 1, &c );
229
- } else if( !strncasecmp( line, "numsuits", 8 ) ) {
230
-
231
- readItems( "%"SCNu8, 1, &line[ 8 ], 1, &game->numSuits, 1, &c );
232
- } else if( !strncasecmp( line, "numranks", 8 ) ) {
233
-
234
- readItems( "%"SCNu8, 1, &line[ 8 ], 1, &game->numRanks, 1, &c );
235
- } else if( !strncasecmp( line, "numholecards", 12 ) ) {
236
-
237
- readItems( "%"SCNu8, 1, &line[ 12 ], 1, &game->numHoleCards, 1, &c );
238
- } else if( !strncasecmp( line, "numboardcards", 13 ) ) {
239
-
240
- boardCardsRead = readItems( "%"SCNu8, MAX_ROUNDS, &line[ 13 ],
241
- 1, game->numBoardCards, 1, &c );
242
- }
243
- }
244
-
245
- /* do sanity checks */
246
- if( game->numRounds == 0 || game->numRounds > MAX_ROUNDS ) {
247
-
248
- fprintf( stderr, "invalid number of rounds: %"PRIu8"\n", game->numRounds );
249
- free( game );
250
- return NULL;
251
- }
252
-
253
- if( game->numPlayers < 2 || game->numPlayers > MAX_PLAYERS ) {
254
-
255
- fprintf( stderr, "invalid number of players: %"PRIu8"\n",
256
- game->numPlayers );
257
- free( game );
258
- return NULL;
259
- }
260
-
261
- if( stackRead < game->numPlayers ) {
262
-
263
- fprintf( stderr, "only read %"PRIu8" stack sizes, need %"PRIu8"\n",
264
- stackRead, game->numPlayers );
265
- free( game );
266
- return NULL;
267
- }
268
-
269
- if( blindRead < game->numPlayers ) {
270
-
271
- fprintf( stderr, "only read %"PRIu8" blinds, need %"PRIu8"\n",
272
- blindRead, game->numPlayers );
273
- free( game );
274
- return NULL;
275
- }
276
- for( c = 0; c < game->numPlayers; ++c ) {
277
-
278
- if( game->blind[ c ] > game->stack[ c ] ) {
279
- fprintf( stderr, "blind for player %d is greater than stack size\n",
280
- c + 1 );
281
- free( game );
282
- return NULL;
283
- }
284
- }
285
-
286
- if( game->bettingType == limitBetting
287
- && raiseSizeRead < game->numRounds ) {
288
-
289
- fprintf( stderr, "only read %"PRIu8" raise sizes, need %"PRIu8"\n",
290
- raiseSizeRead, game->numRounds );
291
- free( game );
292
- return NULL;
293
- }
294
-
295
- for( c = 0; c < game->numRounds; ++c ) {
296
-
297
- if( game->firstPlayer[ c ] == 0
298
- || game->firstPlayer[ c ] > game->numPlayers ) {
299
-
300
- fprintf( stderr, "invalid first player %"PRIu8" on round %d\n",
301
- game->firstPlayer[ c ], c + 1 );
302
- free( game );
303
- return NULL;
304
- }
305
-
306
- --game->firstPlayer[ c ];
307
- }
308
-
309
- if( game->numSuits == 0 || game->numSuits > MAX_SUITS ) {
310
-
311
- fprintf( stderr, "invalid number of suits: %"PRIu8"\n", game->numSuits );
312
- free( game );
313
- return NULL;
314
- }
315
-
316
- if( game->numRanks == 0 || game->numRanks > MAX_RANKS ) {
317
-
318
- fprintf( stderr, "invalid number of ranks: %"PRIu8"\n", game->numRanks );
319
- free( game );
320
- return NULL;
321
- }
322
-
323
- if( game->numHoleCards == 0 || game->numHoleCards > MAX_HOLE_CARDS ) {
324
-
325
- fprintf( stderr, "invalid number of hole cards: %"PRIu8"\n",
326
- game->numHoleCards );
327
- free( game );
328
- return NULL;
329
- }
330
-
331
- if( boardCardsRead < game->numRounds ) {
332
-
333
- fprintf( stderr, "only read %"PRIu8" board card numbers, need %"PRIu8"\n",
334
- boardCardsRead, game->numRounds );
335
- free( game );
336
- return NULL;
337
- }
338
-
339
- t = game->numHoleCards * game->numPlayers;
340
- for( c = 0; c < game->numRounds; ++c ) {
341
- t += game->numBoardCards[ c ];
342
- }
343
- if( t > game->numSuits * game->numRanks ) {
344
-
345
- fprintf( stderr, "too many hole and board cards for specified deck\n" );
346
- free( game );
347
- return NULL;
348
- }
349
-
350
- return game;
351
- }
352
-
353
- void printGame( FILE *file, const Game *game )
354
- {
355
- int i;
356
-
357
- fprintf( file, "GAMEDEF\n" );
358
-
359
- if( game->bettingType == noLimitBetting ) {
360
- fprintf( file, "nolimit\n" );
361
- } else {
362
- fprintf( file, "limit\n" );
363
- }
364
-
365
- fprintf( file, "numPlayers = %"PRIu8"\n", game->numPlayers );
366
-
367
- fprintf( file, "numRounds = %"PRIu8"\n", game->numRounds );
368
-
369
- for( i = 0; i < game->numPlayers; ++i ) {
370
- if( game->stack[ i ] < INT32_MAX ) {
371
-
372
- fprintf( file, "stack =" );
373
- for( i = 0; i < game->numPlayers; ++i ) {
374
- fprintf( file, " %"PRId32, game->stack[ i ] );
375
- }
376
- fprintf( file, "\n" );
377
-
378
- break;
379
- }
380
- }
381
-
382
- fprintf( file, "blind =" );
383
- for( i = 0; i < game->numPlayers; ++i ) {
384
- fprintf( file, " %"PRId32, game->blind[ i ] );
385
- }
386
- fprintf( file, "\n" );
387
-
388
- if( game->bettingType == limitBetting ) {
389
-
390
- fprintf( file, "raiseSize =" );
391
- for( i = 0; i < game->numRounds; ++i ) {
392
- fprintf( file, " %"PRId32, game->raiseSize[ i ] );
393
- }
394
- fprintf( file, "\n" );
395
- }
396
-
397
- for( i = 0; i < game->numRounds; ++i ) {
398
- if( game->firstPlayer[ i ] != 0 ) {
399
-
400
- fprintf( file, "firstPlayer =" );
401
- for( i = 0; i < game->numRounds; ++i ) {
402
- fprintf( file, " %"PRIu8, game->firstPlayer[ i ] + 1 );
403
- }
404
- fprintf( file, "\n" );
405
-
406
- break;
407
- }
408
- }
409
-
410
- for( i = 0; i < game->numRounds; ++i ) {
411
- if( game->maxRaises[ i ] != UINT8_MAX ) {
412
-
413
- fprintf( file, "maxRaises =" );
414
- for( i = 0; i < game->numRounds; ++i ) {
415
- fprintf( file, " %"PRIu8, game->maxRaises[ i ] );
416
- }
417
- fprintf( file, "\n" );
418
-
419
- break;
420
- }
421
- }
422
-
423
- fprintf( file, "numSuits = %"PRIu8"\n", game->numSuits );
424
-
425
- fprintf( file, "numRanks = %"PRIu8"\n", game->numRanks );
426
-
427
- fprintf( file, "numHoleCards = %"PRIu8"\n", game->numHoleCards );
428
-
429
- fprintf( file, "numBoardCards =" );
430
- for( i = 0; i < game->numRounds; ++i ) {
431
- fprintf( file, " %"PRIu8, game->numBoardCards[ i ] );
432
- }
433
- fprintf( file, "\n" );
434
-
435
- fprintf( file, "END GAMEDEF\n" );
436
- }
437
-
438
- uint8_t bcStart( const Game *game, const uint8_t round )
439
- {
440
- int r;
441
- uint8_t start;
442
-
443
- start = 0;
444
- for( r = 0; r < round; ++r ) {
445
-
446
- start += game->numBoardCards[ r ];
447
- }
448
-
449
- return start;
450
- }
451
-
452
- uint8_t sumBoardCards( const Game *game, const uint8_t round )
453
- {
454
- int r;
455
- uint8_t total;
456
-
457
- total = 0;
458
- for( r = 0; r <= round; ++r ) {
459
- total += game->numBoardCards[ r ];
460
- }
461
-
462
- return total;
463
- }
464
-
465
- static uint8_t nextPlayer( const Game *game, const State *state,
466
- const uint8_t curPlayer )
467
- {
468
- uint8_t n;
469
-
470
- n = curPlayer;
471
- do {
472
- n = ( n + 1 ) % game->numPlayers;
473
- } while( state->playerFolded[ n ]
474
- || state->spent[ n ] >= game->stack[ n ] );
475
-
476
- return n;
477
- }
478
-
479
- uint8_t currentPlayer( const Game *game, const State *state )
480
- {
481
- /* if action has already been made, compute next player from last player */
482
- if( state->numActions[ state->round ] ) {
483
- return nextPlayer( game, state, state->actingPlayer[ state->round ]
484
- [ state->numActions[ state->round ] - 1 ] );
485
- }
486
-
487
- /* first player in a round is determined by the game and round
488
- use nextPlayer() because firstPlayer[round] might be unable to act */
489
- return nextPlayer( game, state, game->firstPlayer[ state->round ]
490
- + game->numPlayers - 1 );
491
- }
492
-
493
- uint8_t numRaises( const State *state )
494
- {
495
- int i;
496
- uint8_t ret;
497
-
498
- ret = 0;
499
- for( i = 0; i < state->numActions[ state->round ]; ++i ) {
500
- if( state->action[ state->round ][ i ].type == a_raise ) {
501
- ++ret;
502
- }
503
- }
504
-
505
- return ret;
506
- }
507
-
508
- uint8_t numFolded( const Game *game, const State *state )
509
- {
510
- int p;
511
- uint8_t ret;
512
-
513
- ret = 0;
514
- for( p = 0; p < game->numPlayers; ++p ) {
515
- if( state->playerFolded[ p ] ) {
516
- ++ret;
517
- }
518
- }
519
-
520
- return ret;
521
- }
522
-
523
- uint8_t numCalled( const Game *game, const State *state )
524
- {
525
- int i;
526
- uint8_t ret, p;
527
-
528
- ret = 0;
529
- for( i = state->numActions[ state->round ]; i > 0; --i ) {
530
-
531
- p = state->actingPlayer[ state->round ][ i - 1 ];
532
-
533
- if( state->action[ state->round ][ i - 1 ].type == a_raise ) {
534
- /* player initiated the bet, so they've called it */
535
-
536
- if( state->spent[ p ] < game->stack[ p ] ) {
537
- /* player is not all-in, so they're still acting */
538
-
539
- ++ret;
540
- }
541
-
542
- /* this is the start of the current bet, so we're finished */
543
- return ret;
544
- } else if( state->action[ state->round ][ i - 1 ].type == a_call ) {
545
-
546
- if( state->spent[ p ] < game->stack[ p ] ) {
547
- /* player is not all-in, so they're still acting */
548
-
549
- ++ret;
550
- }
551
- }
552
- }
553
-
554
- return ret;
555
- }
556
-
557
- uint8_t numAllIn( const Game *game, const State *state )
558
- {
559
- int p;
560
- uint8_t ret;
561
-
562
- ret = 0;
563
- for( p = 0; p < game->numPlayers; ++p ) {
564
- if( state->spent[ p ] >= game->stack[ p ] ) {
565
- ++ret;
566
- }
567
- }
568
-
569
- return ret;
570
- }
571
-
572
- uint8_t numActingPlayers( const Game *game, const State *state )
573
- {
574
- int p;
575
- uint8_t ret;
576
-
577
- ret = 0;
578
- for( p = 0; p < game->numPlayers; ++p ) {
579
- if( state->playerFolded[ p ] == 0
580
- && state->spent[ p ] < game->stack[ p ] ) {
581
- ++ret;
582
- }
583
- }
584
-
585
- return ret;
586
- }
587
-
588
- void initState( const Game *game, const uint32_t handId, State *state )
589
- {
590
- int p, r;
591
-
592
- state->handId = handId;
593
-
594
- state->maxSpent = 0;
595
- for( p = 0; p < game->numPlayers; ++p ) {
596
-
597
- state->spent[ p ] = game->blind[ p ];
598
- if( game->blind[ p ] > state->maxSpent ) {
599
-
600
- state->maxSpent = game->blind[ p ];
601
- }
602
- }
603
-
604
- if( game->bettingType == noLimitBetting ) {
605
- /* no-limit games need to keep track of the minimum bet */
606
-
607
- if( state->maxSpent ) {
608
- /* we'll have to call the big blind and then raise by that
609
- amount, so the minimum raise-to is 2*maximum blinds */
610
-
611
- state->minNoLimitRaiseTo = state->maxSpent * 2;
612
- } else {
613
- /* need to bet at least one chip, and there are no blinds/ante */
614
-
615
- state->minNoLimitRaiseTo = 1;
616
- }
617
- } else {
618
- /* no need to worry about minimum raises outside of no-limit games */
619
-
620
- state->minNoLimitRaiseTo = 0;
621
- }
622
-
623
- for( p = 0; p < game->numPlayers; ++p ) {
624
-
625
- state->spent[ p ] = game->blind[ p ];
626
-
627
- if( game->blind[ p ] > state->maxSpent ) {
628
- state->maxSpent = game->blind[ p ];
629
- }
630
-
631
- state->playerFolded[ p ] = 0;
632
- }
633
-
634
- for( r = 0; r < game->numRounds; ++r ) {
635
-
636
- state->numActions[ r ] = 0;
637
- }
638
-
639
- state->round = 0;
640
-
641
- state->finished = 0;
642
- }
643
-
644
- static uint8_t dealCard( rng_state_t *rng, uint8_t *deck, const int numCards )
645
- {
646
- int i;
647
- uint8_t ret;
648
-
649
- i = genrand_int32( rng ) % numCards;
650
- ret = deck[ i ];
651
- deck[ i ] = deck[ numCards - 1 ];
652
-
653
- return ret;
654
- }
655
-
656
- void dealCards( const Game *game, rng_state_t *rng, State *state )
657
- {
658
- int r, s, numCards, i, p;
659
- uint8_t deck[ MAX_RANKS * MAX_SUITS ];
660
-
661
- numCards = 0;
662
- for( s = MAX_SUITS - game->numSuits; s < MAX_SUITS; ++s ) {
663
-
664
- for( r = MAX_RANKS - game->numRanks; r < MAX_RANKS; ++r ) {
665
-
666
- deck[ numCards ] = makeCard( r, s );
667
- ++numCards;
668
- }
669
- }
670
-
671
- for( p = 0; p < game->numPlayers; ++p ) {
672
-
673
- for( i = 0; i < game->numHoleCards; ++i ) {
674
-
675
- state->holeCards[ p ][ i ] = dealCard( rng, deck, numCards );
676
- --numCards;
677
- }
678
- }
679
-
680
- s = 0;
681
- for( r = 0; r < game->numRounds; ++r ) {
682
-
683
- for( i = 0; i < game->numBoardCards[ r ]; ++i ) {
684
-
685
- state->boardCards[ s ] = dealCard( rng, deck, numCards );
686
- --numCards;
687
- ++s;
688
- }
689
- }
690
- }
691
-
692
- /* check whether some portions of a state are equal,
693
- common to both statesEqual and matchStatesEqual */
694
- static int statesEqualCommon( const Game *game, const State *a,
695
- const State *b )
696
- {
697
- int r, i, t;
698
-
699
- /* is it the same hand? */
700
- if( a->handId != b->handId ) {
701
- return 0;
702
- }
703
-
704
- /* make sure the betting is the same */
705
- if( a->round != b->round ) {
706
- return 0;
707
- }
708
-
709
- for( r = 0; r <= a->round; ++r ) {
710
-
711
- if( a->numActions[ r ] != b->numActions[ r ] ) {
712
- return 0;
713
- }
714
-
715
- for( i = 0; i < a->numActions[ r ]; ++i ) {
716
-
717
- if( a->action[ r ][ i ].type != b->action[ r ][ i ].type ) {
718
- return 0;
719
- }
720
- if( a->action[ r ][ i ].size != b->action[ r ][ i ].size ) {
721
- return 0;
722
- }
723
- }
724
- }
725
-
726
- /* spent, maxSpent, actingPlayer, finished, and playerFolded are
727
- all determined by the betting taken, so if it's equal, so are
728
- they (at least for valid states) */
729
-
730
- /* are the board cards the same? */
731
- t = sumBoardCards( game, a->round );
732
- for( i = 0; i < t; ++i ) {
733
-
734
- if( a->boardCards[ i ] != b->boardCards[ i ] ) {
735
- return 0;
736
- }
737
- }
738
-
739
- /* all tests say states are equal */
740
- return 1;
741
- }
742
-
743
- int statesEqual( const Game *game, const State *a, const State *b )
744
- {
745
- int p, i;
746
-
747
- if( !statesEqualCommon( game, a, b ) ) {
748
- return 0;
749
- }
750
-
751
- /* are all the hole cards the same? */
752
- for( p = 0; p < game->numPlayers; ++p ) {
753
-
754
- for( i = 0; i < game->numHoleCards; ++i ) {
755
- if( a->holeCards[ p ][ i ] != b->holeCards[ p ][ i ] ) {
756
- return 0;
757
- }
758
- }
759
- }
760
-
761
- return 1;
762
- }
763
-
764
- int matchStatesEqual( const Game *game, const MatchState *a,
765
- const MatchState *b )
766
- {
767
- int p, i;
768
-
769
- if( a->viewingPlayer != b->viewingPlayer ) {
770
- return 0;
771
- }
772
-
773
- if( !statesEqualCommon( game, &a->state, &b->state ) ) {
774
- return 0;
775
- }
776
-
777
- /* are the viewing player's hole cards the same? */
778
- p = a->viewingPlayer;
779
- for( i = 0; i < game->numHoleCards; ++i ) {
780
- if( a->state.holeCards[ p ][ i ] != b->state.holeCards[ p ][ i ] ) {
781
- return 0;
782
- }
783
- }
784
-
785
- return 1;
786
- }
787
-
788
- int raiseIsValid( const Game *game, const State *curState,
789
- int32_t *minSize, int32_t *maxSize )
790
- {
791
- int p;
792
-
793
- if( numRaises( curState ) >= game->maxRaises[ curState->round ] ) {
794
- /* already made maximum number of raises */
795
-
796
- return 0;
797
- }
798
-
799
- if( curState->numActions[ curState->round ] + game->numPlayers
800
- > MAX_NUM_ACTIONS ) {
801
- /* 1 raise + NUM PLAYERS-1 calls is too many actions */
802
-
803
- fprintf( stderr, "WARNING: #actions in round is too close to MAX_NUM_ACTIONS, forcing call/fold\n" );
804
- return 0;
805
- }
806
-
807
- if( numActingPlayers( game, curState ) <= 1 ) {
808
- /* last remaining player can't bet if there's no one left to call
809
- (this check is needed if the 2nd last player goes all in, and
810
- the last player has enough stack left to bet) */
811
-
812
- return 0;
813
- }
814
-
815
- if( game->bettingType != noLimitBetting ) {
816
- /* if it's not no-limit betting, don't worry about sizes */
817
-
818
- *minSize = 0;
819
- *maxSize = 0;
820
- return 1;
821
- }
822
-
823
- p = currentPlayer( game, curState );
824
- *minSize = curState->minNoLimitRaiseTo;
825
- *maxSize = game->stack[ p ];
826
-
827
- /* handle case where remaining player stack is too small */
828
- if( *minSize > game->stack[ p ] ) {
829
- /* can't handle the minimum bet size - can we bet at all? */
830
-
831
- if( curState->maxSpent >= game->stack[ p ] ) {
832
- /* not enough money to increase current bet */
833
-
834
- return 0;
835
- } else {
836
- /* can raise by going all-in */
837
-
838
- *minSize = *maxSize;
839
- return 1;
840
- }
841
- }
842
-
843
- return 1;
844
- }
845
-
846
- int isValidAction( const Game *game, const State *curState,
847
- const int tryFixing, Action *action )
848
- {
849
- int min, max, p;
850
-
851
- if( stateFinished( curState ) || action->type == a_invalid ) {
852
- return 0;
853
- }
854
-
855
- p = currentPlayer( game, curState );
856
-
857
- if( action->type == a_raise ) {
858
-
859
- if( !raiseIsValid( game, curState, &min, &max ) ) {
860
- /* there are no valid raise sizes */
861
-
862
- return 0;
863
- }
864
-
865
- if( game->bettingType == noLimitBetting ) {
866
- /* no limit games have a size */
867
-
868
- if( action->size < min ) {
869
- /* bet size is too small */
870
-
871
- if( !tryFixing ) {
872
-
873
- return 0;
874
- }
875
- fprintf( stderr, "WARNING: raise of %d increased to %d\n",
876
- action->size, min );
877
- action->size = min;
878
- } else if( action->size > max ) {
879
- /* bet size is too big */
880
-
881
- if( !tryFixing ) {
882
-
883
- return 0;
884
- }
885
- fprintf( stderr, "WARNING: raise of %d decreased to %d\n",
886
- action->size, max );
887
- action->size = max;
888
- }
889
- } else {
890
-
891
- }
892
- } else if( action->type == a_fold ) {
893
-
894
- if( curState->spent[ p ] == curState->maxSpent
895
- || curState->spent[ p ] == game->stack[ p ] ) {
896
- /* player has already called all bets, or is all-in */
897
-
898
- return 0;
899
- }
900
-
901
- if( action->size != 0 ) {
902
-
903
- fprintf( stderr, "WARNING: size given for fold\n" );
904
- action->size = 0;
905
- }
906
- } else {
907
- /* everything else */
908
-
909
- if( action->size != 0 ) {
910
-
911
- fprintf( stderr, "WARNING: size given for something other than a no-limit raise\n" );
912
- action->size = 0;
913
- }
914
- }
915
-
916
- return 1;
917
- }
918
-
919
- void doAction( const Game *game, const Action *action, State *state )
920
- {
921
- int p = currentPlayer( game, state );
922
-
923
- assert( state->numActions[ state->round ] < MAX_NUM_ACTIONS );
924
-
925
- state->action[ state->round ][ state->numActions[ state->round ] ] = *action;
926
- state->actingPlayer[ state->round ][ state->numActions[ state->round ] ] = p;
927
- ++state->numActions[ state->round ];
928
-
929
- switch( action->type ) {
930
- case a_fold:
931
-
932
- state->playerFolded[ p ] = 1;
933
- break;
934
-
935
- case a_call:
936
-
937
- if( state->maxSpent > game->stack[ p ] ) {
938
- /* calling puts player all-in */
939
-
940
- state->spent[ p ] = game->stack[ p ];
941
- } else {
942
- /* player matches the bet by spending same amount of money */
943
-
944
- state->spent[ p ] = state->maxSpent;
945
- }
946
- break;
947
-
948
- case a_raise:
949
-
950
- if( game->bettingType == noLimitBetting ) {
951
- /* no-limit betting uses size in action */
952
-
953
- assert( action->size > state->maxSpent );
954
- assert( action->size <= game->stack[ p ] );
955
-
956
- /* next raise must call this bet, and raise by at least this much */
957
- if( action->size + action->size - state->maxSpent
958
- > state->minNoLimitRaiseTo ) {
959
-
960
- state->minNoLimitRaiseTo
961
- = action->size + action->size - state->maxSpent;
962
- }
963
- state->maxSpent = action->size;
964
- } else {
965
- /* limit betting uses a fixed amount on top of current bet size */
966
-
967
- if( state->maxSpent + game->raiseSize[ state->round ]
968
- > game->stack[ p ] ) {
969
- /* raise puts player all-in */
970
-
971
- state->maxSpent = game->stack[ p ];
972
- } else {
973
- /* player raises by the normal limit size */
974
-
975
- state->maxSpent += game->raiseSize[ state->round ];
976
- }
977
- }
978
-
979
- state->spent[ p ] = state->maxSpent;
980
- break;
981
-
982
- default:
983
- fprintf( stderr, "ERROR: trying to do invalid action %d", action->type );
984
- assert( 0 );
985
- }
986
-
987
- /* see if the round or game has ended */
988
- if( numFolded( game, state ) + 1 >= game->numPlayers ) {
989
- /* only one player left - game is immediately over, no showdown */
990
-
991
- state->finished = 1;
992
- } else if( numCalled( game, state ) >= numActingPlayers( game, state ) ) {
993
- /* >= 2 non-folded players, all acting players have called */
994
-
995
- if( numActingPlayers( game, state ) > 1 ) {
996
- /* there are at least 2 acting players */
997
-
998
- if( state->round + 1 < game->numRounds ) {
999
- /* active players move onto next round */
1000
-
1001
- ++state->round;
1002
-
1003
- /* minimum raise-by is reset to minimum of big blind or 1 chip */
1004
- state->minNoLimitRaiseTo = 1;
1005
- for( p = 0; p < game->numPlayers; ++p ) {
1006
-
1007
- if( game->blind[ p ] > state->minNoLimitRaiseTo ) {
1008
-
1009
- state->minNoLimitRaiseTo = game->blind[ p ];
1010
- }
1011
- }
1012
-
1013
- /* we finished at least one round, so raise-to = raise-by + maxSpent */
1014
- state->minNoLimitRaiseTo += state->maxSpent;
1015
- } else {
1016
- /* no more betting rounds, so we're totally finished */
1017
-
1018
- state->finished = 1;
1019
- }
1020
- } else {
1021
- /* not enough players for more betting, but still need a showdown */
1022
-
1023
- state->finished = 1;
1024
- state->round = game->numRounds - 1;
1025
- }
1026
- }
1027
- }
1028
-
1029
- static int rankHand( const Game *game, const State *state,
1030
- const uint8_t player )
1031
- {
1032
- int i;
1033
- Cardset c = emptyCardset();
1034
-
1035
- for( i = 0; i < game->numHoleCards; ++i ) {
1036
-
1037
- addCardToCardset( &c, suitOfCard( state->holeCards[ player ][ i ] ),
1038
- rankOfCard( state->holeCards[ player ][ i ] ) );
1039
- }
1040
-
1041
- for( i = 0; i < sumBoardCards( game, state->round ); ++i ) {
1042
-
1043
- addCardToCardset( &c, suitOfCard( state->boardCards[ i ] ),
1044
- rankOfCard( state->boardCards[ i ] ) );
1045
- }
1046
-
1047
- return rankCardset( c );
1048
- }
1049
-
1050
- double valueOfState( const Game *game, const State *state,
1051
- const uint8_t player )
1052
- {
1053
- double value;
1054
- int p, numPlayers, playerIdx, numWinners, newNumPlayers;
1055
- int32_t size, spent[ MAX_PLAYERS ];
1056
- int rank[ MAX_PLAYERS ], winRank;
1057
-
1058
- if( state->playerFolded[ player ] ) {
1059
- /* folding player loses all spent money */
1060
-
1061
- return (double)-state->spent[ player ];
1062
- }
1063
-
1064
- if( numFolded( game, state ) + 1 == game->numPlayers ) {
1065
- /* everyone else folded, so player takes the pot */
1066
-
1067
- value = 0.0;
1068
- for( p = 0; p < game->numPlayers; ++p ) {
1069
- if( p == player ) { continue; }
1070
-
1071
- value += (double)state->spent[ p ];
1072
- }
1073
-
1074
- return value;
1075
- }
1076
-
1077
- /* there's a showdown, and player is particpating. Exciting! */
1078
-
1079
- /* make up a list of players */
1080
- numPlayers = 0;
1081
- playerIdx = -1; /* useless, but gets rid of a warning */
1082
- for( p = 0; p < game->numPlayers; ++p ) {
1083
-
1084
- if( state->spent[ p ] == 0 ) {
1085
- continue;
1086
- }
1087
-
1088
- if( state->playerFolded[ p ] ) {
1089
- /* folding players have a negative rank so they lose to a real hand
1090
- we have also tested for fold, so p can't be the player of interest */
1091
-
1092
- rank[ numPlayers ] = -1;
1093
- } else {
1094
- /* p is participating in a showdown */
1095
-
1096
- if( p == player ) {
1097
- playerIdx = numPlayers;
1098
- }
1099
- rank[ numPlayers ] = rankHand( game, state, p );
1100
- }
1101
-
1102
- spent[ numPlayers ] = state->spent[ p ];
1103
- ++numPlayers;
1104
- }
1105
- assert( numPlayers > 1 );
1106
-
1107
- /* go through all the sidepots player is participating in */
1108
- value = 0.0;
1109
- while( 1 ) {
1110
-
1111
- /* find the smallest remaining sidepot, largest rank,
1112
- and number of winners with largest rank */
1113
- size = INT32_MAX;
1114
- winRank = 0;
1115
- numWinners = 0;
1116
- for( p = 0; p < numPlayers; ++p ) {
1117
- assert( spent[ p ] > 0 );
1118
-
1119
- if( spent[ p ] < size ) {
1120
- size = spent[ p ];
1121
- }
1122
-
1123
- if( rank[ p ] > winRank ) {
1124
- /* new largest rank - only one player with this rank so far */
1125
-
1126
- winRank = rank[ p ];
1127
- numWinners = 1;
1128
- } else if( rank[ p ] == winRank ) {
1129
- /* another player with highest rank */
1130
-
1131
- ++numWinners;
1132
- }
1133
- }
1134
-
1135
- if( rank[ playerIdx ] == winRank ) {
1136
- /* player has spent size, and splits pot with other winners */
1137
-
1138
- value += (double)( size * ( numPlayers - numWinners ) )
1139
- / (double)numWinners;
1140
- } else {
1141
- /* player loses this pot */
1142
-
1143
- value -= (double)size;
1144
- }
1145
-
1146
- /* update list of players for next pot */
1147
- newNumPlayers = 0;
1148
- for( p = 0; p < numPlayers; ++p ) {
1149
-
1150
- spent[ p ] -= size;
1151
- if( spent[ p ] == 0 ) {
1152
- /* player p is not participating in next side pot */
1153
-
1154
- if( p == playerIdx ) {
1155
- /* p is the player of interest, so we're done */
1156
-
1157
- return value;
1158
- }
1159
-
1160
- continue;
1161
- }
1162
-
1163
- if( p == playerIdx ) {
1164
- /* p is the player of interest, so note the new index */
1165
-
1166
- playerIdx = newNumPlayers;
1167
- }
1168
-
1169
- if( p != newNumPlayers ) {
1170
- /* put entry p into new position */
1171
-
1172
- spent[ newNumPlayers ] = spent[ p ];
1173
- rank[ newNumPlayers ] = rank[ p ];
1174
- }
1175
- ++newNumPlayers;
1176
- }
1177
- numPlayers = newNumPlayers;
1178
- }
1179
- }
1180
-
1181
- /* read actions from a string, updating state with the actions
1182
- reading is terminated by '\0' and ':'
1183
- returns number of characters consumed, or -1 on failure
1184
- state will be modified, even on failure */
1185
- static int readBetting( const char *string, const Game *game, State *state )
1186
- {
1187
- int c, r;
1188
- Action action;
1189
-
1190
- c = 0;
1191
- while( 1 ) {
1192
-
1193
- if( string[ c ] == 0 ) {
1194
- break;
1195
- }
1196
-
1197
- if( string[ c ] == ':' ) {
1198
- ++c;
1199
- break;
1200
- }
1201
-
1202
- /* ignore / character */
1203
- if( string[ c ] == '/' ) {
1204
- ++c;
1205
- continue;
1206
- }
1207
-
1208
- r = readAction( &string[ c ], game, &action );
1209
- if( r < 0 ) {
1210
- return -1;
1211
- }
1212
-
1213
- if( !isValidAction( game, state, 0, &action ) ) {
1214
- return -1;
1215
- }
1216
-
1217
- doAction( game, &action, state );
1218
- c += r;
1219
- }
1220
-
1221
- return c;
1222
- }
1223
-
1224
- /* print actions to a string
1225
- returns number of characters printed to string, or -1 on failure
1226
- DOES NOT COUNT FINAL 0 TERMINATOR IN THIS COUNT!!! */
1227
- static int printBetting( const Game *game, const State *state,
1228
- const int maxLen, char *string )
1229
- {
1230
- int i, a, c, r;
1231
-
1232
- c = 0;
1233
- for( i = 0; i <= state->round; ++i ) {
1234
-
1235
- /* print state separator */
1236
- if( i != 0 ) {
1237
-
1238
- if( c >= maxLen ) {
1239
- return -1;
1240
- }
1241
- string[ c ] = '/';
1242
- ++c;
1243
- }
1244
-
1245
- /* print betting for round */
1246
- for( a = 0; a < state->numActions[ i ]; ++a ) {
1247
-
1248
- r = printAction( game, &state->action[ i ][ a ],
1249
- maxLen - c, &string[ c ] );
1250
- if( r < 0 ) {
1251
- return -1;
1252
- }
1253
- c += r;
1254
- }
1255
- }
1256
-
1257
- if( c >= maxLen ) {
1258
- return -1;
1259
- }
1260
- string[ c ] = 0;
1261
-
1262
- return c;
1263
- }
1264
-
1265
- static int readHoleCards( const char *string, const Game *game,
1266
- State *state )
1267
- {
1268
- int p, c, r, num;
1269
-
1270
- c = 0;
1271
- for( p = 0; p < game->numPlayers; ++p ) {
1272
-
1273
- /* check for player separator '|' */
1274
- if( p != 0 ) {
1275
- if( string[ c ] == '|' ) {
1276
- ++c;
1277
- }
1278
- }
1279
-
1280
- num = readCards( &string[ c ], game->numHoleCards,
1281
- state->holeCards[ p ], &r );
1282
- if( num == 0 ) {
1283
- /* no cards for player p */
1284
-
1285
- continue;
1286
- }
1287
- if( num != game->numHoleCards ) {
1288
- /* read some cards, but not enough - bad! */
1289
-
1290
- return -1;
1291
- }
1292
- c += r;
1293
- }
1294
-
1295
- return c;
1296
- }
1297
-
1298
- static int printAllHoleCards( const Game *game, const State *state,
1299
- const int maxLen, char *string )
1300
- {
1301
- int p, c, r;
1302
-
1303
- c = 0;
1304
- for( p = 0; p < game->numPlayers; ++p ) {
1305
-
1306
- /* print player separator '|' */
1307
- if( p != 0 ) {
1308
-
1309
- if( c >= maxLen ) {
1310
- return -1;
1311
- }
1312
- string[ c ] = '|';
1313
- ++c;
1314
- }
1315
-
1316
- r = printCards( game->numHoleCards, state->holeCards[ p ],
1317
- maxLen - c, &string[ c ] );
1318
- if( r < 0 ) {
1319
- return -1;
1320
- }
1321
- c += r;
1322
- }
1323
-
1324
- if( c >= maxLen ) {
1325
- return -1;
1326
- }
1327
- string[ c ] = 0;
1328
-
1329
- return c;
1330
- }
1331
-
1332
- static int printPlayerHoleCards( const Game *game, const State *state,
1333
- const uint8_t player,
1334
- const int maxLen, char *string )
1335
- {
1336
- int p, c, r;
1337
-
1338
- c = 0;
1339
- for( p = 0; p < game->numPlayers; ++p ) {
1340
-
1341
- /* print player separator '|' */
1342
- if( p != 0 ) {
1343
-
1344
- if( c >= maxLen ) {
1345
- return -1;
1346
- }
1347
- string[ c ] = '|';
1348
- ++c;
1349
- }
1350
-
1351
- if( p != player ) {
1352
- /* don't print other player's cards unless there was a showdown
1353
- and they didn't fold */
1354
-
1355
- if( !stateFinished( state ) ) {
1356
- continue;
1357
- }
1358
-
1359
- if( state->playerFolded[ p ] ) {
1360
- continue;
1361
- }
1362
-
1363
- if( numFolded( game, state ) + 1 == game->numPlayers ) {
1364
- continue;
1365
- }
1366
- }
1367
-
1368
- r = printCards( game->numHoleCards, state->holeCards[ p ],
1369
- maxLen - c, &string[ c ] );
1370
- if( r < 0 ) {
1371
- return -1;
1372
- }
1373
- c += r;
1374
- }
1375
-
1376
- if( c >= maxLen ) {
1377
- return -1;
1378
- }
1379
- string[ c ] = 0;
1380
-
1381
- return c;
1382
- }
1383
-
1384
- static int readBoardCards( const char *string, const Game *game,
1385
- State *state )
1386
- {
1387
- int i, c, r;
1388
-
1389
- c = 0;
1390
- for( i = 0; i <= state->round; ++i ) {
1391
-
1392
- /* check for round separator '/' */
1393
- if( i != 0 ) {
1394
- if( string[ c ] == '/' ) {
1395
- ++c;
1396
- }
1397
- }
1398
-
1399
- if( readCards( &string[ c ], game->numBoardCards[ i ],
1400
- &state->boardCards[ bcStart( game, i ) ], &r )
1401
- != game->numBoardCards[ i ] ) {
1402
- /* couldn't read the required number of cards - bad! */
1403
-
1404
- return -1;
1405
- }
1406
- c += r;
1407
- }
1408
-
1409
- return c;
1410
- }
1411
-
1412
- static int printBoardCards( const Game *game, const State *state,
1413
- const int maxLen, char *string )
1414
- {
1415
- int i, c, r;
1416
-
1417
- c = 0;
1418
- for( i = 0; i <= state->round; ++i ) {
1419
-
1420
- /* print round separator '/' */
1421
- if( i != 0 ) {
1422
-
1423
- if( c >= maxLen ) {
1424
- return -1;
1425
- }
1426
- string[ c ] = '/';
1427
- ++c;
1428
- }
1429
-
1430
- r = printCards( game->numBoardCards[ i ],
1431
- &state->boardCards[ bcStart( game, i ) ],
1432
- maxLen - c, &string[ c ] );
1433
- if( r < 0 ) {
1434
- return -1;
1435
- }
1436
- c += r;
1437
- }
1438
-
1439
- if( c >= maxLen ) {
1440
- return -1;
1441
- }
1442
- string[ c ] = 0;
1443
-
1444
- return c;
1445
- }
1446
-
1447
- static int readStateCommon( const char *string, const Game *game,
1448
- State *state )
1449
- {
1450
- uint32_t handId;
1451
- int c, r;
1452
-
1453
- /* HEADER */
1454
- c = 0;
1455
-
1456
- /* HEADER:handId */
1457
- if( sscanf( string, ":%"SCNu32"%n", &handId, &r ) < 1 ) {
1458
- return -1;
1459
- }
1460
- c += r;
1461
-
1462
- initState( game, handId, state );
1463
-
1464
- /* HEADER:handId: */
1465
- if( string[ c ] != ':' ) {
1466
- return -1;
1467
- }
1468
- ++c;
1469
-
1470
- /* HEADER:handId:betting: */
1471
- r = readBetting( &string[ c ], game, state );
1472
- if( r < 0 ) {
1473
- return -1;
1474
- }
1475
- c += r;
1476
-
1477
- /* HEADER:handId:betting:holeCards */
1478
- r = readHoleCards( &string[ c ], game, state );
1479
- if( r < 0 ) {
1480
- return -1;
1481
- }
1482
- c += r;
1483
-
1484
- /* HEADER:handId:betting:holeCards boardCards */
1485
- r = readBoardCards( &string[ c ], game, state );
1486
- if( r < 0 ) {
1487
- return -1;
1488
- }
1489
- c += r;
1490
-
1491
- return c;
1492
- }
1493
-
1494
- int readState( const char *string, const Game *game, State *state )
1495
- {
1496
- int c, r;
1497
-
1498
- /* HEADER = STATE */
1499
- if( strncmp( string, "STATE", 5 ) != 0 ) {
1500
- return -1;
1501
- }
1502
- c = 5;
1503
-
1504
- /* read rest of state */
1505
- r = readStateCommon( &string[ 5 ], game, state );
1506
- if( r < 0 ) {
1507
- return -1;
1508
- }
1509
- c += r;
1510
-
1511
- return c;
1512
- }
1513
-
1514
- int readMatchState( const char *string, const Game *game,
1515
- MatchState *state )
1516
- {
1517
- int c, r;
1518
-
1519
- /* HEADER = MATCHSTATE:player */
1520
- if( sscanf( string, "MATCHSTATE:%"SCNu8"%n",
1521
- &state->viewingPlayer, &c ) < 1
1522
- || state->viewingPlayer >= game->numPlayers ) {
1523
- return -1;
1524
- }
1525
-
1526
- /* read rest of state */
1527
- r = readStateCommon( &string[ c ], game, &state->state );
1528
- if( r < 0 ) {
1529
- return -1;
1530
- }
1531
- c += r;
1532
-
1533
- return c;
1534
- }
1535
-
1536
- static int printStateCommon( const Game *game, const State *state,
1537
- const int maxLen, char *string )
1538
- {
1539
- int c, r;
1540
-
1541
- /* HEADER */
1542
- c = 0;
1543
-
1544
- /* HEADER:handId: */
1545
- r = snprintf( &string[ c ], maxLen - c, ":%"PRIu32":", state->handId );
1546
- if( r < 0 ) {
1547
- return -1;
1548
- }
1549
- c += r;
1550
-
1551
- /* HEADER:handId:betting */
1552
- r = printBetting( game, state, maxLen - c, &string[ c ] );
1553
- if( r < 0 ) {
1554
- return -1;
1555
- }
1556
- c += r;
1557
-
1558
- /* HEADER:handId:betting: */
1559
- if( c >= maxLen ) {
1560
- return -1;
1561
- }
1562
- string[ c ] = ':';
1563
- ++c;
1564
-
1565
- return c;
1566
- }
1567
-
1568
- int printState( const Game *game, const State *state,
1569
- const int maxLen, char *string )
1570
- {
1571
- int c, r;
1572
-
1573
- c = 0;
1574
-
1575
- /* STATE */
1576
- r = snprintf( &string[ c ], maxLen - c, "STATE" );
1577
- if( r < 0 ) {
1578
- return -1;
1579
- }
1580
- c += r;
1581
-
1582
- /* STATE:handId:betting: */
1583
- r = printStateCommon( game, state, maxLen - c, &string[ c ] );
1584
- if( r < 0 ) {
1585
- return -1;
1586
- }
1587
- c += r;
1588
-
1589
- /* STATE:handId:betting:holeCards */
1590
- r = printAllHoleCards( game, state, maxLen - c, &string[ c ] );
1591
- if( r < 0 ) {
1592
- return -1;
1593
- }
1594
- c += r;
1595
-
1596
- /* STATE:handId:betting:holeCards boardCards */
1597
- r = printBoardCards( game, state, maxLen - c, &string[ c ] );
1598
- if( r < 0 ) {
1599
- return -1;
1600
- }
1601
- c += r;
1602
-
1603
- if( c >= maxLen ) {
1604
- return -1;
1605
- }
1606
- string[ c ] = 0;
1607
-
1608
- return c;
1609
- }
1610
-
1611
- int printMatchState( const Game *game, const MatchState *state,
1612
- const int maxLen, char *string )
1613
- {
1614
- int c, r;
1615
-
1616
- c = 0;
1617
-
1618
- /* MATCHSTATE:player */
1619
- r = snprintf( &string[ c ], maxLen - c, "MATCHSTATE:%"PRIu8,
1620
- state->viewingPlayer );
1621
- if( r < 0 ) {
1622
- return -1;
1623
- }
1624
- c += r;
1625
-
1626
- /* MATCHSTATE:player:handId:betting: */
1627
- r = printStateCommon( game, &state->state, maxLen - c, &string[ c ] );
1628
- if( r < 0 ) {
1629
- return -1;
1630
- }
1631
- c += r;
1632
-
1633
- /* MATCHSTATE:player:handId:betting:holeCards */
1634
- r = printPlayerHoleCards( game, &state->state, state->viewingPlayer,
1635
- maxLen - c, &string[ c ] );
1636
- if( r < 0 ) {
1637
- return -1;
1638
- }
1639
- c += r;
1640
-
1641
- /* MATCHSTATE:player:handId:betting:holeCards boardCards */
1642
- r = printBoardCards( game, &state->state, maxLen - c, &string[ c ] );
1643
- if( r < 0 ) {
1644
- return -1;
1645
- }
1646
- c += r;
1647
-
1648
- if( c >= maxLen ) {
1649
- return -1;
1650
- }
1651
- string[ c ] = 0;
1652
-
1653
- return c;
1654
- }
1655
-
1656
- int readAction( const char *string, const Game *game, Action *action )
1657
- {
1658
- int c, r;
1659
-
1660
- action->type = charToAction[ (uint8_t)string[ 0 ] ];
1661
- if( action->type < 0 ) {
1662
- return -1;
1663
- }
1664
- c = 1;
1665
-
1666
- if( action->type == a_raise && game->bettingType == noLimitBetting ) {
1667
- /* no-limit bet/raise needs to read a size */
1668
-
1669
- if( sscanf( &string[ c ], "%"SCNd32"%n", &action->size, &r ) < 1 ) {
1670
- return -1;
1671
- }
1672
- c += r;
1673
- } else {
1674
- /* size is zero for anything but a no-limit raise */
1675
-
1676
- action->size = 0;
1677
- }
1678
-
1679
- return c;
1680
- }
1681
-
1682
- int printAction( const Game *game, const Action *action,
1683
- const int maxLen, char *string )
1684
- {
1685
- int c, r;
1686
-
1687
- if( maxLen == 0 ) {
1688
- return -1;
1689
- }
1690
-
1691
- c = 0;
1692
-
1693
- string[ c ] = actionChars[ action->type ];
1694
- ++c;
1695
-
1696
- if( game->bettingType == noLimitBetting && action->type == a_raise ) {
1697
- /* 2010 AAAI no-limit format has a size for bet/raise */
1698
-
1699
- r = snprintf( &string[ c ], maxLen - c, "%"PRId32, action->size );
1700
- if( r < 0 ) {
1701
- return -1;
1702
- }
1703
- c += r;
1704
- }
1705
-
1706
- if( c >= maxLen ) {
1707
- return -1;
1708
- }
1709
- string[ c ] = 0;
1710
-
1711
- return c;
1712
- }
1713
-
1714
- int readCard( const char *string, uint8_t *card )
1715
- {
1716
- char *spos;
1717
- uint8_t c;
1718
-
1719
- if( string[ 0 ] == 0 ) {
1720
- return -1;
1721
- }
1722
- spos = strchr( rankChars, toupper( string[ 0 ] ) );
1723
- if( spos == 0 ) {
1724
- return -1;
1725
- }
1726
- c = spos - rankChars;
1727
-
1728
- if( string[ 1 ] == 0 ) {
1729
- return -1;
1730
- }
1731
- spos = strchr( suitChars, tolower( string[ 1 ] ) );
1732
- if( spos == 0 ) {
1733
- return -1;
1734
- }
1735
-
1736
- *card = makeCard( c, spos - suitChars );
1737
-
1738
- return 2;
1739
- }
1740
-
1741
- int readCards( const char *string, const int maxCards,
1742
- uint8_t *cards, int *charsConsumed )
1743
- {
1744
- int i, c, r;
1745
-
1746
- c = 0;
1747
- for( i = 0; i < maxCards; ++i ) {
1748
-
1749
- r = readCard( &string[ c ], &cards[ i ] );
1750
- if( r < 0 ) {
1751
- break;
1752
- }
1753
- c += r;
1754
- }
1755
-
1756
- *charsConsumed = c;
1757
- return i;
1758
- }
1759
-
1760
- int printCard( const uint8_t card, const int maxLen, char *string )
1761
- {
1762
- if( 3 > maxLen ) {
1763
- return -1;
1764
- }
1765
-
1766
- string[ 0 ] = rankChars[ rankOfCard( card ) ];
1767
- string[ 1 ] = suitChars[ suitOfCard( card ) ];
1768
- string[ 2 ] = 0;
1769
-
1770
- return 2;
1771
- }
1772
-
1773
- int printCards( const int numCards, const uint8_t *cards,
1774
- const int maxLen, char *string )
1775
- {
1776
- int i, c, r;
1777
-
1778
- c = 0;
1779
-
1780
- for( i = 0; i < numCards; ++i ) {
1781
-
1782
- r = printCard( cards[ i ], maxLen - c, &string[ c ] );
1783
- if( r < 0 ) {
1784
- return -1;
1785
- }
1786
- c += r;
1787
- }
1788
-
1789
- /* no need to null terminate, we know for sure that printCard does */
1790
-
1791
- return c;
1792
- }