📄 player.c
字号:
/* $Header: /home/jcb/newmj/RCS/player.c,v 11.3 2002/03/30 22:49:22 jcb Rel $ * player.c * Implements functions on players. *//****************** COPYRIGHT STATEMENT ********************** * This file is Copyright (c) 2000 by J. C. Bradfield. * * Distribution and use is governed by the LICENCE file that * * accompanies this file. * * The moral rights of the author are asserted. * * * ***************** DISCLAIMER OF WARRANTY ******************** * This code is not warranted fit for any purpose. See the * * LICENCE file for further information. * * * *************************************************************/static const char rcs_id[] = "$Header: /home/jcb/newmj/RCS/player.c,v 11.3 2002/03/30 22:49:22 jcb Rel $";#define PLAYER_C /* so that PlayerP is not const */#include "player.h"#include "sysdep.h"/* At present, this function is not exported. If it becomes exported, the name should perhaps be made better. player_has_mah_jong: return true if the player's hand in its current state of organization is a complete mah-jong hand. (That is, the hand has been organized into four sets and a pair, etc.) The last arg is flags for seven pairs etc.*/static int player_has_mah_jong(PlayerP p,MJSpecialHandFlags flags);/* utility function: copy player record into (already allocated) space. Returns new (for consistency with memcpy) */PlayerP copy_player(PlayerP new,const PlayerP old) { return (PlayerP) memcpy((void *)new, (void *)old, sizeof(Player));} int num_tiles_in_set(TileSetP tp) { switch ( tp->type ) { case Empty: return 0; case Pair: case ClosedPair: return 2; case Chow: case ClosedChow: case Pung: case ClosedPung: return 3; case Kong: case ClosedKong: return 4; default: warn("num_tiles_in_set: unknown tile set type %d\n",tp->type); /* this will cause chaos, but that's the idea ... */ return -1; }}void initialize_player(PlayerP p) { p->err = NULL; p->id = 0; p->name = NULL; p->wind = UnknownWind; p->flags = 0; p->cumulative_score = 0; player_newhand(p,UnknownWind);}void player_newhand(PlayerP p,TileWind w) { int i; p->err = NULL; p->wind = w; p->num_concealed = 0; for ( i = 0; i < MAX_CONCEALED; i++ ) (p->concealed)[i] = HiddenTile; for ( i = 0; i < MAX_TILESETS; i++ ) { (p->tilesets)[i].type = Empty; (p->tilesets)[i].tile = HiddenTile; (p->tilesets)[i].annexed = 0; } p->num_specials = 0; for ( i = 0; i < 8; i++ ) (p->specials)[i] = HiddenTile; psetflag(p,Hidden); pclearflag(p,HandDeclared); pclearflag(p,MahJongged); psetflag(p,NoDiscard); pclearflag(p,Calling); pclearflag(p,OriginalCall); for (i=0; i < NUM_SEATS; i++) p->dflags[i] = 0; p->hand_score = -1;}void set_player_id(PlayerP p, int id) { p->err = NULL; p->id = id;}void set_player_name(PlayerP p, const char *n) { p->err = NULL; if ( p->name ) free(p->name); if ( n ) { p->name = (char *)malloc(strlen(n)+1); strcpy(p->name,n); } else p->name = (char *)0;}void set_player_cumulative_score(PlayerP p, int s) { p->err = NULL; p->cumulative_score = s;}void change_player_cumulative_score(PlayerP p, int d) { p->err = NULL; p->cumulative_score += d;}void set_player_hand_score(PlayerP p, int h) { p->err = NULL; p->hand_score = h;}void set_player_userdata(PlayerP p, void *ud) { p->err = NULL; p->userdata = ud;}/* utility functions. Maybe they should be exported. But we should probably adhere to the convention that exported functions do not leave data structures in intermediate states, as these do.*//* player_count_tile: looks for copies of t in p's concealed tiles. returns number found or -1 on error. Should not be called on unknown players.*/int player_count_tile(PlayerP p, Tile t) { int n,i; p->err = NULL; if ( pflag(p,Hidden) ) { p->err = "Can't count tiles of hidden player"; return -1; } for ( i = 0, n = 0 ; i < p->num_concealed ; i++ ) { if ( p->concealed[i] == t ) n++ ; } return n;}/* add_tile: adds t to p's concealed tiles. Should not be called on hidden players.*/static int add_tile(PlayerP p, Tile t) { assert(!pflag(p,Hidden)); /* nonsense to call this on hidden player */ assert(p->num_concealed < MAX_CONCEALED); p->concealed[p->num_concealed++] = t; return 1;}/* remove_tile: removes one instance of t from p's concealed tiles. Returns 1 on success, 0 on failure. Should not be called on hidden players.*/static int remove_tile(PlayerP p, Tile t) { int i; assert(!pflag(p,Hidden)); /* nonsense to call this on hidden player */ i = 0; while ( i < p->num_concealed && p->concealed[i] != t ) i++ ; if ( i == p->num_concealed ) return 0; while ( i+1 < p->num_concealed ) { p->concealed[i] = p->concealed[i+1]; i++ ; } p->num_concealed -= 1 ; return 1;}/* add_tileset: adds a tile set of type ty and tile t to p. Returns 1 on success, 0 on failure. Always sets the annexed flag to 0.*/static int add_tileset(PlayerP p, TileSetType ty, Tile t) { int s = 0; while ( s < MAX_TILESETS && p->tilesets[s].type != Empty ) s++ ; assert(s < MAX_TILESETS); /* serious problem if run out of slots */ p->tilesets[s].type = ty; p->tilesets[s].tile = t; p->tilesets[s].annexed = 0; return 1;}int player_draws_tile(PlayerP p, Tile t) { p->err = NULL; if ( p->num_concealed == 0 ) { if ( t == HiddenTile ) psetflag(p,Hidden); else pclearflag(p,Hidden); } if ( pflag(p,Hidden) ) p->num_concealed++; else { if ( t == HiddenTile ) { p->err = "player_draws_tile: HiddenTile, but we know the player!"; return 0; } if ( add_tile(p,t) == 0 ) { p->err = "player_draws_tile: add_tile failed"; return 0; } } return 1;}int player_draws_loose_tile(PlayerP p, Tile t) { p->err = NULL; if ( pflag(p,Hidden) ) p->num_concealed++; else { if ( t == HiddenTile ) { p->err = "player_draws_loose_tile: HiddenTile, but we know the player!"; return 0; } if ( add_tile(p,t) == 0 ) { p->err = "player_draws_loose_tile: add_tile failed"; return 0; } } return 1;}/* NOTE: it is a required feature of the following functions that if they fail, they leave the player structure unchanged. *//* implements the two following functions */static int int_pds(PlayerP p, Tile spec, int testonly) { p->err = NULL; if ( ! is_special(spec) ) { p->err = "player_declares_special: the special tile isn't special\n"; return 0; } /* paranoid, but why not? */ if ( p->num_specials == 8 ) { p->err = "player_declares_special: player already has all specials!"; return 0; } if ( pflag(p,Hidden) ) { if ( p->num_concealed < 1 ) { p->err = "player_can_declare_special: player has no tiles\n"; return 0; } if ( testonly ) return 1; p->specials[p->num_specials++] = spec; p->num_concealed--; return 1; } if ( player_count_tile(p,spec) < 1 ) return 0; if ( testonly ) return 1; /* now do it */ p->specials[p->num_specials++] = spec; remove_tile(p,spec); return 1;}int player_declares_special(PlayerP p, Tile spec) { return int_pds(p,spec,0);}int player_can_declare_special(PlayerP p, Tile spec) { return int_pds(p,spec,1);}/* implements the several pung functions. Determines whether p can use d to form a pung; and if not testonly, does it. If the last arg is 0, then d is a discard; otherwise, if the last arg is 1, d is also to be found in hand, and we are forming a closed pung (this is used in scoring).*/ static int int_pp(PlayerP p, Tile d, int testonly, int tileinhand) { p->err = NULL; if ( pflag(p,Hidden) ) { /* just assume it can be done */ if ( p->num_concealed < (tileinhand ? 3 : 2) ) { p->err = "player_pungs: can't pung with too few concealed tiles\n"; return 0; } if ( testonly ) return 1; p->num_concealed -= (tileinhand ? 3: 2); add_tileset(p,(tileinhand ? ClosedPung : Pung),d); if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1; } /* otherwise, we know the player, and need to check legality */ if ( player_count_tile(p,d) < (tileinhand ? 3 : 2) ) { p->err = "player_pungs: not enough matching tiles\n"; return 0; } /* OK, it's legal */ if ( testonly ) return 1; /* add the tileset */ add_tileset(p, (tileinhand ? ClosedPung : Pung), d); /* remove the tiles from the hand */ remove_tile(p,d); remove_tile(p,d); if ( tileinhand ) remove_tile(p,d); if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1;}int player_pungs(PlayerP p, Tile d) { return int_pp(p,d,0,0);}int player_can_pung(PlayerP p, Tile d) { return int_pp(p,d,1,0);} int player_forms_closed_pung(PlayerP p, Tile d) { return int_pp(p, d, 0, 1);}int player_can_form_closed_pung(PlayerP p, Tile d) { return int_pp(p, d, 1, 1);}/* likewise implements the several pair functions. Determines whether p can use d to form a pair; and if not testonly, does it. If the last arg is 0, then d is a discard; otherwise, if the last arg is 1, d is also to be found in hand, and we are forming a closed pair (this is used in scoring).*/ static int int_ppr(PlayerP p, Tile d, int testonly, int tileinhand) { p->err = NULL; if ( pflag(p,Hidden) ) { /* just assume it can be done */ if ( p->num_concealed < (tileinhand ? 2 : 1) ) { p->err = "player_pairs: can't pair with too few concealed tiles\n"; return 0; } p->num_concealed -= (tileinhand ? 2: 1); add_tileset(p,(tileinhand ? ClosedPair : Pair),d); if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1; } /* otherwise, we know the player, and need to check legality */ if ( player_count_tile(p,d) < (tileinhand ? 2 : 1) ) { p->err = "player_pairs: not enough matching tiles\n"; return 0; } /* OK, it's legal */ if ( testonly ) return 1; /* add the tileset */ add_tileset(p, (tileinhand ? ClosedPair : Pair), d); /* remove the tiles from the hand */ remove_tile(p,d); if ( tileinhand ) remove_tile(p,d); if ( p->num_concealed == 0 ) psetflag(p,HandDeclared); return 1;}int player_pairs(PlayerP p, Tile d) { return int_ppr(p,d,0,0);}int player_can_pair(PlayerP p, Tile d) { return int_ppr(p,d,1,0);}int player_forms_closed_pair(PlayerP p, Tile t) { return int_ppr(p,t,0,1);}int player_can_form_closed_pair(PlayerP p, Tile t) { return int_ppr(p,t,1,1);} /* implements several following functions; testonly and tileinhand as before */static int int_pk(PlayerP p, Tile d, int testonly, int tileinhand) { p->err = NULL; if ( pflag(p,Hidden) ) { /* just assume it can be done */ if ( p->num_concealed < (tileinhand ? 4 : 3) ) { p->err = "player_kongs: can't kong with too few concealed tiles\n"; return 0; } p->num_concealed -= (tileinhand ? 4 : 3); /* lose three/four, gain replacement */ add_tileset(p,(tileinhand ? ClosedKong : Kong), d); return 1; } /* otherwise, we know the player, and need to check legality */ if ( player_count_tile(p,d) < (tileinhand ? 4 : 3) ) { p->err = "player_kongs: not enough matching tiles\n"; return 0; } /* OK, it's legal */ if ( testonly ) return 1; /* add the tileset */ add_tileset(p,(tileinhand ? ClosedKong : Kong),d); /* remove the tiles from the hand */ remove_tile(p,d); remove_tile(p,d); remove_tile(p,d); if ( tileinhand ) remove_tile(p,d); return 1;}int player_kongs(PlayerP p, Tile d) { return int_pk(p,d,0,0);}int player_can_kong(PlayerP p, Tile d) { return int_pk(p, d, 1, 0);}int player_declares_closed_kong(PlayerP p, Tile d) { return int_pk(p, d, 0, 1);}int player_can_declare_closed_kong(PlayerP p, Tile d) { return int_pk(p, d, 1, 1);}/* Implements two following functions. */static int int_pap(PlayerP p, Tile t, int testonly) { int i; p->err = NULL; /* First check that the player has an exposed pung of the tile */ for ( i=0 ; i < MAX_TILESETS ; i++ ) { if ( p->tilesets[i].type == Pung && p->tilesets[i].tile == t ) break; } if ( i == MAX_TILESETS ) { p->err = "player_adds_to_pung called with no pung"; return 0; } /* now check (if poss) that the tile is in hand */ if ( pflag(p,Hidden) ) { /* can't see the tiles, so trust it */ if ( p->num_concealed < 1 ) { /* err, this is actually impossible */ p->err = "player_adds_to_pung: no concealed tiles"; return 0; } /* lose one concealed tile */ p->num_concealed--; p->tilesets[i].type = Kong; p->tilesets[i].annexed = 1; return 1; } /* otherwise, we know the player and need to check legality */ if ( player_count_tile(p,t) < 1 ) { p->err = "player_adds_to_pung: tile not found in hand"; return 0; } /* OK, it's legal */ if ( testonly ) return 1; /* modify the tileset */ p->tilesets[i].type = Kong; p->tilesets[i].annexed = 1; /* remove the tile from the hand */ remove_tile(p,t); return 1;}int player_adds_to_pung(PlayerP p, Tile t) { return int_pap(p,t,0);}int player_can_add_to_pung(PlayerP p,Tile t) { return int_pap(p,t,1);}/* player_kong_robbed: the player has formed a kong of t, and it is robbed */int player_kong_is_robbed(PlayerP p, Tile t) { int i; p->err = NULL; /* find the kong in question */ for ( i=0 ; i < MAX_TILESETS ; i++ ) { if ( p->tilesets[i].tile == t && ( p->tilesets[i].type == Kong || p->tilesets[i].type == ClosedKong ) ) break; } if ( i == MAX_TILESETS ) { p->err = "player_kong_is_robbed called with no kong"; return 0; } /* and downgrade it */ if ( p->tilesets[i].type == Kong ) p->tilesets[i].type = Pung ; else p->tilesets[i].type = ClosedPung; p->tilesets[i].annexed = 0; return 1;}/* the chow handling function */static int int_pc(PlayerP p, Tile d, ChowPosition r, int testonly, int tileinhand) { Tile low, mid, high; p->err = NULL; if ( ! is_suit(d) ) { p->err = "Can't chow a non-suit tile\n"; return 0; } if ( r == AnyPos ) { if ( !testonly ) { p->err = "Can't make a chow with AnyPos"; return 0; } return
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -