1 /* $Header: /home/jcb/MahJong/newmj/RCS/greedy.c,v 12.2 2014/01/03 14:18:15 jcb Exp $
2 * greedy.c
3 * This is a computer player. Currently offensive only.
4 * Options not documented.
5 */
6 /****************** COPYRIGHT STATEMENT **********************
7 * This file is Copyright (c) 2000 by J. C. Bradfield. *
8 * Distribution and use is governed by the LICENCE file that *
9 * accompanies this file. *
10 * The moral rights of the author are asserted. *
11 * *
12 ***************** DISCLAIMER OF WARRANTY ********************
13 * This code is not warranted fit for any purpose. See the *
14 * LICENCE file for further information. *
15 * *
16 *************************************************************/
17
18 static const char rcs_id[] = "$Header: /home/jcb/MahJong/newmj/RCS/greedy.c,v 12.2 2014/01/03 14:18:15 jcb Exp $";
19
20 static int debugeval = 0;
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include "client.h"
25 #include "sysdep.h"
26
27 #include "version.h"
28
29 static double magic[28+13] =
30 {
31 12, 2, 1, 0.15, 1, 4, 4.97465626805558, 4, 1, 0.00638363133042069,
32 0, 1, 0.025, 0.4, 0.34, 0.4, 0.033333333, 0.3,
33 2, 25, 2, 25, 90.2519293502827, 3, 0.99, 1, -0.0011160627477312, 0.25,
34
35 6.0, 2.0, 25.0, 1.0, 12.0, 0.5, 3.0, 5.0, 0.5, 0.5, 0.5, 0.5,
36 0.5 };
37
38 static double *magic2 = &magic[28];
39
40
41
42 /* is this tile a doubling tile (or scoring pair) */
43 #define is_doubler(t) (is_dragon(t) || (is_wind(t) && \
44 (suit_of(t) == our_player->wind || suit_of(t) == the_game->round)))
45
46 Game *the_game;
47 int our_id = 0;
48 PlayerP our_player;
49 seats our_seat;
50
51 static char *password = NULL;
52
53 static FILE *debugf;
54
55 /* New style strategy */
56 typedef struct {
57 double chowness; /* how much do we like/dislike chows?
58 From -1.0 (no chows) to +1.0 (no pungs) */
59 double hiddenness; /* how much do we want to keep things concealed?
60 From 0.0 (don't care) to 1.0 (absolutely no claims) */
61 double majorness; /* are we trying to get all majors?
62 From 0.0 (don't care) to 1.0 (discard all minors) */
63 double suitness; /* are we trying to collect one suit? From 0.0 to 1.0 */
64 TileSuit suit; /* if so, which? */
65 } strategy;
66
67 /* values to try out */
68 typedef enum { chowness, hiddenness, majorness, suitness } stratparamtype;
69 struct { int ncomps; double values[5]; }
70 stratparams[suitness+1] =
71 { { 3, { 0.0, -0.9, 0.5 } } , /* chowness */
72 { 2, { 0.0, 0.8 } } , /* hiddenness */
73 { 1, { 0.0 } } , /* majorness */
74 { 2, { 0.0, 0.5 } } /* suitness */
75 } ;
76
77 /* routine to parse a comma separated list of floats into
78 the given strat param. Return num of comps, or -1 on error */
parsefloatlist(const char * fl,stratparamtype spt)79 static int parsefloatlist(const char *fl, stratparamtype spt) {
80 char *start, *end;
81 int i;
82 if ( ! fl ) { warn("null arg to parsefloatlist"); return -1; }
83 start = (char *)fl;
84 i = 0;
85 while ( i < 5 ) {
86 /* it is a feature that this will return zero on an empty
87 string, since we shd have at least one value */
88 stratparams[spt].values[i] = strtod(start,&end);
89 i++;
90 start = end;
91 if ( ! *start ) break;
92 if ( *start == ',' ) start++;
93 }
94 if ( *start ) warn("too many components, ignoring excess");
95 stratparams[spt].ncomps = i;
96 return i;
97 }
98
99
100
101 static strategy curstrat;
102 /* These are names for computed strategy values. See the awful mess
103 below... */
104 enum {pungbase,pairbase,chowbase,seqbase,sglbase,
105 partpung,partchow,exposedpungpenalty,exposedchowpenalty,
106 suitfactor, mjbonus, kongbonus,weight};
107
108 /* the value of a new strategy must exceed the current
109 by this amount for it to be chosen */
110 static double hysteresis = 4.0;
111
112
113 /* Used to note availability of tiles */
114 static int tilesleft[MaxTile];
115 /* track discards of player to right */
116 static int rightdiscs[MaxTile]; /* disc_ser of last of each tile discarded */
117 static int strategy_chosen;
118 static int despatch_line(char *line);
119 static void do_something(void);
120 static void check_discard(PlayerP p,strategy *strat,int closed_kong);
121 static Tile decide_discard(PlayerP p, double *score, strategy *newstrat);
122 static void update_tilesleft(CMsgUnion *m);
123 static void maybe_switch_strategy(strategy *strat);
124 static double eval(Tile *tp, strategy *strat, double *stratpoints,int reclevel, int *ninc, int *npr, double *breadth);
125 static double evalhand(PlayerP p, strategy *strat);
126 static int chances_to_win(PlayerP p);
127 /* copy old tile array into new */
128 #define tcopy(new,old) memcpy((void *)new,(void *)old,(MAX_CONCEALED+1)*sizeof(Tile))
129 /* Convenience function */
130 #define send_packet(m) client_send_packet(the_game,(PMsgMsg *)m)
131
usage(char * pname,char * msg)132 static void usage(char *pname,char *msg) {
133 fprintf(stderr,"%s: %s\nUsage: %s [ --id N ] [ --name NAME ] [ --password PASSWORD ] [ --server ADDR ]\n\
134 [ strategy options ]\n",
135 pname,msg,pname);
136 exit(1);
137 }
138
main(int argc,char * argv[])139 int main(int argc, char *argv[]) {
140 char buf[1000];
141 char *l;
142 int i;
143 char *evalh = NULL ; /* just evaluate hand with default strategy
144 and all debugging, and exit */
145 char *address = ":5000";
146 char *name = NULL;
147
148 debugf = stderr;
149
150 /* options. I should use getopt ... */
151 for (i=1;i<argc;i++) {
152 if ( strcmp(argv[i],"--id") == 0 ) {
153 if ( ++i == argc ) usage(argv[0],"missing argument to --id");
154 our_id = atoi(argv[i]);
155 } else if ( strcmp(argv[i],"--server") == 0 ) {
156 if ( ++i == argc ) usage(argv[0],"missing argument to --server");
157 address = argv[i];
158 } else if ( strcmp(argv[i],"--address") == 0 ) {
159 if ( ++i == argc ) usage(argv[0],"missing argument to --address");
160 address = argv[i];
161 } else if ( strcmp(argv[i],"--name") == 0 ) {
162 if ( ++i == argc ) usage(argv[0],"missing argument to --name");
163 name = argv[i];
164 } else if ( strcmp(argv[i],"--password") == 0 ) {
165 if ( ++i == argc ) usage(argv[0],"missing argument to --password");
166 password = argv[i];
167 } else if ( strcmp(argv[i],"--debug") == 0 ) {
168 if ( ++i == argc ) usage(argv[0],"missing argument to --debug");
169 debugeval = atoi(argv[i]);
170 } else if ( strcmp(argv[i],"--eval") == 0 ) {
171 if ( ++i == argc ) usage(argv[0],"missing argument to --eval");
172 evalh = argv[i];
173 debugeval = 99;
174 } else if ( strcmp(argv[i],"--hysteresis") == 0 ) {
175 if ( ++i == argc ) usage(argv[0],"missing argument to --hysteresis");
176 hysteresis = atof(argv[i]);
177 } else if ( strcmp(argv[i],"--chowness") == 0 ) {
178 if ( ++i == argc ) usage(argv[0],"missing argument to --chowness");
179 parsefloatlist(argv[i],chowness);
180 } else if ( strcmp(argv[i],"--hiddenness") == 0 ) {
181 if ( ++i == argc ) usage(argv[0],"missing argument to --hiddenness");
182 parsefloatlist(argv[i],hiddenness);
183 } else if ( strcmp(argv[i],"--majorness") == 0 ) {
184 if ( ++i == argc ) usage(argv[0],"missing argument to --majorness");
185 parsefloatlist(argv[i],majorness);
186 } else if ( strcmp(argv[i],"--suitness") == 0 ) {
187 if ( ++i == argc ) usage(argv[0],"missing argument to --suitness");
188 parsefloatlist(argv[i],suitness);
189 } else if ( strcmp(argv[i],"--magic") == 0 ) {
190 FILE *f;
191 unsigned int j;
192 if ( ++i == argc ) usage(argv[0],"missing argument to --magic");
193 f = fopen(argv[i],"r");
194 if ( f == NULL ) {
195 perror("can't open param file");
196 exit(1);
197 }
198 for ( j = 0 ; j < sizeof(magic)/sizeof(double) ; j++ ) {
199 if ( fscanf(f,"%lg",&magic[j]) != 1 ) {
200 fprintf(stderr,"%s","error in param file\n");
201 exit(1);
202 }
203 }
204 } else if ( strcmp(argv[i],"--magic2") == 0 ) {
205 FILE *f;
206 int j;
207 if ( ++i == argc ) usage(argv[0],"missing argument to --magic2");
208 f = fopen(argv[i],"r");
209 if ( f == NULL ) {
210 perror("can't open param file");
211 exit(1);
212 }
213 for ( j = 0 ; j < 13 ; j++ ) {
214 if ( fscanf(f,"%lg",&magic2[j]) != 1 ) {
215 fprintf(stderr,"%s","error in param file\n");
216 exit(1);
217 }
218 }
219 } else {
220 fprintf(stderr,"unknown option or argument: %s\n",argv[i]);
221 usage(argv[0],"unknown option or argument");
222 }
223 }
224
225 srand(time(NULL));
226
227 if ( evalh ) {
228 Player pp;
229 Game g;
230
231 g.round = EastWind;
232 the_game = &g;
233 pp.wind = EastWind;
234 initialize_player(&pp);
235 set_player_tiles(&pp,evalh);
236 our_player = &pp;
237 evalhand(&pp,&curstrat);
238 exit(0);
239 }
240
241 the_game = client_init(address);
242 if ( ! the_game ) exit(1);
243
244 sprintf(buf,"Robot(%d)",getpid());
245
246 client_connect(the_game,our_id,name ? name : buf);
247
248 while ( 1 ) {
249 l = get_line(the_game->fd);
250 if ( ! l ) {
251 exit(1);
252 }
253 despatch_line(l);
254 }
255 }
256
257 /* despatch_line: this is the mega-switch which deals with
258 the input from the controller */
despatch_line(char * line)259 static int despatch_line(char *line) {
260 CMsgMsg *cm;
261
262 if ( line == NULL ) {
263
264 warn("receive error on controller connexion\n");
265 exit(1);
266 }
267
268 cm = decode_cmsg(line);
269 if ( cm == NULL ) {
270 warn("Protocol error on controller connexion; ignoring\n");
271 return 0;
272 }
273
274 update_tilesleft((CMsgUnion *)cm);
275
276 switch ( cm->type ) {
277 case CMsgError:
278 break; /* damn all we can do */
279 case CMsgGameOver:
280 exit(0);
281 case CMsgReconnect:
282 /* we should never receive this */
283 warn("Received Reconnect command\n");
284 exit(1);
285 case CMsgInfoTiles:
286 /* We ignore these. */
287 break;
288 case CMsgCanMahJong:
289 /* Currently we ignore these, as we don't issue queries */
290 break;
291 case CMsgConnectReply:
292 game_handle_cmsg(the_game,cm);
293 our_id = the_game->players[0]->id;
294 our_player = the_game->players[0];
295 break;
296 case CMsgAuthReqd:
297 {
298 CMsgAuthReqdMsg *carm = (CMsgAuthReqdMsg *)cm;
299 PMsgAuthInfoMsg paim;
300 paim.type = PMsgAuthInfo;
301 if ( strncmp(carm->authtype,"basic",16) != 0 ) {
302 warn("Asked for unknown authorization type %.16s",carm->authtype);
303 exit(1);
304 }
305 strcpy(paim.authtype,"basic");
306 if ( password == NULL ) {
307 warn("Asked for password, don't have it");
308 exit(1);
309 }
310 paim.authdata = password;
311 send_packet(&paim);
312 }
313 break;
314 /* In these cases, our seat might have changed, so we need to calculate it */
315 case CMsgGame:
316 /* In this case, we need to ask for the game options */
317 {
318 PMsgListGameOptionsMsg plgom;
319 plgom.type = PMsgListGameOptions;
320 plgom.include_disabled = 0;
321 send_packet(&plgom);
322 }
323 /* and then ... */
324 case CMsgNewRound:
325 case CMsgNewHand:
326 game_handle_cmsg(the_game,cm);
327 our_seat = game_id_to_seat(the_game,our_id);
328 if ( debugeval ) fprintf(debugf,"New hand\n");
329 /* reset strategy to default */
330 curstrat.chowness = stratparams[chowness].values[0];
331 curstrat.hiddenness = stratparams[hiddenness].values[0];
332 curstrat.majorness = stratparams[majorness].values[0];
333 curstrat.suitness = stratparams[suitness].values[0];
334 curstrat.suit = 0;
335 break;
336 /* in all these cases, game_handle_cmsg does all the work we want */
337 case CMsgPlayer:
338 case CMsgStopPlay:
339 case CMsgClaimDenied:
340 case CMsgPlayerDoesntClaim:
341 case CMsgPlayerClaimsPung:
342 case CMsgPlayerClaimsKong:
343 case CMsgPlayerClaimsChow:
344 case CMsgPlayerClaimsMahJong:
345 case CMsgPlayerShowsTiles:
346 case CMsgDangerousDiscard:
347 case CMsgGameOption:
348 case CMsgChangeManager:
349 case CMsgWall:
350 case CMsgComment:
351 case CMsgStateSaved:
352 case CMsgMessage:
353 game_handle_cmsg(the_game,cm);
354 break;
355 case CMsgHandScore:
356 /* if that was the winner, we should start scoring our hand */
357 if ( ( game_handle_cmsg(the_game,cm)
358 == the_game->players[the_game->player]->id)
359 && the_game->active )
360 do_something();
361 break;
362 /* after a Settlement or Washout message, do something: start next hand */
363 case CMsgWashOut:
364 case CMsgSettlement:
365 game_handle_cmsg(the_game,cm);
366 if ( the_game->active ) do_something();
367 break;
368 /* likewise after a washout */
369 /* after a MahJong message, we should do something: namely
370 start making our scoring sets. */
371 case CMsgPlayerRobsKong:
372 case CMsgPlayerMahJongs:
373 game_handle_cmsg(the_game,cm);
374 if ( the_game->active ) do_something();
375 break;
376 /* in the case of a PlayerDeclaresSpecials message, we need to
377 do something if it is now our turn; but this isn't given
378 by the affected id.
379 However, if the state is Discarding, and no tiles have
380 so far been discarded, we shouldn't do something
381 now, since we are about to be asked to pause.
382 */
383 case CMsgPlayerDeclaresSpecial:
384 game_handle_cmsg(the_game,cm);
385 if ( the_game->player == our_seat && the_game->active
386 && ! ( the_game->state == Discarding && the_game->serial == 0 ))
387 do_something();
388 break;
389 /* in these cases, we need to do something if the message
390 is addressed to us. */
391 case CMsgPlayerDraws:
392 case CMsgPlayerDrawsLoose:
393 case CMsgPlayerPungs:
394 case CMsgPlayerKongs:
395 case CMsgPlayerChows:
396 case CMsgPlayerFormsClosedPung:
397 case CMsgPlayerFormsClosedChow:
398 case CMsgPlayerPairs:
399 case CMsgPlayerFormsClosedPair:
400 case CMsgPlayerSpecialSet:
401 case CMsgPlayerFormsClosedSpecialSet:
402 case CMsgSwapTile:
403 if ( game_handle_cmsg(the_game,cm) == our_id && the_game->active)
404 do_something();
405 break;
406 /* in this case, we need to do something else if it's not our turn! */
407 case CMsgPlayerDiscards:
408 if ( game_handle_cmsg(the_game,cm) != our_id && the_game->active)
409 check_discard(our_player,&curstrat,0);
410 break;
411 /* if this is us, we need to do something, and if it's
412 somebody else, we might be able to rob the kong */
413 case CMsgPlayerDeclaresClosedKong:
414 if ( game_handle_cmsg(the_game,cm) == our_id && the_game->active)
415 do_something();
416 else if ( the_game->active )
417 check_discard(our_player,&curstrat,1); /* actually this checks the kong */
418 break;
419 case CMsgPlayerAddsToPung:
420 if ( game_handle_cmsg(the_game,cm) == our_id && the_game->active)
421 do_something();
422 else if ( the_game->active )
423 check_discard(our_player,&curstrat,0); /* actually this checks the kong */
424 break;
425 /* In this case, it depends on the state of the game */
426 case CMsgStartPlay:
427 /* We need to do something if the id is us, or 0. */
428 { int id;
429 id = game_handle_cmsg(the_game,cm);
430 if ( id == our_id || id == 0 )
431 do_something();
432 }
433 break;
434 /* similarly */
435 case CMsgPlayerReady:
436 game_handle_cmsg(the_game,cm);
437 if ( ! the_game->paused ) do_something();
438 break;
439 case CMsgPause:
440 game_handle_cmsg(the_game,cm);
441 do_something();
442 break;
443 case CMsgPlayerOptionSet:
444 /* we don't recognize any options, so ignore it */
445 break;
446 case CMsgRedirect:
447 warn("Redirect command not currently supported");
448 exit(1);
449 }
450 return 1;
451 }
452
453 /* do something when it's our turn.
454 */
do_something(void)455 static void do_something(void) {
456 int i;
457 MJSpecialHandFlags mjspecflags;
458
459 mjspecflags = 0;
460 if ( game_get_option_value(the_game,GOSevenPairs,NULL).optbool )
461 mjspecflags |= MJSevenPairs;
462
463 /* if the game is paused, and we haven't said we're ready, say so */
464 if ( the_game->paused ) {
465 if ( !the_game->ready[our_seat] ) {
466 PMsgReadyMsg pm;
467 pm.type = PMsgReady;
468 send_packet(&pm);
469 }
470 return;
471 }
472
473 /* if the game state is handcomplete, do nothing */
474 if ( the_game->state == HandComplete ) return;
475
476 /* If the game state is discarded, then it must mean this has
477 been called in response to a StartPlay message after resuming
478 an old hand. So actually we want to check the discard, unless
479 of course we are the discarder, or we have already claimed. */
480 if ( the_game->state == Discarded ) {
481 if ( the_game->player != our_seat
482 && the_game->claims[our_seat] == UnknownClaim )
483 check_discard(our_player,&curstrat,0);
484 return;
485 }
486
487 /* If the game state is Dealing, then we should not do anything.
488 */
489 if ( the_game->state == Dealing ) return;
490
491 /* if we're called in declaring specials or discarding, but it's
492 not our turn, do nothing */
493 if ( (the_game->state == DeclaringSpecials
494 || the_game->state == Discarding)
495 && the_game->player != our_seat ) return;
496
497 /* if we're waiting to draw another tile, do nothing */
498 if ( the_game->needs != FromNone ) return;
499
500 /* if we have a special, declare it. N.B. we'll
501 be called again as a result of this, so only look for first.
502 */
503 for ( i=0; i < our_player->num_concealed
504 && ! is_special(our_player->concealed[i]) ; i++);
505 if ( i < our_player->num_concealed ) {
506 PMsgDeclareSpecialMsg m;
507 m.type = PMsgDeclareSpecial;
508 m.tile = our_player->concealed[i];
509 send_packet(&m);
510 return;
511 }
512 /* OK, no specials */
513 if ( the_game->state == DeclaringSpecials ) {
514 PMsgDeclareSpecialMsg m;
515 m.type = PMsgDeclareSpecial;
516 m.tile = HiddenTile;
517 send_packet(&m);
518 /* and at this point, we should decide our strategy */
519 maybe_switch_strategy(&curstrat);
520 return;
521 }
522 /* if the game is in the mahjonging state, and our hand is not declared,
523 then we should declare a set. */
524 if ( the_game->state == MahJonging ) {
525 TileSet *tsp;
526 PMsgUnion m;
527
528 if ( pflag(our_player,HandDeclared) ) return;
529 /* as courtesy, if we're not the winner, we shouldn't score until
530 the winner has */
531 if ( our_seat != the_game->player
532 && ! pflag(the_game->players[the_game->player],HandDeclared) ) return;
533
534 /* get the list of possible decls */
535 tsp = client_find_sets(our_player,
536 (the_game->player == our_seat
537 && the_game->mjpending)
538 ? the_game->tile : HiddenTile,
539 the_game->player == our_seat,
540 (PlayerP *)0,mjspecflags);
541 if ( !tsp && our_player->num_concealed > 0 ) {
542 m.type = PMsgShowTiles;
543 send_packet(&m);
544 return;
545 }
546 /* just do the first one */
547 switch ( tsp->type ) {
548 case Kong:
549 /* we can't declare a kong now, so declare the pung instead */
550 case Pung:
551 m.type = PMsgPung;
552 m.pung.discard = 0;
553 break;
554 case Chow:
555 m.type = PMsgChow;
556 m.chow.discard = 0;
557 m.chow.cpos = the_game->tile - tsp->tile;
558 break;
559 case Pair:
560 m.type = PMsgPair;
561 break;
562 case ClosedPung:
563 m.type = PMsgFormClosedPung;
564 m.formclosedpung.tile = tsp->tile;
565 break;
566 case ClosedChow:
567 m.type = PMsgFormClosedChow;
568 m.formclosedchow.tile = tsp->tile;
569 break;
570 case ClosedPair:
571 m.type = PMsgFormClosedPair;
572 m.formclosedpair.tile = tsp->tile;
573 break;
574 case Empty: /* can't happen, just to suppress warning */
575 case ClosedKong: /* ditto */
576 ;
577 }
578 send_packet(&m);
579 return;
580 }
581
582 /* if we can declare MahJong, do it */
583 if ( player_can_mah_jong(our_player,HiddenTile,mjspecflags) ) {
584 PMsgMahJongMsg m;
585 m.type = PMsgMahJong;
586 m.discard = 0;
587 send_packet(&m);
588 return;
589 } else if ( the_game->whence != FromDiscard ) {
590 /* check for concealed kongs and melded kongs. Just declare them. */
591 int i;
592 double val;
593 Player pc;
594 val = evalhand(our_player,&curstrat);
595 /* a side effect of the above call is that our concealed tiles
596 are sorted (in reverse order), so we can avoid duplicating effort */
597 for (i=0;i<our_player->num_concealed;i++) {
598 /* don't look at same tile twice */
599 if ( i && our_player->concealed[i] == our_player->concealed[i-1] ) continue;
600 if ( player_can_declare_closed_kong(our_player,our_player->concealed[i]) ) {
601 PMsgDeclareClosedKongMsg m;
602 copy_player(&pc,our_player);
603 player_declares_closed_kong(&pc,our_player->concealed[i]);
604 if ( evalhand(&pc,&curstrat) > val ) {
605 m.type = PMsgDeclareClosedKong;
606 m.tile = our_player->concealed[i];
607 send_packet(&m);
608 return;
609 }
610 }
611 }
612 /* Now check for pungs we can meld to */
613 for (i=0;i<MAX_TILESETS;i++) {
614 if ( our_player->tilesets[i].type == Pung
615 && player_can_add_to_pung(our_player,our_player->tilesets[i].tile) ) {
616 PMsgAddToPungMsg m;
617 copy_player(&pc,our_player);
618 player_adds_to_pung(&pc,our_player->tilesets[i].tile);
619 if ( evalhand(&pc,&curstrat) > val ) {
620 m.type = PMsgAddToPung;
621 m.tile = our_player->tilesets[i].tile;
622 send_packet(&m);
623 return;
624 }
625 }
626 }
627 }
628 /* if we get here, we have to discard */
629 {
630 PMsgDiscardMsg m;
631 Tile t;
632
633 /* strategy switching only after drawing tile from wall */
634 if ( the_game->whence != FromDiscard || !strategy_chosen) {
635 maybe_switch_strategy(&curstrat);
636 }
637
638 t = decide_discard(our_player,NULL,&curstrat);
639
640 m.type = PMsgDiscard;
641 m.tile = t;
642 m.calling = 0; /* we don't bother looking for original call */
643 send_packet(&m);
644 return;
645 }
646 }
647
648 /* Check if we want the discard, and claim it.
649 Arg is strategy.
650 Also called to check whether a kong can be robbed -
651 the last arg says if being called on a closed kong */
check_discard(PlayerP p,strategy * strat,int closedkong)652 static void check_discard(PlayerP p, strategy *strat,int closedkong) {
653 PMsgUnion m;
654 double bestval,val;
655 int canmj;
656 char buf[100];
657 MJSpecialHandFlags mjspecflags;
658
659 mjspecflags = 0;
660 if ( game_get_option_value(the_game,GOSevenPairs,NULL).optbool )
661 mjspecflags |= MJSevenPairs;
662
663 if ( the_game->state == Discarding
664 || the_game->state == DeclaringSpecials ) {
665 /* this means we're being called to check whether a kong
666 can be robbed. Since robbing a kong gets us an extra double,
667 this is probably always worth doing, unless we're trying for
668 some limit hand */
669 if ( player_can_mah_jong(p,the_game->tile,mjspecflags)
670 && (!closedkong || player_can_thirteen_wonders(p,the_game->tile)) ) {
671 m.type = PMsgMahJong;
672 m.mahjong.discard = the_game->serial;
673 } else {
674 m.type = PMsgNoClaim;
675 m.noclaim.discard = the_game->serial;
676 }
677 send_packet(&m);
678 return;
679 }
680
681 if ( debugeval ) {
682 player_print_tiles(buf,p,0);
683 fprintf(debugf,"Hand: %s ; discard %s\n",buf,tile_code(the_game->tile));
684 }
685 bestval = evalhand(p,strat);
686 if ( debugeval ) {
687 fprintf(debugf,"Hand value before claim %.3f\n",bestval);
688 }
689
690 canmj = player_can_mah_jong(p,the_game->tile,mjspecflags);
691 m.type = PMsgNoClaim;
692 m.noclaim.discard = the_game->serial;
693 if ( player_can_kong(p,the_game->tile) ) {
694 Player pc;
695
696 copy_player(&pc,p);
697 player_kongs(&pc,the_game->tile);
698 val = evalhand(&pc,strat);
699 /* we won't discard a tile here */
700 if ( debugeval ) {
701 fprintf(debugf,"Hand after kong %.3f\n",val);
702 }
703 if ( val > bestval ) {
704 m.type = PMsgKong;
705 m.kong.discard = the_game->serial;
706 bestval = val;
707 }
708 else
709 if ( debugeval ) {
710 fprintf(debugf,"Chose not to kong\n");
711 }
712 }
713 if ( player_can_pung(p,the_game->tile) ) {
714 Player pc;
715
716 copy_player(&pc,p);
717 player_pungs(&pc,the_game->tile);
718 decide_discard(&pc,&val,&curstrat);
719 if ( debugeval ) {
720 fprintf(debugf,"Hand after pung %.3f\n",val);
721 }
722 if ( val > bestval ) {
723 m.type = PMsgPung;
724 m.pung.discard = the_game->serial;
725 bestval = val;
726 }
727 else
728 if ( debugeval ) {
729 fprintf(debugf,"Chose not to pung\n");
730 }
731 }
732 if ( (canmj || our_seat == (the_game->player+1)%NUM_SEATS)
733 && is_suit(the_game->tile) ) {
734 ChowPosition cpos = (unsigned) -1;
735 Player pc;
736 int chowposs = 0;
737 Tile t = the_game->tile;
738 copy_player(&pc,p);
739 if ( player_chows(&pc,t,Lower) ) {
740 decide_discard(&pc,&val,&curstrat);
741 if ( debugeval ) {
742 fprintf(debugf,"Hand after lower chow: %.3f\n",val);
743 }
744 chowposs = 1;
745 if ( val > bestval ) {
746 bestval = val;
747 cpos = Lower;
748 }
749 copy_player(&pc,p);
750 }
751 if ( player_chows(&pc,t,Middle) ) {
752 decide_discard(&pc,&val,&curstrat);
753 if ( debugeval ) {
754 fprintf(debugf,"Hand after middle chow: %.3f\n",val);
755 }
756 chowposs = 1;
757 if ( val > bestval ) {
758 bestval = val;
759 cpos = Middle;
760 }
761 copy_player(&pc,p);
762 }
763 if ( player_chows(&pc,t,Upper) ) {
764 chowposs = 1;
765 decide_discard(&pc,&val,&curstrat);
766 if ( debugeval ) {
767 fprintf(debugf,"Hand after upper chow: %.3f\n",val);
768 }
769 if ( val > bestval ) {
770 bestval = val;
771 cpos = Upper;
772 }
773 copy_player(&pc,p);
774 }
775 if ( cpos != (unsigned)-1 ) {
776 m.type = PMsgChow;
777 m.chow.discard = the_game->serial;
778 m.chow.cpos = cpos;
779 }
780 else
781 if ( debugeval ) {
782 if ( chowposs ) fprintf(debugf,"chose not to chow\n");
783 }
784 }
785 /* mah jong */
786 if ( canmj ) {
787 m.type = PMsgMahJong;
788 m.mahjong.discard = the_game->serial;
789
790 #if 1
791 /* if we're following a concealed strategy, and we still have
792 four chances (ex?cluding this one) of going out, then
793 don't claim */
794 /* instead of four chances, make it depend on number of tiles left */
795 /* test: if hidden >1, then never claim */
796 if ( (strat->hiddenness > 1.0) || (strat->hiddenness*chances_to_win(p)
797 * (the_game->wall.live_end-the_game->wall.live_used)
798 / (the_game->wall.dead_end-the_game->wall.live_used)
799 >= 1.5) ) {
800 m.type = PMsgNoClaim;
801 m.noclaim.discard = the_game->serial;
802 }
803 #endif
804 if ( m.type != PMsgNoClaim ) {
805 if ( debugeval && strat->hiddenness > 0.0 ) fprintf(debugf,"claiming mahjong on hidden strategy\n");
806 m.type = PMsgMahJong;
807 m.mahjong.discard = the_game->serial;
808 } else {
809 if ( debugeval ) {
810 fprintf(debugf,"CHOSE NOT TO MAHJONG\n");
811 }
812 }
813 }
814 if ( debugeval ) {
815 fprintf(debugf,"Result: %s",encode_pmsg((PMsgMsg *)&m));
816 }
817 send_packet(&m);
818 return;
819 }
820
821
822 /* Here is a data structure to track the number of (assumed)
823 available tiles elsewhere. The update fn should be called on every CMsg. */
824
update_tilesleft(CMsgUnion * m)825 static void update_tilesleft(CMsgUnion *m) {
826 int i;
827 /* note that we don't need to check whether the tile is blank,
828 since the HiddenTile entry of tilesleft has no meaning.
829 */
830 switch ( m->type ) {
831 case CMsgNewHand:
832 for (i=0; i < MaxTile; i++) {
833 tilesleft[i] = 4;
834 rightdiscs[i] = 0;
835 }
836 return;
837 case CMsgPlayerDeclaresSpecial:
838 return;
839 case CMsgPlayerDraws:
840 tilesleft[m->playerdraws.tile]--;
841 return;
842 case CMsgPlayerDrawsLoose:
843 tilesleft[m->playerdrawsloose.tile]--;
844 return;
845 case CMsgPlayerDiscards:
846 /* if this is us, we've already counted it */
847 if ( m->playerdiscards.id != our_id )
848 tilesleft[m->playerdiscards.tile]--;
849 if ( game_id_to_seat(the_game,m->playerdiscards.id)
850 == (our_seat+1)%4 )
851 rightdiscs[m->playerdiscards.tile] = m->playerdiscards.discard;
852 return;
853 case CMsgPlayerPungs:
854 /* if somebody else pungs, then two more tiles become dead
855 (the discard was already noted).
856 If we pung, nothing new is known */
857 if ( m->playerpungs.id != our_id )
858 tilesleft[m->playerpungs.tile] -= 2;
859 return;
860 case CMsgPlayerKongs:
861 /* if somebody else kongs, then three more tiles become dead
862 (the discard was already noted). */
863 if ( m->playerkongs.id != our_id )
864 tilesleft[m->playerkongs.tile] -= 3;
865 return;
866 case CMsgPlayerDeclaresClosedKong:
867 if ( m->playerdeclaresclosedkong.id != our_id )
868 tilesleft[m->playerdeclaresclosedkong.tile] -= 4;
869 return;
870 case CMsgPlayerAddsToPung:
871 if ( m->playeraddstopung.id != our_id )
872 tilesleft[m->playeraddstopung.tile]--;
873 return;
874 case CMsgPlayerChows:
875 if ( m->playerchows.id != our_id ) {
876 Tile t = m->playerchows.tile;
877 ChowPosition c = m->playerchows.cpos;
878 tilesleft[(c==Lower)?t+1:(c==Middle)?t-1:t-2]--;
879 tilesleft[(c==Lower)?t+2:(c==Middle)?t+1:t-1]--;
880 }
881 return;
882 case CMsgSwapTile:
883 tilesleft[m->swaptile.oldtile]++;
884 tilesleft[m->swaptile.newtile]--;
885 return;
886 default:
887 return;
888 }
889 }
890
891
892 /* This sorts the tiles (as below) into reverse order.
893 Why reverse order? So that HiddenTile terminates the hand.
894 */
tsort(Tile * tp)895 static void tsort(Tile *tp) {
896 int i,m;
897 Tile t;
898 /* bubblesort */
899 m = 1;
900 while ( m ) {
901 m = 0;
902 for (i=0; i<MAX_CONCEALED+1-1;i++)
903 if ( tp[i] < tp[i+1] ) {
904 t = tp[i];
905 tp[i] = tp[i+1];
906 tp[i+1] = t;
907 m = 1;
908 }
909 }
910 }
911
912
913 /* This function attempts to evaluate a partial hand.
914 The input is a (pointer to an) array of tiles
915 (length MAX_CONCEALED+1; some will be blank).
916 The value is a floating point number, calculated thus:
917 for the first tile in the set, form all possible sets
918 and partial sets (incl. singletons) with that tile;
919 then recurse on the remainder. The value is then the
920 max over the partial sets of the inherent value of the
921 set (explained below) and the recursive value of the remaining hand.
922 This would have the slightly awkward consequence that pairs get counted
923 twice in pungs, since given 222... we see 22 2 and also 2 22.
924 Hence, if we form a pung, we don't form a pair also.
925 (Nothing special is done about kongs; it should be.)
926 **** The input is sorted in place. ****
927 */
928
929 /* debugging format:
930 SSTT:mmmm+ (recursive)
931 tttt(nnnn)
932 where SS is a set code (Pu,Pr,Ch,In,Ou,Si),
933 TT is the tile code, mmmm is the set value,
934 tttt is the total, nnnn is the residual
935 Hence the recursive call should indent all lines except the first
936 by reclevel*11 characters, and the base case call should emit
937 a newline.
938 */
939 /* other parameters:
940 strat: current strategy
941 reclevel: recursion level. Should start at 0.
942 ninc: number of incomplete sets (including pairs) in
943 the chain so far.
944 npr: number of pairs in the chain so far.
945 breadth is something to try to count the number of ways a hand
946 can be evaluated. Every time we hit bottom, we'll increment
947 breadth by 1/reclevel.
948 */
949
eval(Tile * tp,strategy * strat,double * stratpoints,int reclevel,int * ninc,int * npr,double * breadth)950 static double eval(Tile *tp,strategy *strat,double *stratpoints,
951 int reclevel,
952 int *ninc,
953 int *npr,
954 double *breadth) {
955 Tile copy[MAX_CONCEALED+1],copy2[MAX_CONCEALED+1];
956 int i;
957 double mval = 0, pval = 0, val = 0,
958 pvalr = 0, mvalr = 0, valr = 0;
959 int ppr, mpr; int spr;
960 char prefix[200];
961 static char totbuf[200];
962 char tb[20];
963 int notfirst=0;
964
965 tsort(tp);
966
967 if ( reclevel == -1 ) {
968 /*evaluate each suit in turn*/
969 mval = 0.0;
970 tcopy(copy,tp);
971 while ( suit_of(copy[0]) ) {
972 tcopy(copy2,copy);
973 for ( i = 0; suit_of(copy[i]) == suit_of(copy[0]); i++ );
974 for ( ; i < MAX_CONCEALED+1 ; i++ ) copy[i] = HiddenTile;
975 val = eval(copy,strat,stratpoints,0,ninc,npr,breadth);
976 mval += val ;
977 if ( debugeval ) fprintf(debugf,"Evaling suit %d returned v=%.2f, b=%.2f\n",
978 suit_of(copy[0]),val,*breadth);
979 tcopy(copy,copy2);
980 for ( i= 0; suit_of(copy[i]) == suit_of(copy2[0]) ; i++ )
981 copy[i] = HiddenTile;
982 tsort(copy);
983 }
984 if ( debugeval ) fprintf(debugf,"Level 0 returned %.2f\n",mval);
985 return mval;
986 }
987
988 if ( debugeval ) {
989 /* The total buffer. A call at recursion level n writes the total into bytes
990 11(n-1) to 11n-1 of the buffer (right justified in the first 4 chars;
991 spaces in the rest.
992 A base case terminates the total buffer with null.
993 When a second set is printed, or when we terminate at level zero,
994 then the total buffer is printed.
995 */
996
997 for (i=0;i<reclevel*11;i++) prefix[i]=' ';
998 prefix[i]=0;
999 }
1000
1001 if ( tp[0] == HiddenTile ) {
1002 if ( debugeval > 1 ) {
1003 fprintf(debugf,"\n");
1004 if (reclevel) sprintf(&totbuf[11*(reclevel-1)+4],"\n");
1005 }
1006 /* if there is one "incomplete" set and one pair, we have a complete
1007 hand */
1008 #if 0
1009 if ( ninc == 1 && npr == 1 ) val = stratpoints[mjbonus];
1010 else val = 0.0;
1011 #else
1012 val = 0.0;
1013 #endif
1014 if ( debugeval > 1 ) {
1015 sprintf(tb,"%4.1f",val);
1016 strncpy(&totbuf[11*reclevel],tb,4);
1017 }
1018 return val;
1019 }
1020
1021 /* does it pung? */
1022 ppr = 0;
1023 if ( tp[1] == tp[0] ) {
1024 if ( tp[2] == tp[0] ) {
1025 val = 0.0;
1026 /* add the value of this set.
1027 */
1028 val += stratpoints[pungbase];
1029 /* if we're trying for a no score hand, scoring pairs are bad */
1030 if ( is_doubler(tp[0]) ) val += (-strat->chowness) * magic2[0];
1031 if ( is_major(tp[0]) ) {
1032 val += magic2[1];
1033 } else {
1034 val -= magic2[2]*strat->majorness;
1035 }
1036 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit )
1037 val *= stratpoints[suitfactor];
1038 if ( debugeval > 1 ) {
1039 fprintf(debugf,"%sPu%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1040 }
1041 /* remove the tiles and recurse */
1042 tcopy(copy,tp);
1043 copy[0] = copy[1] = copy[2] = HiddenTile;
1044 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&ppr,breadth);
1045 if ( debugeval > 1 ) {
1046 sprintf(tb,"%4.1f",val);
1047 strncpy(&totbuf[11*reclevel],tb,4);
1048 }
1049 val += valr;
1050 if ( val > pval ) { pval = val; pvalr = valr ; }
1051 } else {
1052 val = 0.0;
1053 /* A pair is worth something for itself, plus something
1054 for its chances of becoming a pung.
1055 A doubler pair is worth an extra 2 points.
1056 A major pair is worth an extra point.
1057 Beware the risk of arranging effect that a chow and
1058 a pair is worth much more than a pung and an inner sequence,
1059 so that we'd break a pung rather than a chow.
1060 */
1061 ppr++;
1062 val += stratpoints[partpung]*stratpoints[pungbase] * tilesleft[tp[0]];
1063 /* doublers are bad for noscore */
1064 if ( is_doubler(tp[0]) ) val += 2 - (strat->chowness)*4.0;
1065 if ( is_major(tp[0]) ) {
1066 val += magic2[3];
1067 } else {
1068 val -= magic2[4]*strat->majorness;
1069 }
1070 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit )
1071 val *= stratpoints[suitfactor];
1072 if ( debugeval > 1 ) {
1073 fprintf(debugf,"%sPr%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1074 }
1075 /* remove the tiles and recurse */
1076 tcopy(copy,tp);
1077 copy[0] = copy[1] = HiddenTile;
1078 (*ninc)++;
1079 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&ppr,breadth);
1080 val += valr;
1081 if ( debugeval > 1 ) {
1082 sprintf(tb,"%4.1f",val);
1083 strncpy(&totbuf[11*reclevel],tb,4);
1084 }
1085 if ( val > pval ) { pval = val; pvalr = valr; }
1086 }
1087 }
1088 /* OK, now deal with chows. */
1089 mpr = 0;
1090 if ( is_suit(tp[0]) ) {
1091 Tile t1,t2; int i1,i2; /* other tiles, and their indices */
1092 /* NB tiles are in reverse order!!!!! */
1093 t1 = ((value_of(tp[0]) > 1) ? tp[0]-1 : 0);
1094 t2 = ((value_of(tp[0]) > 2) ? tp[0]-2 : 0);
1095 for (i1=0;t1 && tp[i1] && tp[i1]!=t1;i1++);
1096 if ( ! tp[i1] ) i1=0;
1097 for (i2=0;t2 && tp[i2] && tp[i2]!=t2;i2++);
1098 if ( ! tp[i2] ) i2=0;
1099
1100 /* if we have a chow */
1101 if ( i1 && i2 ) {
1102 val = 0.0;
1103 /* A chow is deemed to be worth 12 points also.
1104 (Quick and dirty ... hike pungbonus to avoid chows.)
1105 */
1106 val += stratpoints[chowbase];
1107 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit )
1108 val *= stratpoints[suitfactor];
1109 if ( debugeval > 1 ) {
1110 if ( notfirst ) fprintf(debugf,"%s%s",prefix,&totbuf[11*reclevel]);
1111 fprintf(debugf,"%sCh%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1112 }
1113 /* remove the tiles and recurse */
1114 tcopy(copy,tp);
1115 copy[0] = copy[i1] = copy[i2] = HiddenTile;
1116 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&mpr,breadth);
1117 val += valr;
1118 if ( debugeval > 1 ) {
1119 sprintf(tb,"%4.1f",val);
1120 strncpy(&totbuf[11*reclevel],tb,4);
1121 }
1122 if ( val > mval ) { mval = val; mvalr = valr; }
1123 }
1124 /* If we have an inner sequence... note that it's intentional
1125 that we do this as well, since maybe if we split the chow,
1126 we'll find a pung. But we'
1127 */
1128 if ( i1 ) {
1129 val = 0.0;
1130 /* An inner sequence is worth the number of chances of completion,
1131 allowing for the fact that on average, the tiles in right
1132 and opposite and half the tiles in the wall will not be available
1133 for completing a chow.
1134 NOTE: this needs to change when we're nearly at MahJong.
1135 */
1136 val += stratpoints[seqbase];
1137 if ( value_of(tp[0]) < 9 ) {
1138 val += stratpoints[partchow]*stratpoints[chowbase] * tilesleft[tp[0]+1];
1139 }
1140 if ( value_of(t1) > 1 ) {
1141 val += stratpoints[partchow]*stratpoints[chowbase] * tilesleft[t1-1];
1142 }
1143 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit )
1144 val *= stratpoints[suitfactor];
1145 if ( debugeval > 1 ) {
1146 if ( notfirst ) fprintf(debugf,"%s%s",prefix,&totbuf[11*reclevel]);
1147 fprintf(debugf,"%sIn%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1148 }
1149 /* remove the tiles and recurse */
1150 tcopy(copy,tp);
1151 copy[0] = copy[i1] = HiddenTile;
1152 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&mpr,breadth);
1153 val += valr;
1154 if ( debugeval > 1 ) {
1155 sprintf(tb,"%4.1f",val);
1156 strncpy(&totbuf[11*reclevel],tb,4);
1157 }
1158 if ( val > mval ) { mval = val; mvalr = valr; }
1159 }
1160 /* If we have a split sequence ... Here we don't do this if there's
1161 been a chow. Hmm, why not? I thought I had a reason. Fill it in
1162 sometime. */
1163 else if ( i2 ) {
1164 val = 0.0;
1165 /* Likewise */
1166 val += stratpoints[seqbase];
1167 val += stratpoints[partchow]*stratpoints[chowbase] * tilesleft[t1];
1168 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit )
1169 val *= stratpoints[suitfactor];
1170 if ( debugeval > 1 ) {
1171 if ( notfirst ) fprintf(debugf,"%s%s",prefix,&totbuf[11*reclevel]);
1172 fprintf(debugf,"%sOu%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1173 }
1174 /* remove the tiles and recurse */
1175 tcopy(copy,tp);
1176 copy[0] = copy[i2] = HiddenTile;
1177 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&mpr,breadth);
1178 val += valr;
1179 if ( debugeval > 1 ) {
1180 sprintf(tb,"%4.1f",val);
1181 strncpy(&totbuf[11*reclevel],tb,4);
1182 }
1183 if ( val > mval ) { mval = val; mvalr = valr; }
1184 }
1185 }
1186 /* Finally, the score for a singleton.
1187 */
1188 /* let's also add .25 times the number of neighbouring tiles */
1189 spr = 0;
1190 val = 0.0;
1191 val += stratpoints[sglbase]
1192 * (tilesleft[tp[0]]
1193 + ( is_suit(tp[0])
1194 ? (magic2[5]*strat->chowness*(tilesleft[tp[0]-1]+ tilesleft[tp[0]+1])) : 0))
1195 * (magic2[6]-strat->chowness);
1196 if ( is_doubler(tp[0]) ) {
1197 if (tilesleft[tp[0]] == 0) val -= magic2[7]; /* completely useless */
1198 else {
1199 if ( (magic2[8]-strat->chowness) > 0 )
1200 val += (tilesleft[tp[0]])*stratpoints[sglbase]*(magic2[9]-strat->chowness);
1201 else
1202 val += (3-tilesleft[tp[0]])*stratpoints[sglbase]*(magic2[10]-strat->chowness);
1203 }
1204 }
1205 if ( is_suit(tp[0]) && suit_of(tp[0]) != strat->suit ) {
1206 /* val could be negative, in which case we don't want to shrink it.
1207 So just substract a constant */
1208 val -= magic2[11]*strat->suitness*stratpoints[sglbase];
1209 }
1210 if ( ! is_major(tp[0]) ) {
1211 val -= magic2[12]*strat->majorness*stratpoints[sglbase];
1212 }
1213 if ( debugeval > 1 ) {
1214 if ( notfirst ) fprintf(debugf,"%s%s",prefix,&totbuf[11*reclevel]);
1215 fprintf(debugf,"%sSi%s:%4.1f+ ",notfirst++?prefix:"",tile_code(tp[0]),val);
1216 }
1217 tcopy(copy,tp);
1218 copy[0] = HiddenTile;
1219 valr = eval(copy,strat,stratpoints,reclevel+1,ninc,&spr,breadth);
1220 val += valr;
1221 if ( debugeval > 1 ) {
1222 sprintf(tb,"%4.1f",val);
1223 strncpy(&totbuf[11*reclevel],tb,4);
1224 }
1225 /* at this point, we have pair/seq/single based scores. */
1226
1227 if ( mval > pval ) {
1228 if ( val > mval ) {
1229 if ( spr ) (*npr)++;
1230 mval = val;
1231 } else {
1232 if ( mpr ) (*npr)++;
1233 }
1234 } else {
1235 if ( val > pval ) {
1236 mval = val;
1237 if ( spr ) (*npr)++;
1238 } else {
1239 mval = pval;
1240 if ( ppr ) (*npr)++;
1241 }
1242 }
1243 if ( debugeval > 1 ) {
1244 if ( reclevel ) {
1245 sprintf(tb,"(%4.1f) ",mval);
1246 strncpy(&totbuf[11*(reclevel-1)+4],tb,7);
1247 }
1248 else fputs(totbuf,debugf);
1249 }
1250 return mval;
1251 }
1252
1253
1254 /* This function uses the above to evaluate a player's hand, including
1255 the completed sets. */
evalhand(PlayerP p,strategy * strat)1256 static double evalhand(PlayerP p,strategy *strat) {
1257 Tile tcopy[MAX_CONCEALED+1];
1258 double val,breadth;
1259 int i, npr, ninc;
1260 double stratpoints[weight+1];
1261
1262 if ( debugeval ) {
1263 char buf[100];
1264 fprintf(debugf,"eval with strat params c=%.1f, h=%.1f, m=%.1f, s=%.1f (%d)\n",
1265 strat->chowness,strat->hiddenness,strat->majorness,
1266 strat->suitness,strat->suit);
1267 player_print_tiles(buf,p,0);
1268 fprintf(debugf,"Hand: %s\n",buf);
1269 }
1270 /* calculate old style strategy values from new ones */
1271 stratpoints[pungbase] =
1272 magic[0] - ( strat->chowness > 0 ? strat->chowness * magic[0]
1273 : strat->chowness * magic[1] );
1274 stratpoints[pungbase] *= (magic[2] + magic[3] * strat->hiddenness);
1275 stratpoints[pairbase] = magic[4]*(magic[5] + (magic[6]*fabs(strat->chowness)));
1276 stratpoints[chowbase] =
1277 magic[0] + ( strat->chowness > 0 ? strat->chowness * magic[7]
1278 : strat->chowness * magic[0] );
1279 stratpoints[chowbase] *= (magic[8] + magic[9] * strat->hiddenness)
1280 * (1.0 - strat->majorness);
1281 stratpoints[seqbase] = magic[10]*(magic[11] + ( strat->chowness < 0 ? strat->chowness : 0))
1282 * (1.0 - strat->majorness);
1283 stratpoints[sglbase] = magic[12];
1284 stratpoints[partpung] = magic[13]*(magic[14])*(1-magic[15]*strat->hiddenness);
1285 stratpoints[partchow] = magic[16]*(1-magic[17]*strat->hiddenness) * (1.0 - strat->majorness);
1286 stratpoints[exposedpungpenalty] = magic[18] + strat->hiddenness * stratpoints[pungbase]
1287 + ( (strat->chowness > 0) ? magic[19] * strat->chowness : 0.0 );
1288 stratpoints[exposedchowpenalty] = magic[20] + strat->hiddenness * stratpoints[chowbase]
1289 + ( (strat->chowness < 0) ? -1*magic[21] * strat->chowness : 0.0 )
1290 + magic[21] * strat->majorness;
1291 stratpoints[mjbonus] = magic[22];
1292 stratpoints[kongbonus] = magic[23];
1293 stratpoints[suitfactor] =
1294 ((strat->suitness >= 1.0) ? 0.01 : (1.0 - magic[24]*strat->suitness));
1295 stratpoints[weight] = magic[25] +
1296 (strat->suitness >= 1.0 ? magic[26]*(strat->suitness-1.0) : magic[26]*strat->suitness)
1297 + magic[27]*strat->majorness;
1298
1299 for (i=0; i<p->num_concealed; i++)
1300 tcopy[i] = p->concealed[i];
1301 for ( ; i < MAX_CONCEALED+1; i++)
1302 tcopy[i] = HiddenTile;
1303
1304 val = 0.0;
1305
1306 ninc = npr = 0; /* number of "incomplete"/pairs in hand */
1307
1308 /* note that if we see any closed pungs in here, they are
1309 actually hacks representing hypothetical open sets */
1310
1311 for (i=0; i<5; i++) {
1312 double sval = 0.0;
1313 switch (p->tilesets[i].type) {
1314 case Chow:
1315 case ClosedChow:
1316 sval -= stratpoints[exposedchowpenalty];
1317 sval += stratpoints[chowbase];
1318 break;
1319 case ClosedKong:
1320 sval += stratpoints[exposedpungpenalty]; /* cancel the penalty later */
1321 case Kong:
1322 sval += stratpoints[kongbonus];
1323 case Pung:
1324 case ClosedPung:
1325 sval -= stratpoints[exposedpungpenalty];
1326 sval += stratpoints[pungbase];
1327 /* these shadow evalhand above */
1328 if ( is_doubler(p->tilesets[i].tile) ) sval += (-strat->chowness) * 6.0;
1329 if ( is_major(p->tilesets[i].tile) ) {
1330 sval += 2.0 ; /* small bonus for luck */
1331 } else {
1332 sval -= 25.0*strat->majorness;
1333 }
1334 break;
1335 case Pair:
1336 case ClosedPair:
1337 ninc = npr = 1; /* for correct evaluation of pairs */
1338 sval += stratpoints[pairbase];
1339 break;
1340 default:
1341 ;
1342 }
1343 if ( p->tilesets[i].type != Empty
1344 && is_suit(p->tilesets[i].tile)
1345 && suit_of(p->tilesets[i].tile) != strat->suit )
1346 sval -= 10.0*strat->suitness;
1347 if ( debugeval > 1 ) {
1348 fprintf(debugf,"Set %s: %.1f\n",player_print_TileSetType(p->tilesets[i].type),
1349 sval);
1350 }
1351 val += sval;
1352 }
1353 breadth = 0.0;
1354 val += eval(tcopy,strat,stratpoints,-1,&ninc,&npr,&breadth);
1355 /* add the pairbase if we have at least one pair */
1356 if ( npr > 0 ) {
1357 if ( debugeval > 1 ) fprintf(debugf,"+pairbase %.1f\n",stratpoints[pairbase]);
1358 val += stratpoints[pairbase];
1359 }
1360 val *= stratpoints[weight];
1361 #if 0 /* randomization seems to be a lose */
1362 /* now add a small random offset */
1363 r = (2.0*rand())/RAND_MAX - 1.0;
1364 r = 4*r*r;
1365 return val+r;
1366 #else
1367 if ( debugeval ) fprintf(debugf,"Value %.2f\n",val);
1368 return val;
1369 #endif
1370 }
1371
1372 /* compute the number of ways a calling hand can go out */
chances_to_win(PlayerP p)1373 static int chances_to_win(PlayerP p) {
1374 Tile t;
1375 int n;
1376 MJSpecialHandFlags mjspecflags;
1377
1378 mjspecflags = 0;
1379 if ( game_get_option_value(the_game,GOSevenPairs,NULL).optbool )
1380 mjspecflags |= MJSevenPairs;
1381
1382 t = HiddenTile;
1383 n = 0;
1384 while ( (t = tile_iterate(t,0)) != HiddenTile ) {
1385 if ( tilesleft[t] && player_can_mah_jong(p,t,mjspecflags) )
1386 n += tilesleft[t];
1387 }
1388 return n;
1389 }
1390
1391 /* compute tile to discard. Return value is tile.
1392 Second arg returns score of resulting hand.
1393 Third arg returns the new strategy.
1394 */
decide_discard(PlayerP p,double * score,strategy * strat)1395 static Tile decide_discard(PlayerP p, double *score, strategy *strat) {
1396 /* how do we choose a tile to discard?
1397 remove the tile, and evaluate the remaining hand.
1398 Discard the tile giving the greatest residual value.
1399 */
1400 int i,best;
1401 double values[MAX_CONCEALED+1]; Tile tilesa[MAX_CONCEALED+1],tiles[MAX_CONCEALED+1] UNUSED;
1402 char buf[80];
1403 const Tile *t = p->concealed;
1404
1405
1406 for (i=0;i<MAX_CONCEALED+1;i++) values[i]=0;
1407 for (i=0;i<p->num_concealed;i++) tilesa[i] = t[i];
1408 for (;i<MAX_CONCEALED+1;i++) tilesa[i] = HiddenTile;
1409
1410 if ( debugeval ) {
1411 player_print_tiles(buf,p,0);
1412 fprintf(debugf,"Hand: %s\n",buf);
1413 }
1414 best = 0;
1415 tsort(tilesa); /* no point in looking at same tile twice */
1416 for (i=0;i<p->num_concealed;i++) {
1417 Player cp;
1418 if ( i && tilesa[i] == tilesa[i-1] ) continue;
1419 if ( debugeval ) {
1420 fprintf(debugf,"Trying %s: \n",tile_code(tilesa[i]));
1421 }
1422 copy_player(&cp,p);
1423 player_discards(&cp,tilesa[i]);
1424 values[i] = evalhand(&cp,strat);
1425 /* add a bonus for tiles recently discarded by the player
1426 to the right */
1427 if ( rightdiscs[tilesa[i]] )
1428 values[i] += 0.5; /* /(the_game->serial-rightdiscs[tilesa[i]]) */;
1429 if ( is_suit(tilesa[i])
1430 && ((value_of(tilesa[i]) < 7
1431 && rightdiscs[tilesa[i]+3])
1432 || (value_of(tilesa[i]) > 3
1433 && rightdiscs[tilesa[i]-3])) )
1434 values[i] += 0.4; /* the 1-4-7 argument */
1435 /* Best tile to discard leaves highest residual score */
1436 if ( values[i] > values[best] ) best = i;
1437 if ( debugeval ) {
1438 fprintf(debugf,"Tile %s has value %.1f\n",tile_code(tilesa[i]),values[i]);
1439 }
1440 }
1441 if ( debugeval ) {
1442 fprintf(debugf,"Discarding %s\n",tile_code(tilesa[best]));
1443 }
1444 if ( score ) *score = values[best];
1445 return tilesa[best];
1446 }
1447
1448 /* Maybe switch strategy: takes a strat pointer as argument,
1449 and updates it in place. */
maybe_switch_strategy(strategy * strat)1450 static void maybe_switch_strategy(strategy *strat) {
1451 strategy tmpstrat;
1452 int i,j;
1453 double val,oval, bestval;
1454 strategy beststrat;
1455 int dofast;
1456 PlayerP p = our_player;
1457
1458 oval = evalhand(p,strat);
1459
1460 bestval = -1000.0;
1461 tmpstrat.chowness = stratparams[chowness].values[0];
1462 tmpstrat.hiddenness = stratparams[hiddenness].values[0];
1463 tmpstrat.majorness = stratparams[majorness].values[0];
1464 /* it makes no sense to try to evaluate pung/chowness with
1465 a non-zero suitness, cos we don't know which suit, and
1466 evaluating with suit=0 is equiv to all honours! */
1467 /* tmpstrat.suitness = stratparams[suitness].values[0]; */
1468 tmpstrat.suitness = 0.0;
1469 tmpstrat.suit = 0;
1470 beststrat.chowness = tmpstrat.chowness;
1471 for ( i=0 ; i < stratparams[chowness].ncomps ; i++ ) {
1472 tmpstrat.chowness = stratparams[chowness].values[i];
1473 val = evalhand(p,&tmpstrat);
1474 if ( val > bestval ) {
1475 bestval = val ;
1476 beststrat.chowness = tmpstrat.chowness;
1477 }
1478 }
1479 tmpstrat.chowness = beststrat.chowness;
1480 beststrat.hiddenness = tmpstrat.hiddenness;
1481 /* from i = 1, since we had [0] just above */
1482 for ( i = 1 ; i < stratparams[hiddenness].ncomps ; i++ ) {
1483 tmpstrat.hiddenness = stratparams[hiddenness].values[i];
1484 val = evalhand(p,&tmpstrat);
1485 if ( val > bestval ) {
1486 bestval = val ;
1487 beststrat.hiddenness = tmpstrat.hiddenness;
1488 }
1489 }
1490 /* and for suit */
1491 tmpstrat.hiddenness = beststrat.hiddenness;
1492 /* now we need to reset the best val, since only now
1493 are we considering the values the user had */
1494 bestval = -1000;
1495 for ( i = 0; i < stratparams[suitness].ncomps; i++ ) {
1496 tmpstrat.suitness = stratparams[suitness].values[i];
1497 /* j = 0 corresponds to all honours, which we shd get
1498 if we can ?? */
1499 for ( j = 0 ; j <= ((tmpstrat.suitness == 0.0)? 0 : 3) ; j++ ) {
1500 tmpstrat.suit = j;
1501 val = evalhand(p,&tmpstrat);
1502 if ( val > bestval ) {
1503 bestval = val ;
1504 beststrat = tmpstrat;
1505 }
1506 }
1507 }
1508 /* we'll now try for all majors... */
1509 /* however, there's no point in doing this unless we already
1510 think that a pung-based strategy is best */
1511 if ( beststrat.chowness < 0.0 ) {
1512 tmpstrat = beststrat;
1513 for ( i = 1; i < stratparams[majorness].ncomps; i++ ) {
1514 tmpstrat.majorness = stratparams[majorness].values[i];
1515 val = evalhand(p,&tmpstrat);
1516 if ( val > bestval ) {
1517 bestval = val ;
1518 beststrat = tmpstrat;
1519 }
1520 }
1521 }
1522 /* if some other player has four sets declared, then switch to
1523 Fast */
1524 dofast = 0;
1525 #if 0
1526 for ( i = 0 ; i < NUM_SEATS ; i++ ) {
1527 PlayerP p1 = the_game->players[i];
1528 if ( p1 == p ) continue;
1529 if ( p1->num_concealed <= 1 ) dofast = 1;
1530 }
1531 #endif
1532 if ( dofast ) {
1533 /* strat = Fast; */
1534 if ( debugeval ) { fprintf(debugf,"Using fast\n"); }
1535 } else {
1536 if ( bestval >= oval + hysteresis ) {
1537 *strat = beststrat;
1538 if ( debugeval ) {
1539 static char buf[128];
1540 fprintf(debugf,"Switching to strat c=%.1f h=%.1f s=%.1f (%d) m=%.1f\n",
1541 strat->chowness,strat->hiddenness,
1542 strat->suitness,strat->suit,strat->majorness);
1543 player_print_tiles(buf,p,0);
1544 fprintf(debugf," with hand %s\n",buf);
1545 }
1546 }
1547 }
1548 }
1549