acpc_dealer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/README.md +33 -0
  2. data/Rakefile +20 -0
  3. data/acpc_dealer.gemspec +29 -0
  4. data/bin/acpc_dealer +168 -0
  5. data/ext/hand_evaluator/extconf.rb +5 -0
  6. data/ext/hand_evaluator/hand_evaluator.c +38 -0
  7. data/lib/acpc_dealer/2p.limit.h5.r0.logs/2p.limit.h5.r0.actions.log +5 -0
  8. data/lib/acpc_dealer/2p.limit.h5.r0.logs/2p.limit.h5.r0.log +0 -0
  9. data/lib/acpc_dealer/dealer_runner.rb +72 -0
  10. data/lib/acpc_dealer/version.rb +3 -0
  11. data/lib/acpc_dealer.rb +33 -0
  12. data/lib/hand_evaluator.so +0 -0
  13. data/spec/coverage/assets/0.5.3/app.js +88 -0
  14. data/spec/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  15. data/spec/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  16. data/spec/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  17. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  18. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  19. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  20. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  21. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  22. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  23. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  24. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  25. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  26. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  27. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  28. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  29. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  30. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  31. data/spec/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  32. data/spec/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  33. data/spec/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  34. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
  35. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  36. data/spec/coverage/assets/0.5.3/favicon_green.png +0 -0
  37. data/spec/coverage/assets/0.5.3/favicon_red.png +0 -0
  38. data/spec/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  39. data/spec/coverage/assets/0.5.3/highlight.css +129 -0
  40. data/spec/coverage/assets/0.5.3/highlight.pack.js +1 -0
  41. data/spec/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
  42. data/spec/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
  43. data/spec/coverage/assets/0.5.3/jquery.timeago.js +141 -0
  44. data/spec/coverage/assets/0.5.3/jquery.url.js +174 -0
  45. data/spec/coverage/assets/0.5.3/loading.gif +0 -0
  46. data/spec/coverage/assets/0.5.3/magnify.png +0 -0
  47. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  48. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  49. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  50. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  51. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  52. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  53. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  54. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  55. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  56. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  57. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  58. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  59. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  60. data/spec/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  61. data/spec/coverage/assets/0.5.3/stylesheet.css +383 -0
  62. data/spec/coverage/index.html +81 -0
  63. data/spec/dealer_runner_spec.rb +83 -0
  64. data/spec/support/spec_helper.rb +33 -0
  65. data/vendor/project_acpc_server/LICENCE +23 -0
  66. data/vendor/project_acpc_server/Makefile +22 -0
  67. data/vendor/project_acpc_server/README +113 -0
  68. data/vendor/project_acpc_server/README.submission +42 -0
  69. data/vendor/project_acpc_server/acpc_play_match.pl +101 -0
  70. data/vendor/project_acpc_server/bm_server +0 -0
  71. data/vendor/project_acpc_server/bm_server.c +1557 -0
  72. data/vendor/project_acpc_server/bm_server.config +78 -0
  73. data/vendor/project_acpc_server/bm_widget +0 -0
  74. data/vendor/project_acpc_server/bm_widget.c +186 -0
  75. data/vendor/project_acpc_server/dealer +0 -0
  76. data/vendor/project_acpc_server/dealer.c +1278 -0
  77. data/vendor/project_acpc_server/evalHandTables +4269 -0
  78. data/vendor/project_acpc_server/example_player +0 -0
  79. data/vendor/project_acpc_server/example_player.c +204 -0
  80. data/vendor/project_acpc_server/example_player.limit.2p.sh +2 -0
  81. data/vendor/project_acpc_server/example_player.limit.3p.sh +2 -0
  82. data/vendor/project_acpc_server/example_player.nolimit.2p.sh +2 -0
  83. data/vendor/project_acpc_server/example_player.nolimit.3p.sh +2 -0
  84. data/vendor/project_acpc_server/game.c +1793 -0
  85. data/vendor/project_acpc_server/game.h +253 -0
  86. data/vendor/project_acpc_server/holdem.limit.2p.reverse_blinds.game +13 -0
  87. data/vendor/project_acpc_server/holdem.limit.3p.game +13 -0
  88. data/vendor/project_acpc_server/holdem.nolimit.2p.reverse_blinds.game +12 -0
  89. data/vendor/project_acpc_server/holdem.nolimit.3p.game +12 -0
  90. data/vendor/project_acpc_server/net.c +218 -0
  91. data/vendor/project_acpc_server/net.h +61 -0
  92. data/vendor/project_acpc_server/play_match.pl +99 -0
  93. data/vendor/project_acpc_server/protocol.odt +0 -0
  94. data/vendor/project_acpc_server/protocol.pdf +0 -0
  95. data/vendor/project_acpc_server/rng.c +138 -0
  96. data/vendor/project_acpc_server/rng.h +63 -0
  97. data/vendor/project_acpc_server/test.log +11 -0
  98. data/vendor/project_acpc_server/validate_submission.pl +546 -0
  99. metadata +315 -0
@@ -0,0 +1,1278 @@
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
+ #define __STDC_LIMIT_MACROS
8
+ #include <stdint.h>
9
+ #include <unistd.h>
10
+ #include <sys/socket.h>
11
+ #include <sys/time.h>
12
+ #include <sys/select.h>
13
+ #include <netinet/in.h>
14
+ #include <netinet/tcp.h>
15
+ #include <getopt.h>
16
+ #include "game.h"
17
+ #include "net.h"
18
+
19
+
20
+ /* the ports for players to connect to will be printed on standard out
21
+ (in player order)
22
+
23
+ if log file is enabled, matchName.log will contain finished states
24
+ and values, followed by the final total values for each player
25
+
26
+ if transaction file is enabled, matchName.tlog will contain a list
27
+ of actions taken and timestamps that is sufficient to recreate an
28
+ interrupted match
29
+
30
+ if the quiet option is not enabled, standard error will print out
31
+ the messages sent to and receieved from the players
32
+
33
+ the final total values for each player will be printed to both
34
+ standard out and standard error
35
+
36
+ exit value is EXIT_SUCCESS if the match was a success,
37
+ or EXIT_FAILURE on any failure */
38
+
39
+
40
+ #define DEFAULT_MAX_INVALID_ACTIONS UINT32_MAX
41
+ #define DEFAULT_MAX_RESPONSE_MICROS 600000000
42
+ #define DEFAULT_MAX_USED_HAND_MICROS 600000000
43
+ #define DEFAULT_MAX_USED_PER_HAND_MICROS 7000000
44
+
45
+
46
+ typedef struct {
47
+ uint32_t maxInvalidActions;
48
+ uint64_t maxResponseMicros;
49
+ uint64_t maxUsedHandMicros;
50
+ uint64_t maxUsedMatchMicros;
51
+
52
+ uint32_t numInvalidActions[ MAX_PLAYERS ];
53
+ uint64_t usedHandMicros[ MAX_PLAYERS ];
54
+ uint64_t usedMatchMicros[ MAX_PLAYERS ];
55
+ } ErrorInfo;
56
+
57
+
58
+ static void printUsage( FILE *file, int verbose )
59
+ {
60
+ fprintf( file, "usage: dealer matchName gameDefFile #Hands rngSeed p1name p2name ... [options]\n" );
61
+ fprintf( file, " -f use fixed dealer button at table\n" );
62
+ fprintf( file, " -l/L disable/enable log file - enabled by default\n" );
63
+ fprintf( file, " -p player1_port,player2_port,... [default is random]\n" );
64
+ fprintf( file, " -q only print errors, warnings, and final value to stderr\n" );
65
+ fprintf( file, " -t/T disable/enable transaction file - disabled by default\n" );
66
+ fprintf( file, " -a append to log/transaction files - disabled by default\n" );
67
+ fprintf( file, " --t_response [milliseconds] maximum time per response\n" );
68
+ fprintf( file, " --t_hand [milliseconds] maximum player time per hand\n" );
69
+ fprintf( file, " --t_per_hand [milliseconds] maximum average player time for match\n" );
70
+ fprintf( file, " --start_timeout [milliseconds] maximum time to wait for players to connect\n" );
71
+ fprintf( file, " <0 [default] is no timeout\n" );
72
+ }
73
+
74
+ /* returns >= 0 on success, -1 on error */
75
+ static int scanPortString( const char *string,
76
+ uint16_t listenPort[ MAX_PLAYERS ] )
77
+ {
78
+ int c, r, p;
79
+
80
+ c = 0;
81
+ for( p = 0; p < MAX_PLAYERS; ++p ) {
82
+
83
+ if( string[ c ] == 0 ) {
84
+ /* finished parsing the string */
85
+
86
+ break;
87
+ }
88
+
89
+ if( p ) {
90
+ /* look for separator */
91
+
92
+ if( string[ c ] != ',' ) {
93
+ /* numbers should be comma separated */
94
+
95
+ return -1;
96
+ }
97
+ ++c;
98
+ }
99
+
100
+ if( sscanf( &string[ c ], "%"SCNu16"%n", &listenPort[ p ], &r ) < 1 ) {
101
+ /* couldn't get a number */
102
+
103
+ return -1;
104
+ }
105
+ c += r;
106
+ }
107
+
108
+ return 0;
109
+ }
110
+
111
+ static void initErrorInfo( const uint32_t maxInvalidActions,
112
+ const uint64_t maxResponseMicros,
113
+ const uint64_t maxUsedHandMicros,
114
+ const uint64_t maxUsedMatchMicros,
115
+ ErrorInfo *info )
116
+ {
117
+ int s;
118
+
119
+ info->maxInvalidActions = maxInvalidActions;
120
+ info->maxResponseMicros = maxResponseMicros;
121
+ info->maxUsedHandMicros = maxUsedHandMicros;
122
+ info->maxUsedMatchMicros = maxUsedMatchMicros;
123
+
124
+ for( s = 0; s < MAX_PLAYERS; ++s ) {
125
+ info->numInvalidActions[ s ] = 0;
126
+ info->usedHandMicros[ s ] = 0;
127
+ info->usedMatchMicros[ s ] = 0;
128
+ }
129
+ }
130
+
131
+
132
+ /* update the number of invalid actions for seat
133
+ returns >= 0 if match should continue, -1 for failure */
134
+ static int checkErrorInvalidAction( const uint8_t seat, ErrorInfo *info )
135
+ {
136
+ ++( info->numInvalidActions[ seat ] );
137
+
138
+ if( info->numInvalidActions[ seat ] > info->maxInvalidActions ) {
139
+ return -1;
140
+ }
141
+
142
+ return 0;
143
+ }
144
+
145
+ /* update the time used by seat
146
+ returns >= 0 if match should continue, -1 for failure */
147
+ static int checkErrorTimes( const uint8_t seat,
148
+ const struct timeval *sendTime,
149
+ const struct timeval *recvTime,
150
+ ErrorInfo *info )
151
+ {
152
+ uint64_t responseMicros;
153
+
154
+ /* calls to gettimeofday can return earlier times on later calls :/ */
155
+ if( recvTime->tv_sec < sendTime->tv_sec
156
+ || ( recvTime->tv_sec == sendTime->tv_sec
157
+ && recvTime->tv_usec < sendTime->tv_usec ) ) {
158
+ return 0;
159
+ }
160
+
161
+ /* figure out how many microseconds the response took */
162
+ responseMicros = ( recvTime->tv_sec - sendTime->tv_sec ) * 1000000
163
+ + recvTime->tv_usec - sendTime->tv_usec;
164
+
165
+ /* update usage counts */
166
+ info->usedHandMicros[ seat ] += responseMicros;
167
+ info->usedMatchMicros[ seat ] += responseMicros;
168
+
169
+ /* check time used for the response */
170
+ if( responseMicros > info->maxResponseMicros ) {
171
+ return -1;
172
+ }
173
+
174
+ /* check time used in the current hand */
175
+ if( info->usedHandMicros[ seat ] > info->maxUsedHandMicros ) {
176
+ return -1;
177
+ }
178
+
179
+ /* check time used in the entire match */
180
+ if( info->usedMatchMicros[ seat ] > info->maxUsedMatchMicros ) {
181
+ return -1;
182
+ }
183
+
184
+ return 0;
185
+ }
186
+
187
+ /* note that there is a new hand
188
+ returns >= 0 if match should continue, -1 for failure */
189
+ static int checkErrorNewHand( const Game *game, ErrorInfo *info )
190
+ {
191
+ uint8_t p;
192
+
193
+ for( p = 0; p < game->numPlayers; ++p ) {
194
+ info->usedHandMicros[ p ] = 0;
195
+ }
196
+
197
+ return 0;
198
+ }
199
+
200
+
201
+ static uint8_t seatToPlayer( const Game *game, const uint8_t player0Seat,
202
+ const uint8_t seat )
203
+ {
204
+ return ( seat + game->numPlayers - player0Seat ) % game->numPlayers;
205
+ }
206
+
207
+ static uint8_t playerToSeat( const Game *game, const uint8_t player0Seat,
208
+ const uint8_t player )
209
+ {
210
+ return ( player + player0Seat ) % game->numPlayers;
211
+ }
212
+
213
+ /* returns >= 0 if match should continue, -1 for failure */
214
+ static int sendPlayerMessage( const Game *game, const MatchState *state,
215
+ const int quiet, const uint8_t seat,
216
+ const int seatFD, struct timeval *sendTime )
217
+ {
218
+ int c;
219
+ char line[ MAX_LINE_LEN ];
220
+
221
+ /* prepare the message */
222
+ c = printMatchState( game, state, MAX_LINE_LEN, line );
223
+ if( c < 0 || c > MAX_LINE_LEN - 3 ) {
224
+ /* message is too long */
225
+
226
+ fprintf( stderr, "ERROR: state message too long\n" );
227
+ return -1;
228
+ }
229
+ line[ c ] = '\r';
230
+ line[ c + 1 ] = '\n';
231
+ line[ c + 2 ] = 0;
232
+ c += 2;
233
+
234
+ /* send it to the player and flush */
235
+ if( write( seatFD, line, c ) != c ) {
236
+ /* couldn't send the line */
237
+
238
+ fprintf( stderr, "ERROR: could not send state to seat %"PRIu8"\n",
239
+ seat + 1 );
240
+ return -1;
241
+ }
242
+
243
+ /* note when we sent the message */
244
+ gettimeofday( sendTime, NULL );
245
+
246
+ /* log the message */
247
+ if( !quiet ) {
248
+ fprintf( stderr, "TO %d at %zu.%.06zu %s", seat + 1,
249
+ sendTime->tv_sec, sendTime->tv_usec, line );
250
+ }
251
+
252
+ return 0;
253
+ }
254
+
255
+ /* returns >= 0 if action/size has been set to a valid action
256
+ returns -1 for failure (disconnect, timeout, too many bad actions, etc) */
257
+ static int readPlayerResponse( const Game *game,
258
+ const MatchState *state,
259
+ const int quiet,
260
+ const uint8_t seat,
261
+ const struct timeval *sendTime,
262
+ ErrorInfo *errorInfo,
263
+ ReadBuf *readBuf,
264
+ Action *action,
265
+ struct timeval *recvTime )
266
+ {
267
+ int c, r;
268
+ MatchState tempState;
269
+ char line[ MAX_LINE_LEN ];
270
+
271
+ while( 1 ) {
272
+
273
+ /* read a line of input from player */
274
+ struct timeval start;
275
+ gettimeofday( &start, NULL );
276
+ if( getLine( readBuf, MAX_LINE_LEN, line,
277
+ errorInfo->maxResponseMicros ) <= 0 ) {
278
+ /* couldn't get any input from player */
279
+
280
+ struct timeval after;
281
+ gettimeofday( &after, NULL );
282
+ uint64_t micros_spent =
283
+ (uint64_t)( after.tv_sec - start.tv_sec ) * 1000000
284
+ + ( after.tv_usec - start.tv_usec );
285
+ fprintf( stderr, "ERROR: could not get action from seat %"PRIu8"\n",
286
+ seat + 1 );
287
+ // Print out how much time has passed so we can see if this was a
288
+ // timeout as opposed to some other sort of failure (e.g., socket
289
+ // closing).
290
+ fprintf( stderr, "%.1f seconds spent waiting; timeout %.1f\n",
291
+ micros_spent / 1000000.0,
292
+ errorInfo->maxResponseMicros / 1000000.0);
293
+ return -1;
294
+ }
295
+
296
+ /* note when the message arrived */
297
+ gettimeofday( recvTime, NULL );
298
+
299
+ /* log the response */
300
+ if( !quiet ) {
301
+ fprintf( stderr, "FROM %d at %zu.%06zu %s", seat + 1,
302
+ recvTime->tv_sec, recvTime->tv_usec, line );
303
+ }
304
+
305
+ /* ignore comments */
306
+ if( line[ 0 ] == '#' || line[ 0 ] == ';' ) {
307
+ continue;
308
+ }
309
+
310
+ /* check for any timeout issues */
311
+ if( checkErrorTimes( seat, sendTime, recvTime, errorInfo ) < 0 ) {
312
+
313
+ fprintf( stderr, "ERROR: seat %"PRIu8" ran out of time\n", seat + 1 );
314
+ return -1;
315
+ }
316
+
317
+ /* parse out the state */
318
+ c = readMatchState( line, game, &tempState );
319
+ if( c < 0 ) {
320
+ /* couldn't get an intelligible state */
321
+
322
+ fprintf( stderr, "WARNING: bad state format in response\n" );
323
+ continue;
324
+ }
325
+
326
+ /* ignore responses that don't match the current state */
327
+ if( !matchStatesEqual( game, state, &tempState ) ) {
328
+
329
+ fprintf( stderr, "WARNING: ignoring un-requested response\n" );
330
+ continue;
331
+ }
332
+
333
+ /* get the action */
334
+ if( line[ c++ ] != ':'
335
+ || ( r = readAction( &line[ c ], game, action ) ) < 0 ) {
336
+
337
+ if( checkErrorInvalidAction( seat, errorInfo ) < 0 ) {
338
+
339
+ fprintf( stderr, "ERROR: bad action format in response\n" );
340
+ }
341
+
342
+ fprintf( stderr,
343
+ "WARNING: bad action format in response, changed to call\n" );
344
+ action->type = a_call;
345
+ action->size = 0;
346
+ goto doneRead;
347
+ }
348
+ c += r;
349
+
350
+ /* make sure the action is valid */
351
+ if( !isValidAction( game, &state->state, 1, action ) ) {
352
+
353
+ if( checkErrorInvalidAction( seat, errorInfo ) < 0 ) {
354
+
355
+ fprintf( stderr, "ERROR: invalid action\n" );
356
+ return -1;
357
+ }
358
+
359
+ fprintf( stderr, "WARNING: invalid action, changed to call\n" );
360
+ action->type = a_call;
361
+ action->size = 0;
362
+ }
363
+
364
+ goto doneRead;
365
+ }
366
+
367
+ doneRead:
368
+ return 0;
369
+ }
370
+
371
+ /* returns >= 0 if match should continue, -1 for failure */
372
+ static int setUpNewHand( const Game *game, const uint8_t fixedSeats,
373
+ uint32_t *handId, uint8_t *player0Seat,
374
+ rng_state_t *rng, ErrorInfo *errorInfo, State *state )
375
+ {
376
+ ++( *handId );
377
+
378
+ /* rotate the players around the table */
379
+ if( !fixedSeats ) {
380
+
381
+ *player0Seat = ( *player0Seat + 1 ) % game->numPlayers;
382
+ }
383
+
384
+ if( checkErrorNewHand( game, errorInfo ) < 0 ) {
385
+
386
+ fprintf( stderr, "ERROR: unexpected game\n" );
387
+ return -1;
388
+ }
389
+ initState( game, *handId, state );
390
+ dealCards( game, rng, state );
391
+
392
+ return 0;
393
+ }
394
+
395
+ /* returns >= 0 if match should continue, -1 for failure */
396
+ static int processTransactionFile( const Game *game, const int fixedSeats,
397
+ uint32_t *handId, uint8_t *player0Seat,
398
+ rng_state_t *rng, ErrorInfo *errorInfo,
399
+ double totalValue[ MAX_PLAYERS ],
400
+ MatchState *state, FILE *file )
401
+ {
402
+ int c, r;
403
+ uint32_t h;
404
+ uint8_t s;
405
+ Action action;
406
+ struct timeval sendTime, recvTime;
407
+ char line[ MAX_LINE_LEN ];
408
+
409
+ while( fgets( line, MAX_LINE_LEN, file ) ) {
410
+
411
+ /* get the log entry */
412
+
413
+ /* ACTION */
414
+ c = readAction( line, game, &action );
415
+ if( c < 0 ) {
416
+
417
+ fprintf( stderr, "ERROR: could not parse transaction action %s", line );
418
+ return -1;
419
+ }
420
+
421
+ /* ACTION HANDID SEND RECV */
422
+ if( sscanf( &line[ c ], " %"SCNu32" %zu.%06zu %zu.%06zu%n", &h,
423
+ &sendTime.tv_sec, &sendTime.tv_usec,
424
+ &recvTime.tv_sec, &recvTime.tv_usec, &r ) < 4 ) {
425
+
426
+ fprintf( stderr, "ERROR: could not parse transaction stamp %s", line );
427
+ return -1;
428
+ }
429
+ c += r;
430
+
431
+ /* check that we're processing the expected handId */
432
+ if( h != *handId ) {
433
+
434
+ fprintf( stderr, "ERROR: handId mismatch in transaction log: %s", line );
435
+ return -1;
436
+ }
437
+
438
+ /* make sure the action is valid */
439
+ if( !isValidAction( game, &state->state, 0, &action ) ) {
440
+
441
+ fprintf( stderr, "ERROR: invalid action in transaction log: %s", line );
442
+ return -1;
443
+ }
444
+
445
+ /* check for any timeout issues */
446
+ s = playerToSeat( game, *player0Seat,
447
+ currentPlayer( game, &state->state ) );
448
+ if( checkErrorTimes( s, &sendTime, &recvTime, errorInfo ) < 0 ) {
449
+
450
+ fprintf( stderr,
451
+ "ERROR: seat %"PRIu8" ran out of time in transaction file\n",
452
+ s + 1 );
453
+ return -1;
454
+ }
455
+
456
+ doAction( game, &action, &state->state );
457
+
458
+ if( stateFinished( &state->state ) ) {
459
+ /* hand is finished */
460
+
461
+ /* update the total value for each player */
462
+ for( s = 0; s < game->numPlayers; ++s ) {
463
+
464
+ totalValue[ s ]
465
+ += valueOfState( game, &state->state,
466
+ seatToPlayer( game, *player0Seat, s ) );
467
+ }
468
+
469
+ /* move on to next hand */
470
+ if( setUpNewHand( game, fixedSeats, handId, player0Seat,
471
+ rng, errorInfo, &state->state ) < 0 ) {
472
+
473
+ return -1;
474
+ }
475
+ }
476
+ }
477
+
478
+ return 0;
479
+ }
480
+
481
+ /* returns >= 0 if match should continue, -1 on failure */
482
+ static int logTransaction( const Game *game, const State *state,
483
+ const Action *action,
484
+ const struct timeval *sendTime,
485
+ const struct timeval *recvTime,
486
+ FILE *file )
487
+ {
488
+ int c, r;
489
+ char line[ MAX_LINE_LEN ];
490
+
491
+ c = printAction( game, action, MAX_LINE_LEN, line );
492
+ if( c < 0 ) {
493
+
494
+ fprintf( stderr, "ERROR: transaction message too long\n" );
495
+ return -1;
496
+ }
497
+
498
+ r = snprintf( &line[ c ], MAX_LINE_LEN - c,
499
+ " %"PRIu32" %zu.%06zu %zu.%06zu\n",
500
+ state->handId, sendTime->tv_sec, sendTime->tv_usec,
501
+ recvTime->tv_sec, recvTime->tv_usec );
502
+ if( r < 0 ) {
503
+
504
+ fprintf( stderr, "ERROR: transaction message too long\n" );
505
+ return -1;
506
+ }
507
+ c += r;
508
+
509
+ if( fwrite( line, 1, c, file ) != c ) {
510
+
511
+ fprintf( stderr, "ERROR: could not write to transaction file\n" );
512
+ return -1;
513
+ }
514
+ fflush( file );
515
+
516
+ return c;
517
+ }
518
+
519
+ /* returns >= 0 if match should continue, -1 on failure */
520
+ static int checkVersion( const uint8_t seat,
521
+ ReadBuf *readBuf )
522
+ {
523
+ uint32_t major, minor, rev;
524
+ char line[ MAX_LINE_LEN ];
525
+
526
+
527
+ if( getLine( readBuf, MAX_LINE_LEN, line, -1 ) <= 0 ) {
528
+
529
+ fprintf( stderr,
530
+ "ERROR: could not read version string from seat %"PRIu8"\n",
531
+ seat + 1 );
532
+ return -1;
533
+ }
534
+
535
+ if( sscanf( line, "VERSION:%"SCNu32".%"SCNu32".%"SCNu32,
536
+ &major, &minor, &rev ) < 3 ) {
537
+
538
+ fprintf( stderr,
539
+ "ERROR: invalid version string %s", line );
540
+ return -1;
541
+ }
542
+
543
+ if( major != VERSION_MAJOR || minor > VERSION_MINOR ) {
544
+
545
+ fprintf( stderr, "ERROR: this server is currently using version %"SCNu32".%"SCNu32".%"SCNu32"\n", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION );
546
+ }
547
+
548
+ return 0;
549
+ }
550
+
551
+ /* returns >= 0 if match should continue, -1 on failure */
552
+ static int addToLogFile( const Game *game, const State *state,
553
+ const double value[ MAX_PLAYERS ],
554
+ const uint8_t player0Seat,
555
+ char *seatName[ MAX_PLAYERS ], FILE *logFile )
556
+ {
557
+ int c, r;
558
+ uint8_t p;
559
+ char line[ MAX_LINE_LEN ];
560
+
561
+ /* prepare the message */
562
+ c = printState( game, state, MAX_LINE_LEN, line );
563
+ if( c < 0 ) {
564
+ /* message is too long */
565
+
566
+ fprintf( stderr, "ERROR: log state message too long\n" );
567
+ return -1;
568
+ }
569
+
570
+ /* add the values */
571
+ for( p = 0; p < game->numPlayers; ++p ) {
572
+
573
+ r = snprintf( &line[ c ], MAX_LINE_LEN - c,
574
+ p ? "|%.6f" : ":%.6f", value[ p ] );
575
+ if( r < 0 ) {
576
+
577
+ fprintf( stderr, "ERROR: log message too long\n" );
578
+ return -1;
579
+ }
580
+ c += r;
581
+
582
+ /* remove trailing zeros after decimal-point */
583
+ while( line[ c - 1 ] == '0' ) { --c; }
584
+ if( line[ c - 1 ] == '.' ) { --c; }
585
+ line[ c ] = 0;
586
+ }
587
+
588
+ /* add the player names */
589
+ for( p = 0; p < game->numPlayers; ++p ) {
590
+
591
+ r = snprintf( &line[ c ], MAX_LINE_LEN - c,
592
+ p ? "|%s" : ":%s",
593
+ seatName[ playerToSeat( game, player0Seat, p ) ] );
594
+ if( r < 0 ) {
595
+
596
+ fprintf( stderr, "ERROR: log message too long\n" );
597
+ return -1;
598
+ }
599
+ c += r;
600
+ }
601
+
602
+ /* print the line to log and flush */
603
+ if( fprintf( logFile, "%s\n", line ) < 0 ) {
604
+
605
+ fprintf( stderr, "ERROR: logging failed for game %s\n", line );
606
+ return -1;
607
+ }
608
+ fflush( logFile );
609
+
610
+ return 0;
611
+ }
612
+
613
+ /* returns >= 0 if match should continue, -1 on failure */
614
+ static int printInitialMessage( const char *matchName, const char *gameName,
615
+ const uint32_t numHands, const uint32_t seed,
616
+ const ErrorInfo *info, FILE *logFile )
617
+ {
618
+ int c;
619
+ char line[ MAX_LINE_LEN ];
620
+
621
+ c = snprintf( line, MAX_LINE_LEN, "# name/game/hands/seed %s %s %"PRIu32" %"PRIu32"\n#--t_response %"PRIu64"\n#--t_hand %"PRIu64"\n#--t_per_hand %"PRIu64"\n",
622
+ matchName, gameName, numHands, seed,
623
+ info->maxResponseMicros / 1000,
624
+ info->maxUsedHandMicros / 1000,
625
+ info->maxUsedMatchMicros / numHands / 1000 );
626
+ if( c < 0 ) {
627
+ /* message is too long */
628
+
629
+ fprintf( stderr, "ERROR: initial game comment too long\n" );
630
+ return -1;
631
+ }
632
+
633
+ fprintf( stderr, "%s", line );
634
+ if( logFile ) {
635
+
636
+ fprintf( logFile, "%s", line );
637
+ }
638
+
639
+ return 0;
640
+ }
641
+
642
+ /* returns >= 0 if match should continue, -1 on failure */
643
+ static int printFinalMessage( const Game *game, char *seatName[ MAX_PLAYERS ],
644
+ const double totalValue[ MAX_PLAYERS ],
645
+ FILE *logFile )
646
+ {
647
+ int c, r;
648
+ uint8_t s;
649
+ char line[ MAX_LINE_LEN ];
650
+
651
+ c = snprintf( line, MAX_LINE_LEN, "SCORE" );
652
+ if( c < 0 ) {
653
+ /* message is too long */
654
+
655
+ fprintf( stderr, "ERROR: value state message too long\n" );
656
+ return -1;
657
+ }
658
+
659
+ for( s = 0; s < game->numPlayers; ++s ) {
660
+
661
+ r = snprintf( &line[ c ], MAX_LINE_LEN - c,
662
+ s ? "|%.6f" : ":%.6f", totalValue[ s ] );
663
+ if( r < 0 ) {
664
+
665
+ fprintf( stderr, "ERROR: value message too long\n" );
666
+ return -1;
667
+ }
668
+ c += r;
669
+
670
+ /* remove trailing zeros after decimal-point */
671
+ while( line[ c - 1 ] == '0' ) { --c; }
672
+ if( line[ c - 1 ] == '.' ) { --c; }
673
+ line[ c ] = 0;
674
+ }
675
+
676
+ /* add the player names */
677
+ for( s = 0; s < game->numPlayers; ++s ) {
678
+
679
+ r = snprintf( &line[ c ], MAX_LINE_LEN - c,
680
+ s ? "|%s" : ":%s", seatName[ s ] );
681
+ if( r < 0 ) {
682
+
683
+ fprintf( stderr, "ERROR: log message too long\n" );
684
+ return -1;
685
+ }
686
+ c += r;
687
+ }
688
+
689
+ fprintf( stdout, "%s\n", line );
690
+ fprintf( stderr, "%s\n", line );
691
+
692
+ if( logFile ) {
693
+
694
+ fprintf( logFile, "%s\n", line );
695
+ }
696
+
697
+ return 0;
698
+ }
699
+
700
+ /* run a match of numHands hands of the supplied game
701
+
702
+ cards are dealt using rng, error conditions like timeouts
703
+ are controlled and stored in errorInfo
704
+
705
+ actions are read/sent to seat p on seatFD[ p ]
706
+
707
+ if quiet is not zero, only print out errors, warnings, and final value
708
+
709
+ if logFile is not NULL, print out a single line for each completed
710
+ match with the final state and all player values. The values are
711
+ printed in player, not seat order.
712
+
713
+ if transactionFile is not NULL, a transaction log of actions made
714
+ is written to the file, and if there is any input left to read on
715
+ the stream when gameLoop is called, it will be processed to
716
+ initialise the state
717
+
718
+ returns >=0 if the match finished correctly, -1 on error */
719
+ static int gameLoop( const Game *game, char *seatName[ MAX_PLAYERS ],
720
+ const uint32_t numHands, const int quiet,
721
+ const int fixedSeats, rng_state_t *rng,
722
+ ErrorInfo *errorInfo, const int seatFD[ MAX_PLAYERS ],
723
+ ReadBuf *readBuf[ MAX_PLAYERS ],
724
+ FILE *logFile, FILE *transactionFile )
725
+ {
726
+ uint32_t handId;
727
+ uint8_t seat, p, player0Seat, currentP, currentSeat;
728
+ struct timeval t, sendTime, recvTime;
729
+ Action action;
730
+ MatchState state;
731
+ double value[ MAX_PLAYERS ], totalValue[ MAX_PLAYERS ];
732
+
733
+ /* check version string for each player */
734
+ for( seat = 0; seat < game->numPlayers; ++seat ) {
735
+
736
+ if( checkVersion( seat, readBuf[ seat ] ) < 0 ) {
737
+ /* error messages already handled in function */
738
+
739
+ return -1;
740
+ }
741
+ }
742
+
743
+ gettimeofday( &sendTime, NULL );
744
+ if( !quiet ) {
745
+ fprintf( stderr, "STARTED at %zu.%06zu\n",
746
+ sendTime.tv_sec, sendTime.tv_usec );
747
+ }
748
+
749
+ /* start at the first hand */
750
+ handId = 0;
751
+ if( checkErrorNewHand( game, errorInfo ) < 0 ) {
752
+
753
+ fprintf( stderr, "ERROR: unexpected game\n" );
754
+ return -1;
755
+ }
756
+ initState( game, handId, &state.state );
757
+ dealCards( game, rng, &state.state );
758
+ for( seat = 0; seat < game->numPlayers; ++seat ) {
759
+ totalValue[ seat ] = 0.0;
760
+ }
761
+
762
+ /* seat 0 is player 0 in first game */
763
+ player0Seat = 0;
764
+
765
+ /* process the transaction file */
766
+ if( transactionFile != NULL ) {
767
+
768
+ if( processTransactionFile( game, fixedSeats, &handId, &player0Seat,
769
+ rng, errorInfo, totalValue,
770
+ &state, transactionFile ) < 0 ) {
771
+ /* error messages already handled in function */
772
+
773
+ return -1;
774
+ }
775
+ }
776
+
777
+ if( handId >= numHands ) {
778
+ goto finishedGameLoop;
779
+ }
780
+
781
+ /* play all the (remaining) hands */
782
+ while( 1 ) {
783
+
784
+ /* play the hand */
785
+ while( !stateFinished( &state.state ) ) {
786
+
787
+ /* find the current player */
788
+ currentP = currentPlayer( game, &state.state );
789
+
790
+ /* send state to each player */
791
+ for( seat = 0; seat < game->numPlayers; ++seat ) {
792
+
793
+ state.viewingPlayer = seatToPlayer( game, player0Seat, seat );
794
+ if( sendPlayerMessage( game, &state, quiet, seat,
795
+ seatFD[ seat ], &t ) < 0 ) {
796
+ /* error messages already handled in function */
797
+
798
+ return -1;
799
+ }
800
+
801
+ /* remember the seat and send time if player is acting */
802
+ if( state.viewingPlayer == currentP ) {
803
+
804
+ sendTime = t;
805
+ }
806
+ }
807
+
808
+ /* get action from current player */
809
+ state.viewingPlayer = currentP;
810
+ currentSeat = playerToSeat( game, player0Seat, currentP );
811
+ if( readPlayerResponse( game, &state, quiet, currentSeat, &sendTime,
812
+ errorInfo, readBuf[ currentSeat ],
813
+ &action, &recvTime ) < 0 ) {
814
+ /* error messages already handled in function */
815
+
816
+ return -1;
817
+ }
818
+
819
+ /* log the transaction */
820
+ if( transactionFile != NULL ) {
821
+
822
+ if( logTransaction( game, &state.state, &action,
823
+ &sendTime, &recvTime, transactionFile ) < 0 ) {
824
+ /* error messages already handled in function */
825
+
826
+ return -1;
827
+ }
828
+ }
829
+
830
+ /* do the action */
831
+ doAction( game, &action, &state.state );
832
+ }
833
+
834
+ /* get values */
835
+ for( p = 0; p < game->numPlayers; ++p ) {
836
+
837
+ value[ p ] = valueOfState( game, &state.state, p );
838
+ totalValue[ playerToSeat( game, player0Seat, p ) ] += value[ p ];
839
+ }
840
+
841
+ /* add the game to the log */
842
+ if( logFile != NULL ) {
843
+
844
+ if( addToLogFile( game, &state.state, value, player0Seat,
845
+ seatName, logFile ) < 0 ) {
846
+ /* error messages already handled in function */
847
+
848
+ return -1;
849
+ }
850
+ }
851
+
852
+ /* send final state to each player */
853
+ for( seat = 0; seat < game->numPlayers; ++seat ) {
854
+
855
+ state.viewingPlayer = seatToPlayer( game, player0Seat, seat );
856
+ if( sendPlayerMessage( game, &state, quiet, seat,
857
+ seatFD[ seat ], &t ) < 0 ) {
858
+ /* error messages already handled in function */
859
+
860
+ return -1;
861
+ }
862
+ }
863
+
864
+ /* start a new hand */
865
+ if( setUpNewHand( game, fixedSeats, &handId, &player0Seat,
866
+ rng, errorInfo, &state.state ) < 0 ) {
867
+ /* error messages already handled in function */
868
+
869
+ return -1;
870
+ }
871
+ if( handId >= numHands ) {
872
+ break;
873
+ }
874
+ }
875
+
876
+ finishedGameLoop:
877
+ /* print out the final values */
878
+ if( !quiet ) {
879
+ gettimeofday( &t, NULL );
880
+ fprintf( stderr, "FINISHED at %zu.%06zu\n",
881
+ sendTime.tv_sec, sendTime.tv_usec );
882
+ }
883
+ if( printFinalMessage( game, seatName, totalValue, logFile ) < 0 ) {
884
+ /* error messages already handled in function */
885
+
886
+ return -1;
887
+ }
888
+
889
+ return 0;
890
+ }
891
+
892
+ int main( int argc, char **argv )
893
+ {
894
+ int i, listenSocket[ MAX_PLAYERS ], v, longOpt;
895
+ int fixedSeats, quiet, append;
896
+ int seatFD[ MAX_PLAYERS ];
897
+ FILE *file, *logFile, *transactionFile;
898
+ ReadBuf *readBuf[ MAX_PLAYERS ];
899
+ Game *game;
900
+ rng_state_t rng;
901
+ ErrorInfo errorInfo;
902
+ struct sockaddr_in addr;
903
+ socklen_t addrLen;
904
+ char *seatName[ MAX_PLAYERS ];
905
+
906
+ int useLogFile, useTransactionFile;
907
+ uint64_t maxResponseMicros, maxUsedHandMicros, maxUsedPerHandMicros;
908
+ int64_t startTimeoutMicros;
909
+ uint32_t numHands, seed, maxInvalidActions;
910
+ uint16_t listenPort[ MAX_PLAYERS ];
911
+
912
+ struct timeval startTime, tv;
913
+
914
+ char name[ MAX_LINE_LEN ];
915
+ static struct option longOptions[] = {
916
+ { "t_response", 1, 0, 0 },
917
+ { "t_hand", 1, 0, 0 },
918
+ { "t_per_hand", 1, 0, 0 },
919
+ { "start_timeout", 1, 0, 0 },
920
+ { 0, 0, 0, 0 }
921
+ };
922
+
923
+ /* set defaults */
924
+
925
+ /* game error conditions */
926
+ maxInvalidActions = DEFAULT_MAX_INVALID_ACTIONS;
927
+ maxResponseMicros = DEFAULT_MAX_RESPONSE_MICROS;
928
+ maxUsedHandMicros = DEFAULT_MAX_USED_HAND_MICROS;
929
+ maxUsedPerHandMicros = DEFAULT_MAX_USED_PER_HAND_MICROS;
930
+
931
+ /* use random ports */
932
+ for( i = 0; i < MAX_PLAYERS; ++i ) {
933
+
934
+ listenPort[ i ] = 0;
935
+ }
936
+
937
+ /* use log file, don't use transaction file */
938
+ useLogFile = 1;
939
+ useTransactionFile = 0;
940
+
941
+ /* print all messages */
942
+ quiet = 0;
943
+
944
+ /* by default, overwrite preexisting log/transaction files */
945
+ append = 0;
946
+
947
+ /* players rotate around the table */
948
+ fixedSeats = 0;
949
+
950
+ /* no timeout on startup */
951
+ startTimeoutMicros = -1;
952
+
953
+ /* parse options */
954
+ while( 1 ) {
955
+
956
+ i = getopt_long( argc, argv, "flLp:qtTa", longOptions, &longOpt );
957
+ if( i < 0 ) {
958
+
959
+ break;
960
+ }
961
+
962
+ switch( i ) {
963
+ case 0:
964
+ /* long option longOpt */
965
+
966
+ switch( longOpt ) {
967
+ case 0:
968
+ /* t_response */
969
+
970
+ if( sscanf( optarg, "%"SCNu64, &maxResponseMicros ) < 1 ) {
971
+
972
+ fprintf( stderr, "ERROR: could not get response timeout from %s\n",
973
+ optarg );
974
+ exit( EXIT_FAILURE );
975
+ }
976
+
977
+ /* convert from milliseconds to microseconds */
978
+ maxResponseMicros *= 1000;
979
+ break;
980
+
981
+ case 1:
982
+ /* t_hand */
983
+
984
+ if( sscanf( optarg, "%"SCNu64, &maxUsedHandMicros ) < 1 ) {
985
+
986
+ fprintf( stderr,
987
+ "ERROR: could not get player hand timeout from %s\n",
988
+ optarg );
989
+ exit( EXIT_FAILURE );
990
+ }
991
+
992
+ /* convert from milliseconds to microseconds */
993
+ maxUsedHandMicros *= 1000;
994
+ break;
995
+
996
+ case 2:
997
+ /* t_per_hand */
998
+
999
+ if( sscanf( optarg, "%"SCNu64, &maxUsedPerHandMicros ) < 1 ) {
1000
+
1001
+ fprintf( stderr, "ERROR: could not get average player hand timeout from %s\n", optarg );
1002
+ exit( EXIT_FAILURE );
1003
+ }
1004
+
1005
+ /* convert from milliseconds to microseconds */
1006
+ maxUsedPerHandMicros *= 1000;
1007
+ break;
1008
+
1009
+ case 3:
1010
+ /* start_timeout */
1011
+
1012
+ if( sscanf( optarg, "%"SCNd64, &startTimeoutMicros ) < 1 ) {
1013
+
1014
+ fprintf( stderr, "ERROR: could not get start timeout %s\n", optarg );
1015
+ exit( EXIT_FAILURE );
1016
+ }
1017
+
1018
+ /* convert from milliseconds to microseconds */
1019
+ if( startTimeoutMicros > 0 ) {
1020
+
1021
+ startTimeoutMicros *= 1000;
1022
+ }
1023
+ break;
1024
+
1025
+ }
1026
+ break;
1027
+
1028
+ case 'f':
1029
+ /* fix the player seats */;
1030
+
1031
+ fixedSeats = 1;
1032
+ break;
1033
+
1034
+ case 'l':
1035
+ /* no transactionFile */;
1036
+
1037
+ useLogFile = 0;
1038
+ break;
1039
+
1040
+ case 'L':
1041
+ /* use transactionFile */;
1042
+
1043
+ useLogFile = 1;
1044
+ break;
1045
+
1046
+ case 'p':
1047
+ /* port specification */
1048
+
1049
+ if( scanPortString( optarg, listenPort ) < 0 ) {
1050
+
1051
+ fprintf( stderr, "ERROR: bad port string %s\n", optarg );
1052
+ exit( EXIT_FAILURE );
1053
+ }
1054
+
1055
+ break;
1056
+
1057
+ case 'q':
1058
+
1059
+ quiet = 1;
1060
+ break;
1061
+
1062
+ case 't':
1063
+ /* no transactionFile */
1064
+
1065
+ useTransactionFile = 0;
1066
+ break;
1067
+
1068
+ case 'T':
1069
+ /* use transactionFile */
1070
+
1071
+ useTransactionFile = 1;
1072
+ break;
1073
+
1074
+ case 'a':
1075
+
1076
+ append = 1;
1077
+ break;
1078
+
1079
+ default:
1080
+
1081
+ fprintf( stderr, "ERROR: unknown option %c\n", i );
1082
+ exit( EXIT_FAILURE );
1083
+ }
1084
+ }
1085
+
1086
+ if( optind + 4 > argc ) {
1087
+
1088
+ printUsage( stdout, 0 );
1089
+ exit( EXIT_FAILURE );
1090
+ }
1091
+
1092
+ /* get the game definition */
1093
+ file = fopen( argv[ optind + 1 ], "r" );
1094
+ if( file == NULL ) {
1095
+
1096
+ fprintf( stderr, "ERROR: could not open game definition %s\n",
1097
+ argv[ optind + 1 ] );
1098
+ exit( EXIT_FAILURE );
1099
+ }
1100
+ game = readGame( file );
1101
+ if( game == NULL ) {
1102
+
1103
+ fprintf( stderr, "ERROR: could not read game %s\n", argv[ optind + 1 ] );
1104
+ exit( EXIT_FAILURE );
1105
+ }
1106
+ fclose( file );
1107
+
1108
+ /* save the seat names */
1109
+ if( optind + 4 + game->numPlayers > argc ) {
1110
+
1111
+ printUsage( stdout, 0 );
1112
+ exit( EXIT_FAILURE );
1113
+ }
1114
+ for( i = 0; i < game->numPlayers; ++i ) {
1115
+
1116
+ seatName[ i ] = argv[ optind + 4 + i ];
1117
+ }
1118
+
1119
+ /* get number of hands */
1120
+ if( sscanf( argv[ optind + 2 ], "%"SCNu32, &numHands ) < 1
1121
+ || numHands == 0 ) {
1122
+
1123
+ fprintf( stderr, "ERROR: invalid number of hands %s\n",
1124
+ argv[ optind + 2 ] );
1125
+ exit( EXIT_FAILURE );
1126
+ }
1127
+
1128
+ /* get random number seed */
1129
+ if( sscanf( argv[ optind + 3 ], "%"SCNu32, &seed ) < 1 ) {
1130
+
1131
+ fprintf( stderr, "ERROR: invalid random number seed %s\n",
1132
+ argv[ optind + 3 ] );
1133
+ exit( EXIT_FAILURE );
1134
+ }
1135
+ init_genrand( &rng, seed );
1136
+ srandom( seed ); /* used for random port selection */
1137
+
1138
+ if( useLogFile ) {
1139
+ /* create/open the log */
1140
+ if( snprintf( name, MAX_LINE_LEN, "%s.log", argv[ optind ] ) < 0 ) {
1141
+
1142
+ fprintf( stderr, "ERROR: match file name too long %s\n", argv[ optind ] );
1143
+ exit( EXIT_FAILURE );
1144
+ }
1145
+ if (append) {
1146
+ logFile = fopen( name, "a+" );
1147
+ } else {
1148
+ logFile = fopen( name, "w" );
1149
+ }
1150
+ if( logFile == NULL ) {
1151
+
1152
+ fprintf( stderr, "ERROR: could not open log file %s\n", name );
1153
+ exit( EXIT_FAILURE );
1154
+ }
1155
+ } else {
1156
+ /* no log file */
1157
+
1158
+ logFile = NULL;
1159
+ }
1160
+
1161
+ if( useTransactionFile ) {
1162
+ /* create/open the transaction log */
1163
+
1164
+ if( snprintf( name, MAX_LINE_LEN, "%s.tlog", argv[ optind ] ) < 0 ) {
1165
+
1166
+ fprintf( stderr, "ERROR: match file name too long %s\n", argv[ optind ] );
1167
+ exit( EXIT_FAILURE );
1168
+ }
1169
+ if (append) {
1170
+ transactionFile = fopen( name, "a+" );
1171
+ } else {
1172
+ transactionFile = fopen( name, "w" );
1173
+ }
1174
+ if( transactionFile == NULL ) {
1175
+
1176
+ fprintf( stderr, "ERROR: could not open transaction file %s\n", name );
1177
+ exit( EXIT_FAILURE );
1178
+ }
1179
+ } else {
1180
+ /* no transaction file */
1181
+
1182
+ transactionFile = NULL;
1183
+ }
1184
+
1185
+ /* set up the error info */
1186
+ initErrorInfo( maxInvalidActions, maxResponseMicros, maxUsedHandMicros,
1187
+ maxUsedPerHandMicros * numHands, &errorInfo );
1188
+
1189
+ /* open sockets for players to connect to */
1190
+ for( i = 0; i < game->numPlayers; ++i ) {
1191
+
1192
+ listenSocket[ i ] = getListenSocket( &listenPort[ i ] );
1193
+ if( listenSocket[ i ] < 0 ) {
1194
+
1195
+ fprintf( stderr, "ERROR: could not create listen socket for player %d\n",
1196
+ i + 1 );
1197
+ exit( EXIT_FAILURE );
1198
+ }
1199
+ }
1200
+
1201
+ /* print out the final port assignments */
1202
+ for( i = 0; i < game->numPlayers; ++i ) {
1203
+
1204
+ printf( i ? " %"PRIu16 : "%"PRIu16, listenPort[ i ] );
1205
+ }
1206
+ printf( "\n" );
1207
+ fflush( stdout );
1208
+
1209
+ /* print out usage information */
1210
+ printInitialMessage( argv[ optind ], argv[ optind + 1 ],
1211
+ numHands, seed, &errorInfo, logFile );
1212
+
1213
+ /* wait for each player to connect */
1214
+ gettimeofday( &startTime, NULL );
1215
+ for( i = 0; i < game->numPlayers; ++i ) {
1216
+
1217
+ if( startTimeoutMicros >= 0 ) {
1218
+ uint64_t startTimeLeft;
1219
+ fd_set fds;
1220
+
1221
+ gettimeofday( &tv, NULL );
1222
+ startTimeLeft = startTimeoutMicros
1223
+ - (uint64_t)( tv.tv_sec - startTime.tv_sec ) * 1000000
1224
+ - ( tv.tv_usec - startTime.tv_usec );
1225
+ if( startTimeLeft < 0 ) {
1226
+
1227
+ startTimeLeft = 0;
1228
+ }
1229
+ tv.tv_sec = startTimeLeft / 1000000;
1230
+ tv.tv_usec = startTimeLeft % 1000000;
1231
+
1232
+ FD_ZERO( &fds );
1233
+ FD_SET( listenSocket[ i ], &fds );
1234
+ if( select( listenSocket[ i ] + 1, &fds, NULL, NULL, &tv ) < 1 ) {
1235
+ /* no input ready within time, or an actual error */
1236
+
1237
+ fprintf( stderr, "ERROR: timed out wating for seat %d to connect\n",
1238
+ i + 1 );
1239
+ exit( EXIT_FAILURE );
1240
+ }
1241
+ }
1242
+
1243
+ addrLen = sizeof( addr );
1244
+ seatFD[ i ] = accept( listenSocket[ i ],
1245
+ (struct sockaddr *)&addr, &addrLen );
1246
+ if( seatFD[ i ] < 0 ) {
1247
+
1248
+ fprintf( stderr, "ERROR: seat %d could not connect\n", i + 1 );
1249
+ exit( EXIT_FAILURE );
1250
+ }
1251
+ close( listenSocket[ i ] );
1252
+
1253
+ v = 1;
1254
+ setsockopt( seatFD[ i ], IPPROTO_TCP, TCP_NODELAY,
1255
+ (char *)&v, sizeof(int) );
1256
+
1257
+ readBuf[ i ] = createReadBuf( seatFD[ i ] );
1258
+ }
1259
+
1260
+ /* play the match */
1261
+ if( gameLoop( game, seatName, numHands, quiet, fixedSeats, &rng, &errorInfo,
1262
+ seatFD, readBuf, logFile, transactionFile ) < 0 ) {
1263
+ /* should have already printed an error message */
1264
+
1265
+ exit( EXIT_FAILURE );
1266
+ }
1267
+
1268
+ fflush( NULL );
1269
+ if( transactionFile != NULL ) {
1270
+ fclose( transactionFile );
1271
+ }
1272
+ if( logFile != NULL ) {
1273
+ fclose( logFile );
1274
+ }
1275
+ free( game );
1276
+
1277
+ return EXIT_SUCCESS;
1278
+ }