1 /* $Header: /home/jcb/MahJong/newmj/RCS/player.c,v 12.0 2009/06/28 20:43:13 jcb Rel $
2  * player.c
3  * Implements functions on players.
4  */
5 /****************** COPYRIGHT STATEMENT **********************
6  * This file is Copyright (c) 2000 by J. C. Bradfield.       *
7  * Distribution and use is governed by the LICENCE file that *
8  * accompanies this file.                                    *
9  * The moral rights of the author are asserted.              *
10  *                                                           *
11  ***************** DISCLAIMER OF WARRANTY ********************
12  * This code is not warranted fit for any purpose. See the   *
13  * LICENCE file for further information.                     *
14  *                                                           *
15  *************************************************************/
16 
17 static const char rcs_id[] = "$Header: /home/jcb/MahJong/newmj/RCS/player.c,v 12.0 2009/06/28 20:43:13 jcb Rel $";
18 
19 #define PLAYER_C /* so that PlayerP is not const */
20 #include "player.h"
21 #include "sysdep.h"
22 
23 /* At present, this function is not exported. If it becomes
24    exported, the name should perhaps be made better.
25    player_has_mah_jong: return true if the player's hand in its
26    current state of organization is a complete mah-jong hand.
27    (That is, the hand has been organized into four sets and a pair, etc.)
28    The last arg is flags for seven pairs etc.
29 */
30 static int player_has_mah_jong(PlayerP p,MJSpecialHandFlags flags);
31 
32 /* utility function: copy player record into (already allocated)
33    space. Returns new (for consistency with memcpy) */
copy_player(PlayerP new,const PlayerP old)34 PlayerP copy_player(PlayerP new,const PlayerP old) {
35   return (PlayerP) memcpy((void *)new, (void *)old, sizeof(Player));
36 }
37 
38 
num_tiles_in_set(TileSetP tp)39 int num_tiles_in_set(TileSetP tp) {
40   switch ( tp->type ) {
41   case Empty:
42     return 0;
43   case Pair:
44   case ClosedPair:
45     return 2;
46   case Chow:
47   case ClosedChow:
48   case Pung:
49   case ClosedPung:
50     return 3;
51   case Kong:
52   case ClosedKong:
53     return 4;
54   default:
55     warn("num_tiles_in_set: unknown tile set type %d\n",tp->type);
56     /* this will cause chaos, but that's the idea ... */
57     return -1;
58   }
59 }
60 
61 
initialize_player(PlayerP p)62 void initialize_player(PlayerP p) {
63   p->err = NULL;
64   p->id = 0;
65   p->name = NULL;
66   p->wind = UnknownWind;
67   p->flags = 0;
68   p->cumulative_score = 0;
69   player_newhand(p,UnknownWind);
70 }
71 
player_newhand(PlayerP p,TileWind w)72 void player_newhand(PlayerP p,TileWind w) {
73   int i;
74   p->err = NULL;
75   p->wind = w;
76   p->num_concealed = 0;
77   p->discard_hint = -1;
78   for ( i = 0; i < MAX_CONCEALED; i++ ) (p->concealed)[i] = HiddenTile;
79   for ( i = 0; i < MAX_TILESETS; i++ ) {
80     (p->tilesets)[i].type = Empty;
81     (p->tilesets)[i].tile = HiddenTile;
82     (p->tilesets)[i].annexed = 0;
83   }
84   p->num_specials = 0;
85   for ( i = 0; i < 8; i++ ) (p->specials)[i] = HiddenTile;
86   psetflag(p,Hidden);
87   pclearflag(p,HandDeclared);
88   pclearflag(p,MahJongged);
89   psetflag(p,NoDiscard);
90   pclearflag(p,Calling);
91   pclearflag(p,OriginalCall);
92   for (i=0; i < NUM_SEATS; i++) p->dflags[i] = 0;
93   p->hand_score = -1;
94 }
95 
set_player_id(PlayerP p,int id)96 void set_player_id(PlayerP p, int id) {
97   p->err = NULL;
98   p->id = id;
99 }
100 
set_player_name(PlayerP p,const char * n)101 void set_player_name(PlayerP p, const char *n) {
102   p->err = NULL;
103   if ( p->name ) free(p->name);
104   if ( n ) {
105     p->name = (char *)malloc(strlen(n)+1);
106     strcpy(p->name,n);
107   } else p->name = (char *)0;
108 }
109 
set_player_cumulative_score(PlayerP p,int s)110 void set_player_cumulative_score(PlayerP p, int s) {
111   p->err = NULL;
112   p->cumulative_score = s;
113 }
114 
change_player_cumulative_score(PlayerP p,int d)115 void change_player_cumulative_score(PlayerP p, int d) {
116   p->err = NULL;
117   p->cumulative_score += d;
118 }
119 
set_player_hand_score(PlayerP p,int h)120 void set_player_hand_score(PlayerP p, int h) {
121   p->err = NULL;
122   p->hand_score = h;
123 }
124 
set_player_userdata(PlayerP p,void * ud)125 void set_player_userdata(PlayerP p, void *ud) {
126   p->err = NULL;
127   p->userdata = ud;
128 }
129 
130 /* utility functions. Maybe they should be exported.
131    But we should probably adhere to the convention that exported
132    functions do not leave data structures in intermediate states,
133    as these do.
134 */
135 
136 /* player_count_tile: looks for copies of t in p's concealed tiles.
137    returns number found or -1 on error.
138    Should not be called on unknown players.
139 */
player_count_tile(PlayerP p,Tile t)140 int player_count_tile(PlayerP p, Tile t) {
141   int n,i;
142 
143   p->err = NULL;
144   if ( pflag(p,Hidden) ) {
145     p->err = "Can't count tiles of hidden player";
146     return -1;
147   }
148 
149   for ( i = 0, n = 0 ; i < p->num_concealed ; i++ ) {
150     if ( p->concealed[i] == t ) n++ ;
151   }
152   return n;
153 }
154 
155 
156 /* add_tile: adds t to p's concealed tiles. Should not be called
157    on hidden players.
158 */
add_tile(PlayerP p,Tile t)159 static int add_tile(PlayerP p, Tile t) {
160 
161   assert(!pflag(p,Hidden)); /* nonsense to call this on hidden player */
162 
163   //  assert(p->num_concealed < MAX_CONCEALED);
164   if ( ! (p->num_concealed < MAX_CONCEALED) ) {
165     error("player.c:add_tile(): player id %d has too many tiles: num_concealed = %d\n",p->id,p->num_concealed);
166     return 0;
167   }
168   p->concealed[p->num_concealed++] = t;
169   return 1;
170 }
171 
player_set_discard_hint(PlayerP p,int h)172 int player_set_discard_hint(PlayerP p, int h) {
173   if ( h < 0 || h >= p->num_concealed ) {
174     warn("player_discard_hint: out of range hint %d",h);
175     return 0;
176   }
177   p->discard_hint = h;
178   return 1;
179 }
180 
181 /* remove_tile: removes one instance of t from p's concealed tiles.
182    Returns 1 on success, 0 on failure.
183    Should not be called on hidden players.
184 */
remove_tile(PlayerP p,Tile t)185 static int remove_tile(PlayerP p, Tile t) {
186   int i,d;
187 
188   assert(!pflag(p,Hidden)); /* nonsense to call this on hidden player */
189 
190   i = 0;
191   d = p->discard_hint;
192   p->discard_hint = -1; /* clear it whatever happens next */
193   if ( d >= 0 ) {
194     if ( d >= p->num_concealed ) {
195       warn("Bad discard hint %d -- too big",d);
196       d = -1;
197     } else if ( p->concealed[d] != t ) {
198       warn("Bad discard hint -- tile %d is not %d",d,t);
199       d = -1;
200     }
201   }
202   if ( d >= 0 )
203     i = d;
204   else
205     while ( i < p->num_concealed && p->concealed[i] != t ) i++ ;
206   if ( i == p->num_concealed ) return 0;
207   while ( i+1 < p->num_concealed ) {
208     p->concealed[i] = p->concealed[i+1];
209     i++ ;
210   }
211   p->num_concealed -= 1 ;
212   return 1;
213 }
214 
215 /* add_tileset: adds a tile set of type ty and tile t to p.
216    Returns 1 on success, 0 on failure.
217    Always sets the annexed flag to 0.
218 */
add_tileset(PlayerP p,TileSetType ty,Tile t)219 static int add_tileset(PlayerP p, TileSetType ty, Tile t) {
220   int s = 0;
221 
222   while ( s < MAX_TILESETS && p->tilesets[s].type != Empty ) s++ ;
223 
224   assert(s < MAX_TILESETS); /* serious problem if run out of slots */
225 
226   p->tilesets[s].type = ty;
227   p->tilesets[s].tile = t;
228   p->tilesets[s].annexed = 0;
229   return 1;
230 }
231 
232 
player_draws_tile(PlayerP p,Tile t)233 int player_draws_tile(PlayerP p, Tile t) {
234   p->err = NULL;
235   if ( p->num_concealed == 0 ) {
236     if ( t == HiddenTile ) psetflag(p,Hidden);
237     else pclearflag(p,Hidden);
238   }
239   if ( pflag(p,Hidden) ) p->num_concealed++;
240   else {
241     if ( t == HiddenTile ) {
242       p->err = "player_draws_tile: HiddenTile, but we know the player!";
243       return 0;
244     }
245     if ( add_tile(p,t) == 0 ) {
246       p->err = "player_draws_tile: add_tile failed";
247       return 0;
248     }
249   }
250   return 1;
251 }
252 
player_draws_loose_tile(PlayerP p,Tile t)253 int player_draws_loose_tile(PlayerP p, Tile t) {
254   p->err = NULL;
255   if ( pflag(p,Hidden) ) p->num_concealed++;
256   else {
257     if ( t == HiddenTile ) {
258       p->err = "player_draws_loose_tile: HiddenTile, but we know the player!";
259       return 0;
260     }
261     if ( add_tile(p,t) == 0 ) {
262       p->err = "player_draws_loose_tile: add_tile failed";
263       return 0;
264     }
265   }
266   return 1;
267 }
268 
269 
270 /* NOTE: it is a required feature of the following functions that
271    if they fail, they leave the player structure unchanged. */
272 
273 /* implements the two following functions */
int_pds(PlayerP p,Tile spec,int testonly)274 static int int_pds(PlayerP p, Tile spec, int testonly) {
275   p->err = NULL;
276 
277   if ( ! is_special(spec) ) {
278     p->err = "player_declares_special: the special tile isn't special\n";
279     return 0;
280   }
281   /* paranoid, but why not? */
282   if ( p->num_specials == 8 ) {
283     p->err = "player_declares_special: player already has all specials!";
284     return 0;
285   }
286   if ( pflag(p,Hidden) ) {
287     if ( p->num_concealed < 1 ) {
288       p->err = "player_can_declare_special: player has no tiles\n";
289       return 0;
290     }
291     if ( testonly ) return 1;
292     p->specials[p->num_specials++] = spec;
293     p->num_concealed--;
294     return 1;
295   }
296 
297   if ( player_count_tile(p,spec) < 1 ) return 0;
298   if ( testonly ) return 1;
299   /* now do it */
300   p->specials[p->num_specials++] = spec;
301   remove_tile(p,spec);
302   return 1;
303 }
304 
player_declares_special(PlayerP p,Tile spec)305 int player_declares_special(PlayerP p, Tile spec) {
306   return int_pds(p,spec,0);
307 }
308 
player_can_declare_special(PlayerP p,Tile spec)309 int player_can_declare_special(PlayerP p, Tile spec) {
310   return int_pds(p,spec,1);
311 }
312 
313 /* implements the several pung functions.
314    Determines whether p can use d to form a pung; and if not testonly,
315    does it.
316    If the last arg is 0, then d is a discard; otherwise, if the last arg is
317    1, d is also to be found in hand, and we are forming a closed pung
318    (this is used in scoring).
319 */
320 
int_pp(PlayerP p,Tile d,int testonly,int tileinhand)321 static int int_pp(PlayerP p, Tile d, int testonly, int tileinhand) {
322   p->err = NULL;
323 
324   if ( pflag(p,Hidden) ) {
325     /* just assume it can be done */
326     if ( p->num_concealed < (tileinhand ? 3 : 2) ) {
327       p->err = "player_pungs: can't pung with too few concealed tiles\n";
328       return 0;
329     }
330     if ( testonly ) return 1;
331     p->num_concealed -= (tileinhand ? 3: 2);
332     add_tileset(p,(tileinhand ? ClosedPung : Pung),d);
333     if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
334     return 1;
335   }
336 
337   /* otherwise, we know the player, and need to check legality */
338   if ( player_count_tile(p,d) < (tileinhand ? 3 : 2) ) {
339     p->err = "player_pungs: not enough matching tiles\n";
340     return 0;
341   }
342   /* OK, it's legal */
343   if ( testonly ) return 1;
344   /* add the tileset */
345   add_tileset(p, (tileinhand ? ClosedPung : Pung), d);
346   /* remove the tiles from the hand */
347   remove_tile(p,d);
348   remove_tile(p,d);
349   if ( tileinhand ) remove_tile(p,d);
350   if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
351   return 1;
352 }
353 
player_pungs(PlayerP p,Tile d)354 int player_pungs(PlayerP p, Tile d) {
355   return int_pp(p,d,0,0);
356 }
357 
player_can_pung(PlayerP p,Tile d)358 int player_can_pung(PlayerP p, Tile d) {
359   return int_pp(p,d,1,0);
360 }
361 
player_forms_closed_pung(PlayerP p,Tile d)362 int player_forms_closed_pung(PlayerP p, Tile d) {
363   return int_pp(p, d, 0, 1);
364 }
365 
player_can_form_closed_pung(PlayerP p,Tile d)366 int player_can_form_closed_pung(PlayerP p, Tile d) {
367   return int_pp(p, d, 1, 1);
368 }
369 
370 /* likewise implements the several pair functions.
371    Determines whether p can use d to form a pair; and if not testonly,
372    does it.
373    If the last arg is 0, then d is a discard; otherwise, if the last arg is
374    1, d is also to be found in hand, and we are forming a closed pair
375    (this is used in scoring).
376 */
377 
int_ppr(PlayerP p,Tile d,int testonly,int tileinhand)378 static int int_ppr(PlayerP p, Tile d, int testonly, int tileinhand) {
379 
380   p->err = NULL;
381 
382   if ( pflag(p,Hidden) ) {
383     /* just assume it can be done */
384     if ( p->num_concealed < (tileinhand ? 2 : 1) ) {
385       p->err = "player_pairs: can't pair with too few concealed tiles\n";
386       return 0;
387     }
388     p->num_concealed -= (tileinhand ? 2: 1);
389     add_tileset(p,(tileinhand ? ClosedPair : Pair),d);
390     if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
391     return 1;
392   }
393 
394   /* otherwise, we know the player, and need to check legality */
395   if ( player_count_tile(p,d) < (tileinhand ? 2 : 1) ) {
396     p->err = "player_pairs: not enough matching tiles\n";
397     return 0;
398   }
399   /* OK, it's legal */
400   if ( testonly ) return 1;
401   /* add the tileset */
402   add_tileset(p, (tileinhand ? ClosedPair : Pair), d);
403   /* remove the tiles from the hand */
404   remove_tile(p,d);
405   if ( tileinhand ) remove_tile(p,d);
406   if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
407   return 1;
408 }
409 
player_pairs(PlayerP p,Tile d)410 int player_pairs(PlayerP p, Tile d) {
411   return int_ppr(p,d,0,0);
412 }
413 
player_can_pair(PlayerP p,Tile d)414 int player_can_pair(PlayerP p, Tile d) {
415   return int_ppr(p,d,1,0);
416 }
417 
player_forms_closed_pair(PlayerP p,Tile t)418 int player_forms_closed_pair(PlayerP p, Tile t) {
419   return int_ppr(p,t,0,1);
420 }
421 
player_can_form_closed_pair(PlayerP p,Tile t)422 int player_can_form_closed_pair(PlayerP p, Tile t) {
423   return int_ppr(p,t,1,1);
424 }
425 
426 /* implements several following functions;
427    testonly and tileinhand as before */
428 
int_pk(PlayerP p,Tile d,int testonly,int tileinhand)429 static int int_pk(PlayerP p, Tile d, int testonly, int tileinhand) {
430 
431   p->err = NULL;
432   if ( pflag(p,Hidden) ) {
433     /* just assume it can be done */
434     if ( p->num_concealed < (tileinhand ? 4 : 3) ) {
435       p->err = "player_kongs: can't kong with too few concealed tiles\n";
436       return 0;
437     }
438     p->num_concealed -= (tileinhand ? 4 : 3); /* lose three/four, gain replacement */
439     add_tileset(p,(tileinhand ? ClosedKong : Kong), d);
440     return 1;
441   }
442 
443   /* otherwise, we know the player, and need to check legality */
444   if ( player_count_tile(p,d) < (tileinhand ? 4 : 3) ) {
445     p->err = "player_kongs: not enough matching tiles\n";
446     return 0;
447   }
448   /* OK, it's legal */
449   if ( testonly ) return 1;
450   /* add the tileset */
451   add_tileset(p,(tileinhand ? ClosedKong : Kong),d);
452   /* remove the tiles from the hand */
453   remove_tile(p,d);
454   remove_tile(p,d);
455   remove_tile(p,d);
456   if ( tileinhand ) remove_tile(p,d);
457   return 1;
458 }
459 
player_kongs(PlayerP p,Tile d)460 int player_kongs(PlayerP p, Tile d) {
461   return int_pk(p,d,0,0);
462 }
463 
player_can_kong(PlayerP p,Tile d)464 int player_can_kong(PlayerP p, Tile d) {
465   return int_pk(p, d, 1, 0);
466 }
467 
player_declares_closed_kong(PlayerP p,Tile d)468 int player_declares_closed_kong(PlayerP p, Tile d) {
469   return int_pk(p, d, 0, 1);
470 }
471 
player_can_declare_closed_kong(PlayerP p,Tile d)472 int player_can_declare_closed_kong(PlayerP p, Tile d) {
473   return int_pk(p, d, 1, 1);
474 }
475 
476 /* Implements two following functions. */
int_pap(PlayerP p,Tile t,int testonly)477 static int int_pap(PlayerP p, Tile t, int testonly) {
478   int i;
479 
480   p->err = NULL;
481   /* First check that the player has an exposed pung of the tile */
482   for ( i=0 ; i < MAX_TILESETS ; i++ ) {
483     if ( p->tilesets[i].type == Pung && p->tilesets[i].tile == t )
484       break;
485   }
486   if ( i == MAX_TILESETS ) {
487     p->err = "player_adds_to_pung called with no pung";
488     return 0;
489   }
490   /* now check (if poss) that the tile is in hand */
491   if ( pflag(p,Hidden) ) {
492     /* can't see the tiles, so trust it */
493     if ( p->num_concealed < 1 ) {
494       /* err, this is actually impossible */
495       p->err = "player_adds_to_pung: no concealed tiles";
496       return 0;
497     }
498     /* lose one concealed tile */
499     p->num_concealed--;
500     p->tilesets[i].type = Kong;
501     p->tilesets[i].annexed = 1;
502     return 1;
503   }
504 
505   /* otherwise, we know the player and need to check legality */
506   if ( player_count_tile(p,t) < 1 ) {
507     p->err = "player_adds_to_pung: tile not found in hand";
508     return 0;
509   }
510     /* OK, it's legal */
511   if ( testonly ) return 1;
512 
513   /* modify the tileset */
514   p->tilesets[i].type = Kong;
515   p->tilesets[i].annexed = 1;
516   /* remove the tile from the hand */
517   remove_tile(p,t);
518   return 1;
519 }
520 
player_adds_to_pung(PlayerP p,Tile t)521 int player_adds_to_pung(PlayerP p, Tile t) {
522   return int_pap(p,t,0);
523 }
524 
player_can_add_to_pung(PlayerP p,Tile t)525 int player_can_add_to_pung(PlayerP p,Tile t) {
526   return int_pap(p,t,1);
527 }
528 
529 /* player_kong_robbed: the player has formed a kong of t, and it is robbed */
player_kong_is_robbed(PlayerP p,Tile t)530 int player_kong_is_robbed(PlayerP p, Tile t) {
531   int i;
532 
533   p->err = NULL;
534   /* find the kong in question */
535   for ( i=0 ; i < MAX_TILESETS ; i++ ) {
536     if ( p->tilesets[i].tile == t
537 	 && ( p->tilesets[i].type == Kong
538 	      || p->tilesets[i].type == ClosedKong ) )
539       break;
540   }
541   if ( i == MAX_TILESETS ) {
542     p->err = "player_kong_is_robbed called with no kong";
543     return 0;
544   }
545   /* and downgrade it */
546   if ( p->tilesets[i].type == Kong ) p->tilesets[i].type = Pung ;
547   else p->tilesets[i].type = ClosedPung;
548   p->tilesets[i].annexed = 0;
549   return 1;
550 }
551 
552 /* the chow handling function */
int_pc(PlayerP p,Tile d,ChowPosition r,int testonly,int tileinhand)553 static int int_pc(PlayerP p, Tile d, ChowPosition r, int testonly, int tileinhand) {
554   Tile low, mid, high;
555 
556   p->err = NULL;
557   if ( ! is_suit(d) ) {
558     p->err = "Can't chow a non-suit tile\n";
559     return 0;
560   }
561 
562   if ( r == AnyPos ) {
563     if ( !testonly ) {
564       p->err = "Can't make a chow with AnyPos";
565       return 0;
566     }
567     return
568       int_pc(p,d,Lower,testonly,tileinhand)
569       || int_pc(p,d,Middle,testonly,tileinhand)
570       || int_pc(p,d,Upper,testonly,tileinhand);
571   }
572 
573   if ( r == Lower ) {
574     if ( value_of(d) > 7 ) {
575       p->err = "Impossible chow\n";
576       return 0;
577     }
578     low = d;
579     mid = d+1;
580     high = d+2;
581   } else if ( r == Middle ) {
582     if ( value_of(d) == 1 || value_of(d) == 9 ) {
583       p->err = "Impossible chow\n";
584       return 0;
585     }
586     mid = d;
587     low = d-1;
588     high = d+1;
589   } else /* r == Upper */ {
590     if ( value_of(d) < 3 ) {
591       p->err = "Impossible chow\n";
592       return 0;
593     }
594     high = d;
595     mid = d-1;
596     low = d-2;
597   }
598 
599   if ( pflag(p,Hidden) ) {
600     if ( testonly ) return 1;
601     p->num_concealed -= (tileinhand ? 3 : 2);
602     add_tileset(p,(tileinhand ? ClosedChow : Chow),low);
603     if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
604     return 1;
605   }
606 
607   /* Check we have the tiles */
608   if ( tileinhand || r != Lower ) {
609     if ( player_count_tile(p,low) < 1 ) {
610       p->err = "Don't have the other tiles for the chow";
611       return 0;
612     }
613   }
614   if ( tileinhand || r != Middle ) {
615     if ( player_count_tile(p,mid) < 1 ) {
616       p->err = "Don't have the other tiles for the chow";
617       return 0;
618     }
619   }
620   if ( tileinhand || r != Upper ) {
621     if ( player_count_tile(p,high) < 1 ) {
622       p->err = "Don't have the other tiles for the chow";
623       return 0;
624     }
625   }
626   /* OK, we're ready to go */
627   if ( testonly ) return 1;
628   /* this might fail if the state is inconsistent, in which case
629      we should leave p unchanged. Hence it comes first.
630   */
631   if ( !add_tileset(p,(tileinhand ? ClosedChow : Chow),low) ) {
632     p->err = "int_pc: add_tileset failed!";
633     return 0;
634   }
635   /* remove the tiles */
636   if ( tileinhand || r != Lower ) {
637     remove_tile(p,low);
638   }
639   if ( tileinhand || r != Middle ) {
640     remove_tile(p,mid);
641   }
642   if ( tileinhand || r != Upper ) {
643     remove_tile(p,high);
644   }
645   if ( p->num_concealed == 0 ) psetflag(p,HandDeclared);
646   return 1;
647 }
648 
player_chows(PlayerP p,Tile d,ChowPosition r)649 int player_chows(PlayerP p, Tile d, ChowPosition r) {
650   return int_pc(p,d,r,0,0);
651 }
652 
player_can_chow(PlayerP p,Tile d,ChowPosition r)653 int player_can_chow(PlayerP p, Tile d, ChowPosition r) {
654   return int_pc(p,d,r,1,0);
655 }
656 
player_forms_closed_chow(PlayerP p,Tile d,ChowPosition r)657 int player_forms_closed_chow(PlayerP p, Tile d, ChowPosition r) {
658   return int_pc(p,d,r,0,1);
659 }
660 
player_can_form_closed_chow(PlayerP p,Tile d,ChowPosition r)661 int player_can_form_closed_chow(PlayerP p, Tile d, ChowPosition r) {
662   return int_pc(p,d,r,1,1);
663 }
664 
665 /* discarding a tile */
int_pd(PlayerP p,Tile t,int testonly)666 static int int_pd(PlayerP p, Tile t, int testonly) {
667   p->err = NULL;
668   if ( pflag(p,Hidden) ) {
669     if ( p->num_concealed < 1 ) {
670       p->err = "No tiles to discard!\n";
671       return 0;
672     }
673     if ( testonly ) return 1;
674     p->num_concealed -= 1;
675     pclearflag(p,NoDiscard);
676     return 1;
677   }
678   if ( player_count_tile(p,t) < 1 ) {
679     p->err = "Tile not found in hand\n";
680     return 0;
681   }
682   if ( testonly ) return 1;
683   remove_tile(p,t);
684   pclearflag(p,NoDiscard);
685   return 1;
686 }
687 
player_discards(PlayerP p,Tile t)688 int player_discards(PlayerP p, Tile t) {
689   return int_pd(p,t,0);
690 }
691 
player_can_discard(PlayerP p,Tile t)692 int player_can_discard(PlayerP p, Tile t) {
693   return int_pd(p,t,1);
694 }
695 
696 /* player_can_mah_jong: determine whether this hand (perhaps with
697    an added discard tile) is a mah-jong hand.
698    This function is completely brain-dead about finding a possible
699    mah jong; given the small search space, there seems no point in
700    being at all clever.
701 */
player_can_mah_jong(PlayerP p,Tile d,MJSpecialHandFlags flags)702 int player_can_mah_jong(PlayerP p,Tile d,MJSpecialHandFlags flags) {
703   Player pcopy; /* we will manipulate this copy of the player */
704   PlayerP pcp = &pcopy;
705   int answer = 0;
706   Tile t;
707 
708   p->err = NULL;
709   /* Technique is depth-first search of possible hands: just
710      try applying making all possible tilesets until we succeed */
711 
712   /* The base case of the recursion */
713   if ( d == HiddenTile && p->num_concealed <= 1 ) {
714     answer = player_has_mah_jong(p,flags);
715     goto done;
716   }
717 
718   /* otherwise, try making tilesets.
719      First deal with the discard tile, if it exists.
720      For each possible way of using the discard,
721      make a copy of the player, use the discard, and see
722      if the result is mah-jongable.
723   */
724   if ( d != HiddenTile ) {
725     copy_player(pcp,p);
726     answer = (player_pungs(pcp,d)
727 	      && player_can_mah_jong(pcp,HiddenTile,flags));
728     if (answer) goto done;
729 
730     copy_player(pcp,p);
731     answer = (player_chows(pcp,d,Lower)
732 	      && player_can_mah_jong(pcp,HiddenTile,flags));
733     if (answer) goto done;
734 
735     copy_player(pcp,p);
736     answer = (player_chows(pcp,d,Middle)
737 	      && player_can_mah_jong(pcp,HiddenTile,flags));
738     if (answer) goto done;
739 
740     copy_player(pcp,p);
741     answer = (player_chows(pcp,d,Upper)
742 	      && player_can_mah_jong(pcp,HiddenTile,flags));
743     if (answer) goto done;
744 
745     copy_player(pcp,p);
746     answer = (player_pairs(pcp,d)
747 	      && player_can_mah_jong(pcp,HiddenTile,flags));
748     goto done;
749   } else {
750     /* otherwise, for each concealed tile, try making something of it */
751     int i;
752 
753     for ( i = 0; i < p->num_concealed; i++ ) {
754       t = p->concealed[i];
755 
756       copy_player(pcp,p);
757       answer = (player_forms_closed_pung(pcp,t)
758 		&& player_can_mah_jong(pcp,HiddenTile,flags));
759       if (answer) goto done;
760 
761       copy_player(pcp,p);
762       answer = (player_forms_closed_chow(pcp,t,Lower)
763 		&& player_can_mah_jong(pcp,HiddenTile,flags));
764       if (answer) goto done;
765 
766       copy_player(pcp,p);
767       answer = (player_forms_closed_pair(pcp,t)
768 		&& player_can_mah_jong(pcp,HiddenTile,flags));
769       if (answer) goto done;
770     }
771   }
772  done:
773   /* if all else fails, try 13 unique wonders */
774   if ( (! answer)
775        && ((p->num_concealed == 13 && d != HiddenTile)
776 	   || (p->num_concealed == 14 && d == HiddenTile)))
777     answer = player_can_thirteen_wonders(p,d);
778   return answer;
779 }
780 
781 /* does the player have 13 unique wonders? */
player_can_thirteen_wonders(PlayerP p,Tile d)782 int player_can_thirteen_wonders(PlayerP p, Tile d) {
783   Player pcopy; /* we will manipulate this copy of the player */
784   PlayerP pcp = &pcopy;
785   int answer = 0;
786   int i, havetwo, n;
787 
788   if ( d != HiddenTile ) {
789     copy_player(pcp,p);
790     answer = (player_draws_tile(pcp,d)
791 	      && player_can_thirteen_wonders(pcp,HiddenTile));
792   } else {
793     answer = 1;
794     havetwo = 0;
795     for ( i=0; i < 13; i++ ) {
796       n = player_count_tile(p,thirteen_wonders[i]);
797       if ( n > 2 ) { answer = 0; break; }
798       else if ( n == 2 ) {
799 	if ( havetwo ) { answer = 0; break; }
800 	else { havetwo = 1; }
801       } else if ( n == 0 ) { answer = 0; break; }
802     }
803     if ( ! havetwo ) answer = 0;
804   }
805   return answer;
806 }
807 
808 
809 /* This internal function defines the mah jong hands.
810    (Currently it does not include thirteen wonders).
811    Last arg allows seven pairs etc.
812 */
player_has_mah_jong(PlayerP p,MJSpecialHandFlags flags)813 static int player_has_mah_jong(PlayerP p,MJSpecialHandFlags flags) {
814   int i,s,n;
815   int answer;
816 
817   p->err = NULL;
818   /* all tiles must be in sets */
819   if ( p->num_concealed > 0 ) return 0;
820 
821   answer = 0;
822   /* there must be exactly  five non-empty sets, exactly
823      one of which must be a pair */
824   for (i=0, n=0, s=0; i<MAX_TILESETS; i++) {
825     if ( p->tilesets[i].type != Empty ) s++;
826     if ( num_tiles_in_set(&(p->tilesets[i])) == 2) n++;
827   }
828   if ( s == 5 && n == 1 ) answer = 1;
829   /* seven pairs may be allowed */
830   if ( (flags & MJSevenPairs) && s == 7 && n == 7 ) answer = 1;
831 
832   /* that's it */
833   return answer;
834 }
835 
836 /* sort the player's concealed tiles.
837 */
player_sort_tiles(PlayerP p)838 void player_sort_tiles(PlayerP p) {
839   int nconc; int i,j;
840   Tile *tp = p->concealed;
841   Tile t;
842 
843   p->err = NULL;
844   if ( pflag(p,Hidden) ) return; /* pretty dumb ... */
845   nconc = p->num_concealed;
846 
847   /* bubble sort */
848   for ( i = 0 ; i < nconc-1 ; i++ ) {
849     if ( tp[i+1] < tp[i] ) {
850       /* sink it to the bottom */
851       for ( j = i+1 ; tp[j] < tp[j-1] && j > 0 ; j-- ) {
852 	t = tp[j-1]; tp[j-1] = tp[j]; tp[j] = t;
853       }
854     }
855   }
856 }
857 
858 /* move a tile */
player_reorder_tile(PlayerP p,int old,int new)859 int player_reorder_tile(PlayerP p, int old, int new) {
860   int i;
861   if ( old < 0 || new < 0
862     || old >= p->num_concealed || new >= p->num_concealed ) {
863     warn("player_reorder_tile: arguments out of range %d, %d",old,new);
864     return 0;
865   }
866   if ( old == new ) return 1;
867   Tile t = p->concealed[old];
868   if ( new > old ) {
869     for (i = old+1; i <= new; i++)
870       p->concealed[i-1] = p->concealed[i];
871     p->concealed[new] = t;
872   } else {
873     for (i = old-1; i >= new; i--)
874       p->concealed[i+1] = p->concealed[i];
875     p->concealed[new] = t;
876   }
877   return 1;
878 }
879 
880 /* This function generates a string repn of a players tiles. */
player_print_tiles(char * buf,PlayerP p,int hide)881 void player_print_tiles(char *buf, PlayerP p, int hide) {
882   int i,j;
883   Tile *tp; Tile t;
884   Tile ctiles[MAX_CONCEALED]; int nconc;
885 
886   p->err = NULL;
887   if ( pflag(p,Hidden) ) hide = 1; /* can't see the tiles anyway */
888 
889   /* copy the concealed files and sort them.
890      This assumes knowledge that Tiles are shorts.
891   */
892   nconc = p->num_concealed;
893   if ( ! hide ) {
894     memcpy(ctiles,p->concealed,nconc*sizeof(Tile));
895     tp = ctiles;
896     /* bubble sort */
897     for ( i = 0 ; i < nconc-1 ; i++ ) {
898       if ( tp[i+1] < tp[i] ) {
899 	/* sink it to the bottom */
900 	for ( j = i+1 ; tp[j] < tp[j-1] && j > 0 ; j-- ) {
901 	  t = tp[j-1]; tp[j-1] = tp[j]; tp[j] = t;
902 	}
903       }
904     }
905   }
906 
907   buf[0] = '\000' ;
908   for (i=0; i < nconc; i++) {
909     if ( i > 0 ) strcat(buf," ");
910     strcat(buf, hide ? "--" : tile_code(ctiles[i]));
911   }
912   strcat(buf," *");
913 
914   for (i = 0; i<MAX_TILESETS; i++) {
915     if ( p->tilesets[i].type == Empty ) continue;
916     strcat(buf," ");
917     strcat(buf,tileset_string(&p->tilesets[i]));
918   }
919 
920   strcat(buf," * ");
921   for (i=0; i < p->num_specials; i++) {
922     if ( i > 0 ) strcat(buf," ");
923     strcat(buf,tile_code(p->specials[i]));
924   }
925 }
926 
set_player_tiles(PlayerP p,char * desc)927 int set_player_tiles(PlayerP p,char *desc) {
928   int i = 0;
929   char tc[3]; /* for tile codes */
930   int ts;
931   int closed, annexed;
932   Tile tile;
933   TileSetType ttype;
934 
935   p->err = NULL;
936   /* clear the current information */
937   p->num_concealed = 0;
938   for (i=0; i < MAX_TILESETS ; i++) p->tilesets->type = Empty;
939   p->num_specials = 0;
940   pclearflag(p,Hidden);
941 
942   while ( *desc && isspace(*desc) ) desc++;
943   /* read the tiles in hand */
944   while ( *desc && *desc != '*' ) {
945     tc[0] = *(desc++);
946     tc[1] = *(desc++);
947     tc[2] = '\000';
948     tile = tile_decode(tc);
949     if ( tile == ErrorTile ) {
950       warn("format error in set_player_tile");
951       return 0;
952     }
953     if ( tile == HiddenTile ) {
954       p->num_concealed++;
955       psetflag(p,Hidden);
956     } else {
957       p->concealed[p->num_concealed++] = tile;
958     }
959     while ( *desc && isspace(*desc) ) desc++;
960   }
961   if ( ! *desc ) {
962     warn("format error in set_player_tiles");
963     return 0;
964   }
965   desc++; /* skip the * */
966   while ( *desc && isspace(*desc) ) desc++;
967 
968   /* parse the tilesets */
969   ts = 0;
970   while ( *desc && *desc != '*' ) {
971     annexed = 0;
972     closed = 0;
973     tc[0] = *(desc++);
974     tc[1] = *(desc++);
975     tc[2] = 0;
976     tile = tile_decode(tc);
977     if ( tile == ErrorTile ) {
978       warn("format error in set_player_tiles");
979       return 0;
980     }
981     if ( *(desc++) == '+' ) closed = 1;
982     /* we're lazy now: if the next tile is different,
983        we know it's a chow; otherwise we just skip to
984        the next white space, counting letters */
985     tc[0] = *(desc++);
986     tc[1] = *(desc++);
987     if ( tile_decode(tc) != tile ) {
988       ttype = closed ? ClosedChow : Chow;
989       desc += 3; /* skip last tile */
990     } else if ( isspace(*desc) ) {
991       /* two tiles; it's a pair */
992       ttype = closed ? ClosedPair : Pair;
993     } else {
994       /* skip next tile */
995       desc += 3;
996       if ( isspace(*desc) ) {
997 	/* it's a pung */
998 	ttype = closed ? ClosedPung : Pung;
999       } else {
1000 	/* it's a kong */
1001 	ttype = closed ? ClosedKong : Kong;
1002 	/* millington rules ? */
1003 	if ( ! closed )
1004 	  annexed = (*desc == '-');
1005 	desc += 3;
1006       }
1007     }
1008     p->tilesets[ts].type = ttype;
1009     p->tilesets[ts].tile = tile;
1010     p->tilesets[ts].annexed = annexed;
1011     ts++;
1012     while ( *desc && isspace(*desc) ) desc++;
1013   } /* end of matching tilesets loop */
1014 
1015   if ( ! *desc ) {
1016     warn("format error in set_player_tiles");
1017     return 0;
1018   }
1019   desc++; /* skip the * */
1020 
1021   while ( *desc && isspace(*desc) ) desc++;
1022   /* parse the specials */
1023   while ( *desc && *desc != '*' ) {
1024     tc[0] = *(desc++);
1025     tc[1] = *(desc++);
1026     tc[2] = '\000';
1027     tile = tile_decode(tc);
1028     if ( tile == ErrorTile ) {
1029       warn("format error in set_player_tile");
1030       return 0;
1031     }
1032     p->specials[p->num_specials++] = tile;
1033     while ( *desc && isspace(*desc) ) desc++;
1034   }
1035   /* phew */
1036   return 1;
1037 }
1038 
player_shows_tiles(PlayerP p,char * desc)1039 int player_shows_tiles(PlayerP p, char *desc) {
1040   char tc[3];
1041   Tile tile;
1042 
1043   p->err = NULL;
1044   /* If the player is not hidden, then we needn't
1045      do anything to the tiles */
1046   if ( pflag(p,Hidden) ) {
1047     /* clear the current information */
1048     p->num_concealed = 0;
1049     pclearflag(p,Hidden);
1050 
1051     while ( *desc && isspace(*desc) ) desc++;
1052     /* read the tiles in hand */
1053     while ( *desc ) {
1054       tc[0] = *(desc++);
1055       tc[1] = *(desc++);
1056       tc[2] = '\000';
1057       tile = tile_decode(tc);
1058       if ( tile == ErrorTile ) {
1059 	warn("format error in player_shows_tiles");
1060 	return 0;
1061       }
1062       p->concealed[p->num_concealed++] = tile;
1063       while ( *desc && isspace(*desc) ) desc++;
1064     }
1065   }
1066   psetflag(p,HandDeclared);
1067   return 1;
1068 }
1069 
1070 /* player_swap_tile: swaps the oldtile for the newtile in the
1071    concealed tiles. Only used in testing, of course */
player_swap_tile(PlayerP p,Tile oldtile,Tile newtile)1072 int player_swap_tile(PlayerP p, Tile oldtile, Tile newtile) {
1073   p->err = NULL;
1074   if ( ! remove_tile(p,oldtile) ) {
1075     p->err = "player doesn't have old tile";
1076     return 0;
1077   }
1078   add_tile(p,newtile);
1079   return 1;
1080 }
1081 
1082 /* return string representing a tileset */
tileset_string(TileSetP tp)1083 char *tileset_string(TileSetP tp) {
1084   char *sep;
1085   static char buf[20];
1086   short v; TileSuit s;
1087   int j;
1088 
1089   buf[0] = '\000';
1090 
1091   sep = "-";
1092   switch ( tp->type ) {
1093   case ClosedPung:
1094   case ClosedKong:
1095   case ClosedPair:
1096     sep = "+";
1097   case Pung:
1098   case Kong:
1099   case Pair:
1100     for ( j = 0; j < num_tiles_in_set(tp); j++ ) {
1101       if ( tp->type == Kong
1102 	&& !tp->annexed
1103 	&& j == num_tiles_in_set(tp) - 1 )
1104 	sep = "+";
1105       if ( j > 0 ) strcat(buf,sep);
1106       strcat(buf,tile_code(tp->tile));
1107     }
1108     break;
1109   case ClosedChow:
1110     sep = "+";
1111   case Chow:
1112     v = value_of(tp->tile);
1113     s = suit_of(tp->tile);
1114     strcat(buf,tile_code(tp->tile));
1115     strcat(buf,sep);
1116     strcat(buf,tile_code(make_tile(s,v+1)));
1117     strcat(buf,sep);
1118     strcat(buf,tile_code(make_tile(s,v+2)));
1119     break;
1120   case Empty:
1121     /* errrr */
1122     return "";
1123   }
1124   return buf;
1125 }
1126 
1127 /* print into an internal buffer a representation of the player */
player_print_state(PlayerP p)1128 char *player_print_state(PlayerP p) {
1129   static char buf[256],tiles[80],flags[80],dflags[80];
1130   int i,j;
1131 
1132   flags[0] = 0;
1133   for ( i = 0; i < 32; i++ ) {
1134     if ( pflag(p,i) ) {
1135       strcat(flags,nullprotect(player_print_PlayerFlags(i)));
1136       strcat(flags," ");
1137     }
1138   }
1139   dflags[0] = 0;
1140   for ( j = 0; j < NUM_SEATS; j++ ) {
1141     for ( i = 0; i < 32; i++ ) {
1142       if ( pdflag(p,j,1 << i) ) {
1143 	strcat(dflags,nullprotect(player_print_DangerSignals(1 << i)));
1144 	strcat(dflags," ");
1145       }
1146     }
1147     strcat(dflags,"; ");
1148   }
1149   player_print_tiles(tiles,p,0);
1150 
1151   sprintf(buf,"Player:\n"
1152 "id: %d\n"
1153 "name: %.100s\n"
1154 "wind: %s\n"
1155 "tiles: %s\n"
1156 "flags: %s\n"
1157 "dflags: %s\n"
1158 "cumulative_score: %d\n"
1159 "hand_score: %d\n"
1160 "err: %s\n"
1161 "userdata: %p\n",
1162     p->id,
1163     nullprotect(p->name),
1164     nullprotect(tiles_print_TileWind(p->wind)),
1165     tiles,
1166     flags,
1167     dflags,
1168     p->cumulative_score,
1169     p->hand_score,
1170     nullprotect(p->err),
1171     p->userdata);
1172   return buf;
1173 }
1174 
1175 /* enum functions */
1176 #include "player-enums.c"
1177