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