📄 player.c
字号:
int_pc(p,d,Lower,testonly,tileinhand) || int_pc(p,d,Middle,testonly,tileinhand) || int_pc(p,d,Upper,testonly,tileinhand); } if ( r == Lower ) { if ( value_of(d) > 7 ) { p->err = "Impossible chow\n"; return 0; } low = d; mid = d+1; high = d+2; } else if ( r == Middle ) { if ( value_of(d) == 1 || value_of(d) == 9 ) { p->err = "Impossible chow\n"; return 0; } mid = d; low = d-1; high = d+1; } else /* r == Upper */ { if ( value_of(d) < 3 ) { p->err = "Impossible chow\n"; return 0; } high = d; mid = d-1; low = d-2; } if ( pflag(p,Hidden) ) { if ( testonly ) return 1; p->num_concealed -= (tileinhand ? 3 : 2); add_tileset(p,(tileinhand ? ClosedChow : Chow),low); if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1; } /* Check we have the tiles */ if ( tileinhand || r != Lower ) { if ( player_count_tile(p,low) < 1 ) { p->err = "Don't have the other tiles for the chow"; return 0; } } if ( tileinhand || r != Middle ) { if ( player_count_tile(p,mid) < 1 ) { p->err = "Don't have the other tiles for the chow"; return 0; } } if ( tileinhand || r != Upper ) { if ( player_count_tile(p,high) < 1 ) { p->err = "Don't have the other tiles for the chow"; return 0; } } /* OK, we're ready to go */ if ( testonly ) return 1; /* this might fail if the state is inconsistent, in which case we should leave p unchanged. Hence it comes first. */ if ( !add_tileset(p,(tileinhand ? ClosedChow : Chow),low) ) { p->err = "int_pc: add_tileset failed!"; return 0; } /* remove the tiles */ if ( tileinhand || r != Lower ) { remove_tile(p,low); } if ( tileinhand || r != Middle ) { remove_tile(p,mid); } if ( tileinhand || r != Upper ) { remove_tile(p,high); } if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1;}int player_chows(PlayerP p, Tile d, ChowPosition r) { return int_pc(p,d,r,0,0);}int player_can_chow(PlayerP p, Tile d, ChowPosition r) { return int_pc(p,d,r,1,0);}int player_forms_closed_chow(PlayerP p, Tile d, ChowPosition r) { return int_pc(p,d,r,0,1);}int player_can_form_closed_chow(PlayerP p, Tile d, ChowPosition r) { return int_pc(p,d,r,1,1);}/* discarding a tile */static int int_pd(PlayerP p, Tile t, int testonly) { p->err = NULL; if ( pflag(p,Hidden) ) { if ( p->num_concealed < 1 ) { p->err = "No tiles to discard!\n"; return 0; } if ( testonly ) return 1; p->num_concealed -= 1; return 1; } if ( player_count_tile(p,t) < 1 ) { p->err = "Tile not found in hand\n"; return 0; } if ( testonly ) return 1; remove_tile(p,t); pclearflag(p,NoDiscard); return 1;}int player_discards(PlayerP p, Tile t) { return int_pd(p,t,0);}int player_can_discard(PlayerP p, Tile t) { return int_pd(p,t,1);}/* player_can_mah_jong: determine whether this hand (perhaps with an added discard tile) is a mah-jong hand. This function is completely brain-dead about finding a possible mah jong; given the small search space, there seems no point in being at all clever.*/int player_can_mah_jong(PlayerP p,Tile d,MJSpecialHandFlags flags) { Player pcopy; /* we will manipulate this copy of the player */ PlayerP pcp = &pcopy; int answer = 0; Tile t; p->err = NULL; /* Technique is depth-first search of possible hands: just try applying making all possible tilesets until we succeed */ /* The base case of the recursion */ if ( d == HiddenTile && p->num_concealed <= 1 ) { answer = player_has_mah_jong(p,flags); goto done; } /* otherwise, try making tilesets. First deal with the discard tile, if it exists. For each possible way of using the discard, make a copy of the player, use the discard, and see if the result is mah-jongable. */ if ( d != HiddenTile ) { copy_player(pcp,p); answer = (player_pungs(pcp,d) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_chows(pcp,d,Lower) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_chows(pcp,d,Middle) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_chows(pcp,d,Upper) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_pairs(pcp,d) && player_can_mah_jong(pcp,HiddenTile,flags)); goto done; } else { /* otherwise, for each concealed tile, try making something of it */ int i; for ( i = 0; i < p->num_concealed; i++ ) { t = p->concealed[i]; copy_player(pcp,p); answer = (player_forms_closed_pung(pcp,t) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_forms_closed_chow(pcp,t,Lower) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; copy_player(pcp,p); answer = (player_forms_closed_pair(pcp,t) && player_can_mah_jong(pcp,HiddenTile,flags)); if (answer) goto done; } } done: /* if all else fails, try 13 unique wonders */ if ( (! answer) && ((p->num_concealed == 13 && d != HiddenTile) || (p->num_concealed == 14 && d == HiddenTile))) answer = player_can_thirteen_wonders(p,d); return answer;}/* does the player have 13 unique wonders? */int player_can_thirteen_wonders(PlayerP p, Tile d) { Player pcopy; /* we will manipulate this copy of the player */ PlayerP pcp = &pcopy; int answer = 0; int i, havetwo, n; if ( d != HiddenTile ) { copy_player(pcp,p); answer = (player_draws_tile(pcp,d) && player_can_thirteen_wonders(pcp,HiddenTile)); } else { answer = 1; havetwo = 0; for ( i=0; i < 13; i++ ) { n = player_count_tile(p,thirteen_wonders[i]); if ( n > 2 ) { answer = 0; break; } else if ( n == 2 ) { if ( havetwo ) { answer = 0; break; } else { havetwo = 1; } } else if ( n == 0 ) { answer = 0; break; } } } return answer;}/* This internal function defines the mah jong hands. (Currently it does not include thirteen wonders). Last arg allows seven pairs etc.*/static int player_has_mah_jong(PlayerP p,MJSpecialHandFlags flags) { int i,s,n; int answer; p->err = NULL; /* all tiles must be in sets */ if ( p->num_concealed > 0 ) return 0; answer = 0; /* there must be exactly five non-empty sets, exactly one of which must be a pair */ for (i=0, n=0, s=0; i<MAX_TILESETS; i++) { if ( p->tilesets[i].type != Empty ) s++; if ( num_tiles_in_set(&(p->tilesets[i])) == 2) n++; } if ( s == 5 && n == 1 ) answer = 1; /* seven pairs may be allowed */ if ( (flags & MJSevenPairs) && s == 7 && n == 7 ) answer = 1; /* that's it */ return answer;} /* sort the player's concealed tiles.*/void player_sort_tiles(PlayerP p) { int nconc; int i,j; Tile *tp = p->concealed; Tile t; p->err = NULL; if ( pflag(p,Hidden) ) return; /* pretty dumb ... */ nconc = p->num_concealed; /* bubble sort */ for ( i = 0 ; i < nconc-1 ; i++ ) { if ( tp[i+1] < tp[i] ) { /* sink it to the bottom */ for ( j = i+1 ; tp[j] < tp[j-1] && j > 0 ; j-- ) { t = tp[j-1]; tp[j-1] = tp[j]; tp[j] = t; } } }}/* This function generates a string repn of a players tiles. */void player_print_tiles(char *buf, PlayerP p, int hide) { int i,j; Tile *tp; Tile t; Tile ctiles[MAX_CONCEALED]; int nconc; p->err = NULL; if ( pflag(p,Hidden) ) hide = 1; /* can't see the tiles anyway */ /* copy the concealed files and sort them. This assumes knowledge that Tiles are shorts. */ nconc = p->num_concealed; if ( ! hide ) { memcpy(ctiles,p->concealed,nconc*sizeof(Tile)); tp = ctiles; /* bubble sort */ for ( i = 0 ; i < nconc-1 ; i++ ) { if ( tp[i+1] < tp[i] ) { /* sink it to the bottom */ for ( j = i+1 ; tp[j] < tp[j-1] && j > 0 ; j-- ) { t = tp[j-1]; tp[j-1] = tp[j]; tp[j] = t; } } } } buf[0] = '\000' ; for (i=0; i < nconc; i++) { if ( i > 0 ) strcat(buf," "); strcat(buf, hide ? "--" : tile_code(ctiles[i])); } strcat(buf," *"); for (i = 0; i<MAX_TILESETS; i++) { if ( p->tilesets[i].type == Empty ) continue; strcat(buf," "); strcat(buf,tileset_string(&p->tilesets[i])); } strcat(buf," * "); for (i=0; i < p->num_specials; i++) { if ( i > 0 ) strcat(buf," "); strcat(buf,tile_code(p->specials[i])); }}int set_player_tiles(PlayerP p,char *desc) { int i = 0; char tc[3]; /* for tile codes */ int ts; int closed, annexed; Tile tile; TileSetType ttype; p->err = NULL; /* clear the current information */ p->num_concealed = 0; for (i=0; i < MAX_TILESETS ; i++) p->tilesets->type = Empty; p->num_specials = 0; pclearflag(p,Hidden); while ( *desc && isspace(*desc) ) desc++; /* read the tiles in hand */ while ( *desc && *desc != '*' ) { tc[0] = *(desc++); tc[1] = *(desc++); tc[2] = '\000'; tile = tile_decode(tc); if ( tile == ErrorTile ) { warn("format error in set_player_tile"); return 0; } if ( tile == HiddenTile ) { p->num_concealed++; psetflag(p,Hidden); } else { p->concealed[p->num_concealed++] = tile; } while ( *desc && isspace(*desc) ) desc++; } if ( ! *desc ) { warn("format error in set_player_tiles"); return 0; } desc++; /* skip the * */ while ( *desc && isspace(*desc) ) desc++; /* parse the tilesets */ ts = 0; while ( *desc && *desc != '*' ) { annexed = 0; closed = 0; tc[0] = *(desc++); tc[1] = *(desc++); tc[2] = 0; tile = tile_decode(tc); if ( tile == ErrorTile ) { warn("format error in set_player_tiles"); return 0; } if ( *(desc++) == '+' ) closed = 1; /* we're lazy now: if the next tile is different, we know it's a chow; otherwise we just skip to the next white space, counting letters */ tc[0] = *(desc++); tc[1] = *(desc++); if ( tile_decode(tc) != tile ) { ttype = closed ? ClosedChow : Chow; desc += 3; /* skip last tile */ } else if ( isspace(*desc) ) { /* two tiles; it's a pair */ ttype = closed ? ClosedPair : Pair; } else { /* skip next tile */ desc += 3; if ( isspace(*desc) ) { /* it's a pung */ ttype = closed ? ClosedPung : Pung; } else { /* it's a kong */ ttype = closed ? ClosedKong : Kong; /* millington rules ? */ if ( ! closed ) annexed = (*desc == '-'); desc += 3; } } p->tilesets[ts].type = ttype; p->tilesets[ts].tile = tile; p->tilesets[ts].annexed = annexed; ts++; while ( *desc && isspace(*desc) ) desc++; } /* end of matching tilesets loop */ if ( ! *desc ) { warn("format error in set_player_tiles"); return 0; } desc++; /* skip the * */ while ( *desc && isspace(*desc) ) desc++; /* parse the specials */ while ( *desc && *desc != '*' ) { tc[0] = *(desc++); tc[1] = *(desc++); tc[2] = '\000'; tile = tile_decode(tc); if ( tile == ErrorTile ) { warn("format error in set_player_tile"); return 0; } p->specials[p->num_specials++] = tile; while ( *desc && isspace(*desc) ) desc++; } /* phew */ return 1;}int player_shows_tiles(PlayerP p, char *desc) { char tc[3]; Tile tile; p->err = NULL; /* If the player is not hidden, then we needn't do anything to the tiles */ if ( pflag(p,Hidden) ) { /* clear the current information */ p->num_concealed = 0; pclearflag(p,Hidden); while ( *desc && isspace(*desc) ) desc++; /* read the tiles in hand */ while ( *desc ) { tc[0] = *(desc++); tc[1] = *(desc++); tc[2] = '\000'; tile = tile_decode(tc); if ( tile == ErrorTile ) { warn("format error in player_shows_tiles"); return 0; } p->concealed[p->num_concealed++] = tile; while ( *desc && isspace(*desc) ) desc++; } } psetflag(p,HandDeclared); return 1;}/* player_swap_tile: swaps the oldtile for the newtile in the concealed tiles. Only used in testing, of course */int player_swap_tile(PlayerP p, Tile oldtile, Tile newtile) { p->err = NULL; if ( ! remove_tile(p,oldtile) ) { p->err = "player doesn't have old tile"; return 0; } add_tile(p,newtile); return 1;}/* return string representing a tileset */char *tileset_string(TileSetP tp) { char *sep; static char buf[20]; short v; TileSuit s; int j; buf[0] = '\000'; sep = "-"; switch ( tp->type ) { case ClosedPung: case ClosedKong: case ClosedPair: sep = "+"; case Pung: case Kong: case Pair: for ( j = 0; j < num_tiles_in_set(tp); j++ ) { if ( tp->type == Kong && !tp->annexed && j == num_tiles_in_set(tp) - 1 ) sep = "+"; if ( j > 0 ) strcat(buf,sep); strcat(buf,tile_code(tp->tile)); } break; case ClosedChow: sep = "+"; case Chow: v = value_of(tp->tile); s = suit_of(tp->tile); strcat(buf,tile_code(tp->tile)); strcat(buf,sep); strcat(buf,tile_code(make_tile(s,v+1))); strcat(buf,sep); strcat(buf,tile_code(make_tile(s,v+2))); break; case Empty: /* errrr */ return ""; } return buf;}/* enum functions */#include "player-enums.c"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -