home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-386-Vol-2of3.iso
/
x
/
xhearts.zip
/
HEARTSD.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-01-10
|
58KB
|
2,285 lines
/* MIKEY: notes, todo */
/* shooting scoring messed up */
/* improve passing */
/* keep high cards for jd trick at end? only if don't have jd */
/* => take all tricks for jd at end if qs gone */
/* computer shoot attempt? */
/* jdvoided */
/*
* dealer - more commonly known as heartsd
*
* Smarts of hearts program. Runs in background when invoked by initial
* caller of hearts. Contains all logic for communicating with other
* hearts players. Handles all computer players, and communicates play
* of others to other players.
*
* JD variant addition and strategy mods by:
* Mike Yang of Silicon Graphics (mikey@sgi.com)
*
* Computer strategy originally written in Pascal by:
* Don Baccus of Oregon Software.
* Strategy mods by:
* Jeff Hemmerling of Test Systems Strategies, Inc.
*
* Converted strategy to C, added curses and multi-player (sockets)
* interface by:
* Bob Ankeney of Generic Computer Products
*
* Thanks to Keith Packard, for his invaluable (is that the same as
* unvaluable?) assistance as a unix guru, for design suggestions, and
* (I suppose) for convincing me to rewrite the bloody thing.
*
* Bug reports to Bob Ankeney at:
* ...!tektronix!reed!bob
*
*/
#include "misc.h"
#include "defs.h"
#include "local.h"
#include <sys/resource.h>
/* #define DEBUG uncomment to enable play logging */
#define BUF_SIZE 64
#define FUNNY_FACTOR 2
#define NPASS 3 /* # cards to pass */
#define PLAYERS 4 /* # players total */
#define NCARDS (52 / PLAYERS)
#define ACE 13
#define KING 12
#define QUEEN 11
#define JACK 10
#define HEARTS_PANIC 4
#define PANIC_RATIO 0.85
#define COMPUTER 1
#define HUMAN 2
#define VOYEUR 3
int debug = 0;
int num_humans, /* # HUMAN players */
human_type, /* HUMAN or VOYEUR */
player_mask; /* Bit n+1 is set for each human player n */
char someone_is_shooting,
queen_played,
jack_played;
typedef struct node *ptr;
struct card {
int suit, rank;
};
struct node {
ptr llink, rlink;
int rank;
};
struct suit_list {
ptr head, tail;
int length;
};
typedef struct suit_list card_hand[MAX_SUIT + 1];
struct player_status {
card_hand cards;
int pts, totalpts;
int passer;
struct card passed_cards[NPASS + 1];
int num_passed;
int player_kind;
int socket;
char name[9];
};
struct player_status card_table[PLAYERS + 1];
card_hand cards_played;
struct card deck[53];
struct sockaddr_in sockaddr;
int player_count, leader,
round, hand,
points_played;
int mikeyshooter,MIKEYJder,MIKEYJdintrick;
int privatemsgs = 1;
int voided[MAX_SUIT+1][PLAYERS+1], hasqs[PLAYERS+1][PLAYERS+1];
/* FALSE: No info, assume not
TRUE: Yes
MAYBE: Maybe, based on play */
#define MAYBE 2
#define NONE 666
FILE *fp;
char hearts_broken;
struct {
int card_count, highest_played, high_player, suit_led, pts;
char any_hearts;
struct card played[PLAYERS + 1];
} trick;
char *snames[] = {
"",
"clubs",
"diamonds",
"hearts",
"spades"
},
rnames[] = " 23456789TJQKA",
*comp_names[] = {
"",
"zeppo",
"chico",
"harpo",
"groucho"
};
#define map_player(leader, player) (((leader + player - 1) % PLAYERS) + 1)
#define queen_safe(player) (queen_played || (find_true(hasqs[player]) == FALSE))
#define safe_suit(suit) ((suit != HEARTS) && (find_true(voided[suit]) == FALSE))
#define highest(player, suit) (card_table[player].cards[suit].head->rlink)
#define lowest(player, suit) (card_table[player].cards[suit].tail->llink)
#define cards_out(suit) (MAX_RANK - cards_played[suit].length)
#define spades_safe(player) (( (float) (cards_out(SPADES) - \
card_table[player].cards[SPADES].length) / (PLAYERS - 1.0) >= 2.3) && \
safe_suit(SPADES))
#define diamonds_safe(player) (( (float) (cards_out(DIAMONDS) - \
card_table[player].cards[DIAMONDS].length) / (PLAYERS - 1.0) >= 2.3) && \
safe_suit(DIAMONDS))
#define clubs_safe(player) (( (float) (cards_out(CLUBS) - \
card_table[player].cards[CLUBS].length) / (PLAYERS - 1.0) >= 2.3) && \
safe_suit(CLUBS))
#define only_hearts(player) ((14 - round) == \
card_table[player].cards[HEARTS].length)
#define max(a,b) ((a > b) ? a : b)
rnd(num)
{
extern long random();
return (random() % num);
}
init_deck()
{
int suit_temp, rank_temp, j;
j = 0;
for (rank_temp = MIN_RANK; rank_temp <= MAX_RANK ; rank_temp++)
for (suit_temp = CLUBS; suit_temp <= SPADES; suit_temp++) {
deck[++j].suit = suit_temp;
deck[j].rank = rank_temp;
}
}
init_suit(list)
struct suit_list *list;
{
char *malloc();
ptr p, p1;
list->length = 0;
p = list->head = (ptr) malloc(sizeof(*p));
p1 = list->tail = (ptr) malloc(sizeof(*p1));
p->llink = NULL;
p1->rlink = NULL;
p->rlink = p1;
p1->llink = p;
p->rank = MAX_RANK + 1;
p1->rank = 0;
}
init()
{
int player;
srandom(getpid()); /* Init random number gen */
for (player = 1; player <= PLAYERS; player++) {
card_table[player].player_kind = COMPUTER;
(void) strcpy(card_table[player].name, comp_names[player]);
card_table[player].socket = -1;
}
num_humans = 0;
}
new_game()
{
int suit, player;
init_deck();
for (player = 1; player <= PLAYERS; player++) {
card_table[player].totalpts = 0;
for (suit = CLUBS; suit <= SPADES; suit++)
init_suit(&card_table[player].cards[suit]);
}
for (suit = CLUBS; suit <= SPADES; suit++)
init_suit(&cards_played[suit]);
}
get_rank(rch)
char rch;
{
int i;
for (i = 1; i <= MAX_RANK; i++)
if (rch == rnames[i])
return(i);
return(0);
}
get_suit(sch)
char sch;
{
int i;
for (i = 1; i <= MAX_SUIT; i++)
if (sch == *snames[i])
return(i);
return(0);
}
char *
get_name(player_num)
int player_num;
{
int i;
char unique_name = TRUE;
static char pname[16+64];
for (i = 1; i <= PLAYERS; i++)
if ((i != player_num) &&
!strcmp(card_table[i].name, card_table[player_num].name))
unique_name = FALSE;
if (unique_name)
(void) strcpy(pname, card_table[player_num].name);
else
(void) sprintf(pname, "%s (%d)", card_table[player_num].name,
player_num);
if (strlen(pname) >= 16) {
pname[15] = '\0';
}
return(pname);
}
/*
* Get a card from somebody. get_mask is a bit mask specifying who is
* acceptable to get a card from (i.e., bit n states player n can send
* a card.) Scans all sockets for input, handling messages and player
* leaving signals. Returns when one of the specified players passes
* a card. Returns 0 if one of the specified players exits the game.
*/
get_card(get_mask, from, buf)
int get_mask, *from;
char *buf;
{
int i, player, ret;
char mesg_buf[64+16];
fd_type read_fd;
for (;;) {
fd_init(4, &read_fd); /* Watch for new arrivals */
for (player = 1; player <= PLAYERS; player++)
if (card_table[player].player_kind != COMPUTER)
fd_set(card_table[player].socket, &read_fd);
if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
(struct timeval *) 0)) {
if (fd_isset(4, read_fd)) /* New arrival? */
add_player();
for (player = 1; player <= PLAYERS; player++) /* This player send? */
if (fd_isset(card_table[player].socket, read_fd)) {
ret = read_socket(card_table[player].socket, buf);
*from = player;
if (ret && (buf[0] == 'M')) {
(void) sprintf(mesg_buf, "M%d%s: %s", MESG_WINDOW,
get_name(player), buf + 1);
if (strlen(mesg_buf) >= 64) {
mesg_buf[63] = '\0';
}
send_to_all(mesg_buf); /* Message to everyone */
}
else if (ret && (buf[0] == '~')) {
if (privatemsgs)
privatemsgs = FALSE;
else
privatemsgs = TRUE;
}
else if (ret && (buf[0] == ';')) {
if ((1 <= buf[1]-'0') &&
privatemsgs &&
(buf[1]-'0' <= PLAYERS) &&
(card_table[buf[1]-'0'].player_kind != COMPUTER)) {
(void) sprintf(mesg_buf, "M%d%s; %s", MESG_WINDOW,
get_name(player), buf + 2);
if (strlen(mesg_buf) >= 64) {
mesg_buf[63] = '\0';
}
send_buf(buf[1]-'0',mesg_buf);
send_buf(player,mesg_buf);
}
}
else {
if (!ret) {
if (card_table[player].player_kind == HUMAN)
player_mask &= ~(1 << player); /* player went away */
card_table[player].player_kind = COMPUTER;
(void) close(card_table[player].socket);
if (--num_humans == 0)
exit (0); /* give up if nobody wants to play (sniff) */
for (i = 1; i <= PLAYERS; i++) {
if (card_table[i].player_kind != COMPUTER) {
(void) sprintf(buf,
"M%d%s vanishes in a puff of greasy black smoke!",
MESG_WINDOW, get_name(player)); /* Announce deletion */
send_buf(i, buf); /* to others */
(void) sprintf(buf,
/*
* Inform dealer.
*/
"p%d<empty>", player - 1);
write_socket(3, buf);
}
}
(void) strcpy(card_table[player].name, comp_names[player]);
send_all_totals();
}
if (get_mask & (1 << player))
return(ret);
}
}
}
}
}
toss_card(player, card_to_toss, suit_to_toss)
int player, suit_to_toss;
ptr card_to_toss;
{
char buf[BUF_SIZE];
(void) sprintf(buf, "R%c%c",
rnames[card_to_toss->rank], *snames[suit_to_toss]);
send_buf(player, buf); /* Tell player to remove card */
}
read_card(read_mask, player, card_to_play, suit_to_play)
int read_mask, *player, *suit_to_play;
ptr *card_to_play;
{
char buf[BUF_SIZE];
int rank, suit;
ptr p;
char rank_ch, suit_ch;
if (get_card(read_mask, player, buf)) {
do {
rank_ch = buf[1];
if (rank_ch == '?') {
if (round == 0)
get_pass(*player, card_table[*player].num_passed+1,
&p, &suit);
else
if (leader == *player)
computer_lead(&p,&suit);
else
computer_pick(*player,&p,&suit);
erase_window(*player, TEXT_WINDOW);
} else {
suit_ch = buf[2];
rank = get_rank(rank_ch);
suit = get_suit(suit_ch);
erase_window(*player, TEXT_WINDOW);
p = card_table[*player].cards[suit].head->rlink;
while (p && (p->rank != rank))
p = p->rlink;
if (p == NULL) {
(void) sprintf(buf, "M%dPlay a card you have:",
TEXT_WINDOW);
send_buf(*player, buf);
send_buf(*player, "G");
if (!get_card(read_mask, player, buf))
return(0);
}
}
}
while (p == NULL);
*card_to_play = p;
*suit_to_play = suit;
return(1);
}
return(0);
}
send_card(player, rank, suit)
int player, rank, suit;
{
char buf[BUF_SIZE];
(void) sprintf(buf, "A%c%c", rnames[rank], *snames[suit]);
send_buf(player, buf);
}
/*
* send cards in hand to player
*/
send_hand(player)
int player;
{
int suit;
ptr p;
for (suit = CLUBS; suit <= SPADES; suit++) { /* send cards in hand */
p = card_table[player].cards[suit].head->rlink;
while (p->rank) {
send_card(player, p->rank, suit);
p = p->rlink;
}
}
}
get_first_player()
{
fd_type read_fd; /* main socket bit mask */
/*
* wait for new player to show up
*/
fd_init(4, &read_fd); /* Wait for someone to show up */
if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
(struct timeval *) 0) == -1) {
perror("select");
exit(1);
}
}
add_player()
{
int new_socket; /* new file descriptor */
char pname[128], buf[128];
struct sockaddr_in sockad;
int ssize; /* to make accept happy */
int new_player = 0;
int i;
/*
* add whoever's waiting
*/
ssize = sizeof (sockad);
if ((new_socket = accept(4, &sockad, &ssize)) == -1) {
perror("accept");
exit(1);
}
/* get user name */
if (read_socket(new_socket, pname) > 0) {
pname[8] = '\0'; /* Name must be less than 9 chars */
for (i = PLAYERS; i >= 1; i--)
if (card_table[i].player_kind == COMPUTER)
new_player = i;
}
if (new_player) {
/*
* Inform distributor
*/
(void) sprintf(buf, "p%d%s", new_player - 1, pname);
write_socket(3, buf);
card_table[new_player].player_kind = human_type;
card_table[new_player].socket = new_socket;
(void) strcpy(card_table[new_player].name, pname);
++num_humans;
if (human_type == VOYEUR) {
(void) sprintf(buf,
"M%d Game in progress...", PLAY_WINDOW);
send_buf(new_player, buf);
(void) sprintf(buf,
"M%dYou may watch till next hand.", MESG_WINDOW);
send_buf(new_player, buf);
}
else
player_mask |= 1 << new_player;
for (i = 1; i <= PLAYERS; i++) {
if (card_table[i].player_kind != COMPUTER) {
(void) sprintf(buf,
"M%d%s added as player %d", MESG_WINDOW,
pname, new_player);
if (i != new_player) /* Announce addition */
send_buf(i, buf);
send_totals(i);
}
}
send_hand(new_player);
}
else {
(void) sprintf(buf, "M%dAll seats are full!", MESG_WINDOW);
write_socket(new_socket, buf);
write_socket(new_socket, "Z");
(void) close(new_socket);
}
}
erase_window(player, which_window)
int player, which_window;
{
char buf[BUF_SIZE];
(void) sprintf(buf, "E%d", which_window);
send_buf(player, buf);
}
erase_all_window(which_window)
int which_window;
{
int player;
for (player = 1; player <= PLAYERS; player++)
if (card_table[player].player_kind != COMPUTER)
erase_window(player, which_window);
}
send_totals(player)
int player;
{
int i;
char buf[BUF_SIZE];
if (card_table[player].player_kind != COMPUTER) {
for (i = 1; i <= PLAYERS; i++) {
(void) sprintf(buf, "S%d%03d%03d%s", i,
card_table[i].pts,
card_table[i].totalpts, card_table[i].name);
send_buf(player, buf);
}
}
}
send_all_totals()
{
int player;
for (player = 1; player <= PLAYERS; player++)
send_totals(player);
}
send_all_winner()
{
int max_score,
player, shooter;
char buf[BUF_SIZE];
char mikeysh;
(void) sprintf(buf, "M%d--------------------------------------------------------", MESG_WINDOW);
send_to_all(buf);
max_score = 0;
mikeysh = TRUE;
for (player = 1; player <= PLAYERS; player++) {
if ((max_score != 0) &&
(card_table[player].pts != 0) &&
(card_table[player].pts != -10))
mikeysh = FALSE;
if ((card_table[player].pts > max_score) &&
(card_table[player].pts != -10) &&
(card_table[player].pts != 0)) {
max_score = card_table[player].pts;
shooter = player;
}
}
mikeysh = mikeysh && (mikeyshooter == shooter);
if (mikeysh) {
for (player = 1; player <= PLAYERS; player++) {
if (player != shooter)
card_table[player].totalpts += 26;
if ((player == MIKEYJder) && MIKEYJ)
card_table[player].totalpts -= 10;
}
}
else {
for (player = 1; player <= PLAYERS; player++)
card_table[player].totalpts += card_table[player].pts;
}
if (mikeysh)
(void) sprintf(buf, "M %s wins by shooting the moon!",
get_name(shooter));
else {
get_winner(buf, FALSE);
(void) strcat(buf, ".");
}
buf[1] = MESG_WINDOW + '0';
send_to_all(buf);
}
send_final_winner()
{
char buf[64];
get_winner(buf, TRUE);
buf[1] = PLAY_WINDOW + '0';
(void) strcat(buf, " total!");
erase_all_window(PLAY_WINDOW);
send_to_all(buf);
}
/*
* Get buffer of form: "M groucho wins with nn points"
* or: "M groucho and chico tie with nn points"
*/
get_winner(buf, use_totalpts)
char *buf;
char use_totalpts;
{
int player, winning_score, temp,
scores[PLAYERS + 1], players[PLAYERS + 1];
char sorted;
for (player = 1; player <= PLAYERS; player++) {
scores[player] = use_totalpts ? card_table[player].totalpts
: card_table[player].pts;
players[player] = player;
}
do { /* Bubble sort the bloody thing */
sorted = TRUE;
for (player = 1; player < PLAYERS; player++)
if (scores[player + 1] < scores[player]) {
temp = scores[player];
scores[player] = scores[player + 1];
scores[player + 1] = temp;
temp = players[player];
players[player] = players[player + 1];
players[player + 1] = temp;
sorted = FALSE;
}
}
while (!sorted);
winning_score = scores[1];
(void) sprintf(buf, "M %s", card_table[players[1]].name);
for (player = 2;
(player <= PLAYERS) && (scores[player] == winning_score); player++)
(void) sprintf(buf + strlen(buf), " and %s",
card_table[players[player]].name);
if (scores[2] == winning_score)
(void) sprintf(buf + strlen(buf),
" tie with %d point", winning_score);
else
(void) sprintf(buf + strlen(buf),
" wins with %d point", winning_score);
if (winning_score != 1)
(void) strcat(buf, "s");
}
send_buf(player, buf)
int player;
char *buf;
{
write_socket(card_table[player].socket, buf);
}
send_to_all(buf)
char *buf;
{
int player;
for (player = 1; player <= PLAYERS; player++)
if (card_table[player].player_kind != COMPUTER)
send_buf(player, buf);
}
new_round()
{
char buf[BUF_SIZE];
int mikey;
for(mikey = 1;mikey <= PLAYERS;mikey++) {
trick.played[mikey].rank = 0;
trick.played[mikey].suit = 0;
}
for(mikey=CLUBS; mikey<=SPADES; mikey++) {
if (cards_out(mikey) <= 1.5*PLAYERS) {
int each;
for (each=1; each<=PLAYERS; each++) {
if (voided[mikey][each] == FALSE) {
voided[mikey][each] = MAYBE;
}
}
}
}
trick.pts = 0;
trick.any_hearts = FALSE;
player_count = 0;
erase_all_window(PLAY_WINDOW);
/*
* Inform distributor
*/
(void) sprintf(buf, "r%d", round);
write_socket(3, buf);
(void) sprintf(buf, "M%dHand: %d Round: %d",
ROUND_WINDOW, hand, round);
send_to_all(buf);
}
enter_card(which_card, which_hand)
struct card which_card;
card_hand which_hand;
{
ptr p, p1;
p = which_hand[which_card.suit].head;
++which_hand[which_card.suit].length;
while (p->rank > which_card.rank)
p = p->rlink;
p1 = (ptr) malloc(sizeof(*p1));
p1->llink = p->llink;
p1->llink->rlink = p1;
p->llink = p1;
p1->rlink = p;
p1->rank = which_card.rank;
}
remove_node(p)
ptr p;
{
p->llink->rlink = p->rlink;
p->rlink->llink = p->llink;
free((char *) p);
}
clear_hand(which_hand)
card_hand which_hand;
{
int suit;
ptr p, p1;
for (suit = CLUBS; suit <= SPADES; suit++) {
which_hand[suit].length = 0;
p = which_hand[suit].head->rlink;
while (p->rank > 0) {
p1 = p;
p = p->rlink;
remove_node(p1);
}
}
}
shuffle()
{
int j, k;
struct card t;
for (j = 52; j >= 1; j--) {
k = rnd(j) + 1;
t = deck[k];
deck[k] = deck[j];
deck[j] = t;
}
}
deal()
{
int i, j, player;
for (player = 1; player <= PLAYERS; player++) {
j = NCARDS * (player - 1);
for (i = 1; i <= NCARDS; i++)
enter_card(deck[i+j], card_table[player].cards);
if (card_table[player].player_kind != COMPUTER)
send_hand(player);
}
}
new_hand()
{
int player;
char buf[BUF_SIZE];
mikeyshooter = 0;
MIKEYJder = 0;
MIKEYJdintrick = FALSE;
points_played = 0;
someone_is_shooting = FALSE;
jack_played = queen_played = hearts_broken = FALSE;
trick.suit_led = CLUBS;
for (player = 1; player <= PLAYERS; player++) {
int each;
card_table[player].pts = 0;
clear_hand(card_table[player].cards);
voided[CLUBS][player] = voided[DIAMONDS][player] =
voided[HEARTS][player] = voided[SPADES][player] = FALSE;
for (each=1; each<=PLAYERS; each++) {
hasqs[player][each] = TRUE;
}
}
clear_hand(cards_played);
erase_all_window(PLAY_WINDOW);
send_all_totals();
/*
* Inform distributor
*/
(void) sprintf(buf, "h%d", hand);
write_socket(3, buf);
write_socket(3, "r0");
(void) sprintf(buf, "M%dNew hand...", ROUND_WINDOW);
send_to_all(buf);
shuffle(); /* Shuffle three times! */
shuffle(); /* (just like vegas!) */
shuffle();
deal();
}
ptr
find_card(player,rank,suit)
int player,rank,suit;
{
ptr p;
p = card_table[player].cards[suit].head->rlink;
while (p && p->rank)
if (p->rank == rank)
return(p);
else
p = p->rlink;
return(NULL);
}
char
not_in_trick(rank,suit)
int rank,suit;
{
int mikey;
for(mikey = 1;mikey <= PLAYERS;mikey++) {
if ((trick.played[mikey].rank == rank) &&
(trick.played[mikey].suit == suit))
return(FALSE);
}
return(TRUE);
}
ptr
effective_high(player,rank,suit)
int player,rank,suit;
{
ptr card,p,q;
int r;
r = rank-1;
card = find_card(player,rank,suit);
p = card->rlink;
q = cards_played[suit].head->rlink;
while (q->rank > r)
q = q->rlink;
while (r && (!MIKEYJ || (suit != DIAMONDS) || (r != JACK)) &&
((suit != SPADES) || (r != QUEEN)) &&
((p->rank == r) || ((q->rank == r) &&
(not_in_trick(r,suit))))) {
if (p->rank == r) {
card = p;
p = p->rlink;
} else {
q = q->rlink;
}
r--;
}
return(card);
}
ptr
effective_low(player,rank,suit)
int player,rank,suit;
{
ptr card,p,q;
int r;
r = rank+1;
card = find_card(player,rank,suit);
p = card->llink;
q = cards_played[suit].tail->llink;
while (q->rank < r)
q = q->llink;
while ((r <= MAX_RANK) &&
(!MIKEYJ || (suit != DIAMONDS) || (r != JACK)) &&
((suit != SPADES) || (r != QUEEN)) &&
((p->rank == r) || ((q->rank == r) &&
(not_in_trick(r,suit))))) {
if (p->rank == r) {
card = p;
p = p->llink;
} else {
q = q->llink;
}
r++;
}
return(card);
}
ptr
next_highest(player, rank, suit_to_play)
int player, rank, suit_to_play;
{
ptr p;
p = card_table[player].cards[suit_to_play].head->rlink;
while (p && p->rank)
if (p->rank < rank)
return(effective_high(player,p->rank,suit_to_play));
else
p = p->rlink;
return(NULL);
}
int
highest_unplayed(suit, player)
int suit, player;
{
ptr p, q;
int last;
p = cards_played[suit].head->rlink;
q = card_table[player].cards[suit].head->rlink;
last = MAX_RANK+1;
while (last) {
if ((p->rank != last-1) && (q->rank != last-1)) {
return(last-1);
} else {
if (q->rank == last-1) {
q = q->rlink;
} else {
p = p->rlink;
}
last--;
}
}
return(0);
}
int
lowest_unplayed(suit, player)
int suit, player;
{
ptr p, q;
int last;
p = cards_played[suit].tail->llink;
q = card_table[player].cards[suit].tail->llink;
last = 0;
while (last != MAX_RANK) {
if ((p->rank != last+1) && (q->rank != last+1)) {
return(last+1);
} else {
if (q->rank == last+1) {
q = q->rlink;
} else {
p = p->rlink;
}
last++;
}
}
return(MAX_RANK+1);
}
int
count_losers(rank, suit)
int rank, suit;
{
ptr p;
int losers;
p = cards_played[suit].head->rlink;
losers = 0;
while (p->rank) {
if (p->rank < rank)
losers++;
p = p->rlink;
}
return(losers);
}
int
find_true(array)
int array[PLAYERS+1];
{
int each, result;
result = FALSE;
for (each=trick.card_count+1; each<=PLAYERS; each++) {
if (array[map_player(leader,each)] == TRUE)
result = TRUE;
if ((array[map_player(leader,each)] == MAYBE) && (result != TRUE))
result = MAYBE;
}
return(result);
}
int
count_array(array, value)
int array[PLAYERS+1], value;
{
int each, result;
result = 0;
for (each=1; each<=PLAYERS; each++) {
if (array[each] == value)
result++;
}
return(result);
}
int
find_false(array)
int array[PLAYERS+1];
{
int each, result;
result = TRUE;
for (each=trick.card_count+1; each<=PLAYERS; each++) {
if (array[map_player(leader,each)] == FALSE)
result = FALSE;
if ((array[map_player(leader,each)] == MAYBE) && (result != FALSE))
result = MAYBE;
}
return(result);
}
ptr
effective_highest(player, suit)
int player, suit;
{
ptr p;
p = highest(player,suit);
if (p->rank)
return(effective_high(player,p->rank,suit));
else
return(p);
}
ptr
effective_lowest(player, suit)
int player, suit;
{
ptr p;
p = lowest(player,suit);
if (p->rank != MAX_RANK+1)
return(effective_low(player,p->rank,suit));
else
return(p);
}
int
lowestp(rank,suit)
int rank, suit;
{
ptr p;
int each;
p = cards_played[suit].tail->llink;
for (each=1; each<rank; each++) {
if (p->rank == each) {
p = p->llink;
} else {
return(FALSE);
}
}
return(TRUE);
}
find_high_card(player, card_to_play, suit_to_play, play_points, play_spades, play_diamonds)
int player, *suit_to_play;
char play_points, play_spades, play_diamonds;
ptr *card_to_play;
{
int high_card, suit;
ptr p;
high_card = -1;
*card_to_play = NULL;
for (suit = CLUBS; suit <= SPADES; suit++) {
p = card_table[player].cards[suit].head->rlink;
if ((suit == DIAMONDS) && (p->rank >= JACK) && MIKEYJ) {
p=p->rlink;
}
if ((p->rank != 0) &&
(p->rank-count_losers(p->rank,suit) > high_card) &&
(play_spades ||
(suit != SPADES)) &&
(play_diamonds ||
(suit != DIAMONDS)) &&
(play_points ||
(point_value(p->rank, suit) == 0))) {
high_card = p->rank-count_losers(p->rank,suit);
*card_to_play = p;
*suit_to_play = suit;
}
}
if (*card_to_play == NULL) {
/*
* No clubs or diamonds. Q spades is highest spade, or no
* spades at all. Try to play next highest spade.
*/
/*
* May have Jack of Diamonds also, if MIKEYJ
*/
if (play_spades)
*card_to_play = next_highest(player, QUEEN, SPADES);
if (*card_to_play)
*suit_to_play = SPADES;
else if (play_points &&
(card_table[player].cards[HEARTS].length)) {
/* Play highest heart */
*card_to_play = effective_highest(player, HEARTS);
*suit_to_play = HEARTS;
}
/* otherwise, try lowest diamond */
if ((*card_to_play == NULL) &&
card_table[player].cards[DIAMONDS].length) {
*card_to_play = lowest(player, DIAMONDS);
*suit_to_play = DIAMONDS;
}
if ((*card_to_play == NULL) &&
card_table[player].cards[SPADES].length) {
*card_to_play = effective_highest(player, SPADES);
*suit_to_play = SPADES;
}
/* shouldn't ever need the following
if (*card_to_play == NULL) {
*card_to_play = effective_highest(player, CLUBS);
*suit_to_play = CLUBS;
if (((*card_to_play)->rank) == 0)
*card_to_play = NULL;
}
*/
}
}
find_low_card(player, card_to_play, suit_to_play)
int player, *suit_to_play;
ptr *card_to_play;
{
int low_card, suit;
ptr p;
low_card = MAX_RANK + 1;
*card_to_play = NULL;
for (suit = CLUBS; suit <= SPADES; suit++) {
p = card_table[player].cards[suit].tail->llink;
if (MIKEYJ && (p->rank == JACK) && (suit == DIAMONDS))
p = p->llink;
if ((p->rank < low_card) &&
(point_value(p->rank, suit) == 0)) {
low_card = p->rank;
*card_to_play = p;
*suit_to_play = suit;
}
}
if (*card_to_play == NULL) {
*suit_to_play = HEARTS;
*card_to_play = card_table[player].cards[HEARTS].tail->llink;
if ((*card_to_play)->rank == MAX_RANK+1) {
*suit_to_play = DIAMONDS;
*card_to_play = find_card(player,JACK,DIAMONDS);
}
if (!(*card_to_play) || (*card_to_play)->rank == MAX_RANK+1) {
*suit_to_play = SPADES;
*card_to_play = find_card(player,QUEEN,SPADES);
}
}
}
find_low_club(leader)
int *leader;
{
int player, low_club, rank;
low_club = MAX_RANK;
for (player = 1; player <= PLAYERS; player++)
if ((rank = card_table[player].cards[CLUBS].tail->llink->rank) < low_club) {
*leader = player;
low_club = rank;
}
}
find_queen(player, card_to_play)
int player;
ptr *card_to_play;
{
ptr p;
*card_to_play = NULL;
p = card_table[player].cards[SPADES].head->rlink;
while (p)
if (p->rank == QUEEN) {
*card_to_play = p;
p = NULL;
}
else
p = p->rlink;
}
print_pass(player)
int player;
{
int i;
char buf[BUF_SIZE];
struct card cd;
erase_window(player, PLAY_WINDOW);
(void) sprintf(buf, "M%d%s passes you:", LEAD_WINDOW,
get_name(card_table[player].passer));
send_buf(player, buf);
for (i = 1; i <= NPASS; i++) {
cd = card_table[player].passed_cards[i];
send_card(player, cd.rank, cd.suit);
(void) sprintf(buf, "P%d%c%c", i, rnames[cd.rank], *snames[cd.suit]);
send_buf(player, buf);
}
}
/*
* Get card to pass from computer player
*/
get_pass(from, which_pass, card_to_pass, suit_to_pass)
int from, which_pass, *suit_to_pass;
ptr *card_to_pass;
{
ptr p1;
int each;
p1 = highest(from, SPADES);
if ((((p1->rank > QUEEN) &&
(!find_card(from,QUEEN,SPADES) ||
(card_table[from].cards[SPADES].length <= 4))) ||
((p1->rank == QUEEN) &&
(card_table[from].cards[SPADES].length <= 4))) &&
card_table[from].cards[SPADES].length) {
*card_to_pass = p1;
*suit_to_pass = SPADES;
if (p1->rank == QUEEN) {
/* remember who has the QS */
for (each=1; each<=PLAYERS; each++) {
hasqs[from][each] = FALSE;
}
hasqs[from][map_player(from, hand)] = TRUE;
}
} else if (card_table[from].cards[DIAMONDS].length &&
!find_card(from,JACK,DIAMONDS) &&
(highest(from,DIAMONDS)->rank >= 4) &&
(card_table[from].cards[DIAMONDS].length <= NPASS-which_pass+1)) {
/* create void in DIAMONDS */
*card_to_pass = highest(from, DIAMONDS);
*suit_to_pass = DIAMONDS;
} else if (card_table[from].cards[CLUBS].length &&
(highest(from,CLUBS)->rank >= 4) &&
(card_table[from].cards[CLUBS].length <= NPASS-which_pass+1)) {
/* create void in CLUBS */
*card_to_pass = highest(from, CLUBS);
*suit_to_pass = CLUBS;
} else if (card_table[from].cards[HEARTS].length &&
(highest(from,HEARTS)->rank >= 7) &&
(card_table[from].cards[HEARTS].length <= NPASS-which_pass+1)) {
/* create void in HEARTS */
*card_to_pass = highest(from, HEARTS);
*suit_to_pass = HEARTS;
} else if (card_table[from].cards[HEARTS].length &&
((card_table[from].cards[HEARTS].length <= 2) ||
((card_table[from].cards[HEARTS].length == 3) &&
(highest(from,HEARTS)->rlink->rank > 8))) &&
(highest(from,HEARTS)->rank > JACK)) {
/* get rid of high HEART */
*card_to_pass = highest(from, HEARTS);
*suit_to_pass = HEARTS;
} else if ((card_table[from].cards[CLUBS].length > 2) &&
(lowest(from,CLUBS)->rank >= 8)) {
/* get rid of high CLUBS */
*card_to_pass = highest(from, CLUBS);
*suit_to_pass = CLUBS;
} else if ((card_table[from].cards[HEARTS].length > 2) &&
(lowest(from,HEARTS)->rank >= 8)) {
/* get rid of high HEARTS */
*card_to_pass = highest(from, HEARTS);
*suit_to_pass = HEARTS;
} else if (card_table[from].cards[CLUBS].length &&
(highest(from,CLUBS)->rank >= 7) &&
(card_table[from].cards[CLUBS].length <= NPASS-which_pass+2)) {
/* create almost void in CLUBS */
*card_to_pass = find_card(from,1,CLUBS);
if (!*card_to_pass)
*card_to_pass = highest(from, CLUBS);
*suit_to_pass = CLUBS;
} else if (card_table[from].cards[HEARTS].length &&
(card_table[from].cards[HEARTS].length >= 5) &&
(highest(from,HEARTS)->rlink->rank >= 7)) {
/* get rid of some HEARTS */
if (highest(from, HEARTS)->rank >= JACK)
*card_to_pass = highest(from, HEARTS)->rlink;
else
*card_to_pass = highest(from, HEARTS);
*suit_to_pass = HEARTS;
} else if (card_table[from].cards[CLUBS].length &&
(card_table[from].cards[CLUBS].length >= 5)) {
/* get rid of some CLUBS */
*card_to_pass = find_card(from,1,CLUBS);
if (!*card_to_pass)
*card_to_pass = highest(from, CLUBS);
*suit_to_pass = CLUBS;
} else if (card_table[from].cards[DIAMONDS].length &&
!find_card(from,JACK,DIAMONDS) &&
(card_table[from].cards[DIAMONDS].length >= 5)) {
/* get rid of some DIAMONDS */
*card_to_pass = highest(from, DIAMONDS);
*suit_to_pass = DIAMONDS;
} else if (card_table[from].cards[SPADES].length &&
!find_card(from,QUEEN,SPADES) &&
(card_table[from].cards[SPADES].length >= 5)) {
/* get rid of some SPADES */
*card_to_pass = highest(from, SPADES);
*suit_to_pass = SPADES;
} else if (find_card(from,1,CLUBS)) {
*card_to_pass = find_card(from,1,CLUBS);
*suit_to_pass = CLUBS;
} else {
find_high_card(from, card_to_pass, suit_to_pass, FALSE, TRUE, TRUE);
}
}
/*
* Pass card to player
*/
pass_to(from, who_to, which_pass, card_to_pass, suit_to_pass)
int from, who_to, which_pass;
ptr card_to_pass;
char suit_to_pass;
{
char buf[BUF_SIZE];
card_table[who_to].passed_cards[which_pass].rank = card_to_pass->rank;
card_table[who_to].passed_cards[which_pass].suit = suit_to_pass;
--card_table[from].cards[suit_to_pass].length;
card_table[who_to].passer = from;
if (card_table[from].player_kind != COMPUTER) {
erase_window(from, INP_WINDOW);
(void) sprintf(buf, "P%d%c%c", which_pass,
rnames[card_to_pass->rank], *snames[suit_to_pass]);
send_buf(from, buf);
toss_card(from, card_to_pass, suit_to_pass);
}
remove_node(card_to_pass);
}
player_pass(player)
int player;
{
char buf[BUF_SIZE];
(void) sprintf(buf, "M%dPass %d to %s:",
TEXT_WINDOW, NPASS, get_name(map_player(player, hand)));
send_buf(player, buf);
(void) sprintf(buf, "M%dCard 1:", LEAD_WINDOW);
send_buf(player, buf);
send_buf(player, "G");
}
pass_cards()
{
int player, pass, suit, old_mask;
int passed_mask = 0;
ptr p;
char buf[BUF_SIZE];
human_type = HUMAN;
for (player = 1; player <= PLAYERS; player++) {
card_table[player].num_passed = 0;
if (card_table[player].player_kind == HUMAN)
player_pass(player);
}
while (player_mask != passed_mask) {
old_mask = player_mask;
if (read_card(player_mask, &player, &p, &suit)) {
pass_to(player, map_player(player, hand),
++card_table[player].num_passed, p, suit);
if (card_table[player].num_passed == NPASS)
/*
* Done passing
*/
passed_mask |= 1 << player;
else {
(void) sprintf(buf, "M%dCard %d:", LEAD_WINDOW,
card_table[player].num_passed + 1);
/*
* Ask for next card
*/
send_buf(player, buf);
send_buf(player, "G");
}
}
else
/*
* Player left game
*/
passed_mask &= ~(1 << player);
/*
* Player added?
*/
for (player = 1; player <= PLAYERS; player++)
if (~old_mask & player_mask & (1 << player)) {
card_table[player].num_passed = 0;
player_pass(player);
}
}
erase_all_window(LEAD_WINDOW);
human_type = VOYEUR;
/*
* Let computer pass.
*/
for (player = 1; player <= PLAYERS; player++)
if (card_table[player].player_kind != HUMAN)
for (pass = ++card_table[player].num_passed;
pass <= NPASS; pass++) {
get_pass(player, pass, &p, &suit);
pass_to(player, map_player(player, hand), pass, p, suit);
}
for (player = 1; player <= PLAYERS; player++) {
for (pass = 1; pass <= NPASS; pass++)
enter_card(card_table[player].passed_cards[pass],
card_table[player].cards);
if (card_table[player].player_kind != COMPUTER)
print_pass(player);
}
}
point_value(rank, suit)
int rank, suit;
{
if (suit == HEARTS)
return(1);
else
if ((suit == SPADES) && (rank == QUEEN))
return(13);
else
if ((suit == DIAMONDS) && (rank == JACK) && MIKEYJ)
return(-10);
return(0);
}
show(player, card_to_play, suit_to_play)
int player, suit_to_play;
ptr card_to_play;
{
int rank_to_play;
char buf[BUF_SIZE];
rank_to_play = card_to_play->rank;
if (card_table[player].player_kind != COMPUTER) {
erase_window(player, INP_WINDOW);
erase_window(player, LEAD_WINDOW);
toss_card(player, card_to_play, suit_to_play);
}
remove_node(card_to_play);
--card_table[player].cards[suit_to_play].length;
if (suit_to_play == HEARTS)
trick.any_hearts = TRUE;
else {
if ((suit_to_play == SPADES) && (rank_to_play == QUEEN))
queen_played = TRUE;
else
if ((suit_to_play == DIAMONDS) &&
(rank_to_play == JACK) &&
MIKEYJ) {
jack_played = TRUE;
MIKEYJdintrick = TRUE;
}
}
if ((suit_to_play == trick.suit_led) &&
(rank_to_play > trick.highest_played)) {
trick.highest_played = rank_to_play;
trick.high_player = player;
}
trick.played[player].rank = rank_to_play;
trick.played[player].suit = suit_to_play;
trick.pts += point_value(rank_to_play, suit_to_play);
enter_card(trick.played[player], cards_played);
++trick.card_count;
(void) sprintf(buf, "P%d%c%c%s", player, rnames[rank_to_play],
*snames[suit_to_play], card_table[player].name);
send_to_all(buf);
}
really_safe(suit)
int suit;
{
char safe;
int losers_out, low_rank;
low_rank = card_table[leader].cards[suit].tail->llink->rank;
/*
* Count # cards of _suit_ played that would be losers
* to leaders lowest of _suit_.
*/
losers_out = count_losers(low_rank,suit);
/*
* Card is guaranteed to lose the trick if:
* -- All cards of suit lower than low_rank have been played;
* -- There is at least one other card of the suit unplayed.
*
*/
safe = (losers_out == (low_rank - 1)) &&
(card_table[leader].cards[suit].length +
cards_played[suit].length != 13);
safe &= (find_true(voided[suit]) == FALSE);
if (suit == SPADES)
safe &= ((card_table[leader].cards[SPADES].tail->llink->rank <
QUEEN) || queen_played);
return(safe);
}
find_low_lead(card_to_play, suit_to_play, play_hearts)
ptr *card_to_play;
int *suit_to_play;
char play_hearts;
{
int pass, low_card, suit, f, last_gasp;
ptr p, q;
last_gasp = FALSE;
*card_to_play = NULL;
pass = 0;
q = find_card(leader,QUEEN,SPADES);
while (*card_to_play == NULL) {
pass++;
if (pass > 20)
last_gasp = TRUE;
low_card = MAX_RANK+1;
for (suit = CLUBS; suit <= SPADES; suit++) {
p = card_table[leader].cards[suit].tail->llink;
if ((((p->rank == JACK) && (suit == DIAMONDS) && MIKEYJ) ||
((p->rank == QUEEN) && (suit == SPADES))) &&
(card_table[leader].cards[suit].length > 1)) {
p = p->llink;
}
if ((p->rank <= MAX_RANK) &&
(p->rank-count_losers(p->rank,suit) < low_card) &&
(play_hearts || (suit != HEARTS) || last_gasp)) {
f = TRUE;
switch(pass) {
case 1:
f = ((cards_out(suit) == card_table[leader].cards[suit].length+1) &&
(!q || (suit != SPADES)) &&
(highest_unplayed(suit,leader) > p->rank));
break;
case 2:
f = (lowestp(p->rank, suit) &&
(!q || (suit != SPADES)) &&
(highest_unplayed(suit,leader) > p->rank));
break;
case 3:
f = ((count_array(voided[suit],TRUE) == 2) &&
(!q || (suit != SPADES)) &&
(highest_unplayed(suit,leader) > p->rank) &&
(lowest_unplayed(suit,leader) > p->rank));
break;
case 4:
f = (safe_suit(suit) &&
(!q || (suit != SPADES) ||
(card_table[leader].cards[suit].length > 3)) &&
(highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2) &&
!((MIKEYJ && (suit == DIAMONDS) && (p->rank == JACK)) ||
((suit == SPADES) && (p->rank == QUEEN))));
break;
case 5:
f = ((find_true(voided[suit]) == MAYBE) &&
(!q || (suit != SPADES) ||
(card_table[leader].cards[suit].length > 2)) &&
(highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2) &&
!((MIKEYJ && (suit == DIAMONDS) && (p->rank == JACK)) ||
((suit == SPADES) && (p->rank == QUEEN))));
break;
case 6:
f = ((highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2) &&
(!q || (suit != SPADES) ||
(card_table[leader].cards[suit].length > 2)) &&
!((MIKEYJ && (suit == DIAMONDS) && (p->rank == JACK)) ||
((suit == SPADES) && (p->rank == QUEEN))));
break;
case 7:
f = ((highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2) &&
!((MIKEYJ && (suit == DIAMONDS) && (p->rank == JACK)) ||
((suit == SPADES) && (p->rank == QUEEN))));
break;
case 8:
f = ((highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2) &&
((suit != SPADES) || (p->rank != QUEEN)));
break;
case 9:
f = ((highest_unplayed(suit,leader) > p->rank) &&
(count_array(voided[suit],TRUE) < 2));
break;
}
if (f) {
low_card = p->rank-count_losers(p->rank,suit);
*card_to_play = p;
*suit_to_play = suit;
}
}
}
}
#ifdef DEBUG
fprintf(fp, "%2d ",pass);
#endif
}
show_lead(card_to_play, suit_to_play)
int suit_to_play;
ptr card_to_play;
{
trick.card_count = 1;
trick.highest_played = card_to_play->rank;
trick.suit_led = suit_to_play;
trick.high_player = leader;
show(leader, card_to_play, suit_to_play);
}
read_lead(leader, card_to_play, suit_to_play)
int leader, *suit_to_play;
ptr *card_to_play;
{
char good, alive, buf[64];
int t; /* Ignored */
do {
send_buf(leader, "G");
if (alive = read_card(1 << leader, &t, card_to_play, suit_to_play)) {
good = (((round != 1) ||
(*card_to_play == card_table[leader].cards[CLUBS].tail->llink)) &&
((*suit_to_play != HEARTS) || hearts_broken || only_hearts(leader)));
if (!good) {
if (*suit_to_play == HEARTS)
(void) sprintf(buf,"M%dHearts not broken yet!", TEXT_WINDOW);
else
(void) sprintf(buf,"M%dLead your lowest club!", TEXT_WINDOW);
send_buf(leader, buf);
send_buf(leader, "G");
}
}
}
while (alive && !good);
}
read_next_card(player, card_to_play, suit_to_play)
int player, *suit_to_play;
ptr *card_to_play;
{
char good, good_pts, alive, buf[64];
int t; /* Ignored */
do {
send_buf(player, "G");
if (alive = read_card(1 << player, &t, card_to_play, suit_to_play)) {
good_pts = (((*suit_to_play != SPADES) ||
((*card_to_play)->rank != QUEEN))
&& ((*suit_to_play != HEARTS) || only_hearts(player)) ||
(round != 1));
good = (((*suit_to_play == trick.suit_led) ||
(card_table[player].cards[trick.suit_led].length == 0)) && good_pts);
if (!good) {
if (!good_pts)
(void) sprintf(buf, "M%dCan't dump points yet!", TEXT_WINDOW);
else
(void) sprintf(buf, "M%dTry following suit!", TEXT_WINDOW);
send_buf(player, buf);
send_buf(player, "G");
}
}
}
while (alive && !good);
}
int
num_lower(player, rank, suit_to_play)
int player, rank, suit_to_play;
{
ptr p;
int mikey;
mikey = 0;
p = card_table[player].cards[suit_to_play].head->rlink;
while (p && p->rank) {
if (p->rank < rank)
mikey++;
p = p->rlink;
}
return(mikey);
}
char flushdiamonds(player)
int player;
{
ptr mikey;
mikey = find_card(player,JACK,DIAMONDS);
if (MIKEYJ && mikey && (mikey->rank == JACK) &&
(safe_suit(DIAMONDS) || queen_played) &&
(cards_out(DIAMONDS) > PLAYERS) &&
((safe_suit(DIAMONDS) &&
(card_table[player].cards[DIAMONDS].length >
((cards_out(DIAMONDS)/PLAYERS)+2))) ||
(num_lower(player,JACK,DIAMONDS) >=
(cards_out(DIAMONDS) - card_table[player].cards[DIAMONDS].length))))
return(TRUE);
else
return(FALSE);
}
char gotthejack(player)
int player;
{
ptr p,q;
if (card_table[player].cards[DIAMONDS].length > 0)
p = card_table[player].cards[DIAMONDS].head->rlink;
else
p = NULL;
if (cards_played[DIAMONDS].length > 0)
q = cards_played[DIAMONDS].head->rlink;
else
q = NULL;
if (!((p&&(p->rank == ACE)) || ((q&&(q->rank == ACE)&&
(not_in_trick(ACE,DIAMONDS))))))
return(FALSE);
if ((p&&(p->rank == ACE)))
p = p->rlink;
else
q = q->rlink;
if (!((p&&(p->rank == KING)) || ((q&&(q->rank == KING)&&
(not_in_trick(KING,DIAMONDS))))))
return(FALSE);
if ((p&&(p->rank == KING)))
p = p->rlink;
else
q = q->rlink;
if (!((p&&(p->rank == QUEEN)) || ((q&&(q->rank == QUEEN)&&
(not_in_trick(QUEEN,DIAMONDS))))))
return(FALSE);
if ((p&&(p->rank == QUEEN)))
p = p->rlink;
else
q = q->rlink;
if (p&&(p->rank == JACK))
return(TRUE);
else
return(FALSE);
}
computer_lead(card_to_play, suit_to_play)
int *suit_to_play;
ptr *card_to_play;
{
ptr p;
find_queen(leader, &p);
if (round == 1) {
/* Lead the 2 of clubs */
*card_to_play = card_table[leader].cards[CLUBS].tail->llink;
*suit_to_play = CLUBS;
} else if (MIKEYJ && gotthejack(leader) &&
(safe_suit(DIAMONDS) || queen_played)) {
*card_to_play = find_card(leader,JACK,DIAMONDS);
*suit_to_play = DIAMONDS;
} else if (!queen_played && !p && spades_safe(leader) &&
next_highest(leader,QUEEN,SPADES) &&
(highest(leader,SPADES)->rank < QUEEN)) {
/* try and flush the queen out */
*suit_to_play = SPADES;
*card_to_play = next_highest(leader, QUEEN, SPADES);
} else if (flushdiamonds(leader)) {
*suit_to_play = DIAMONDS;
if (diamonds_safe(leader) || queen_safe(leader)) {
*card_to_play = next_highest(leader,JACK,DIAMONDS);
if (*card_to_play == NULL)
*card_to_play = highest(leader,DIAMONDS);
} else {
*card_to_play = lowest(leader,DIAMONDS);
if ((*card_to_play)->rank == JACK)
*card_to_play = highest(leader,DIAMONDS);
}
} else {
find_low_lead(card_to_play, suit_to_play,
(hearts_broken || only_hearts(leader)));
}
}
pick_a_loser(player, card_to_play, suit_to_play)
int player, *suit_to_play;
ptr *card_to_play;
{
ptr p;
if (card_table[player].cards[trick.suit_led].length > 0) {
*card_to_play = NULL;
*suit_to_play = trick.suit_led;
if ((trick.suit_led != SPADES) &&
(trick.pts == 0) &&
(!MIKEYJ || jack_played || (trick.suit_led != DIAMONDS) ||
(((highest(player,DIAMONDS))->rank) < JACK)) &&
((trick.card_count == PLAYERS) ||
(round == 1)))
/*
* Play the highest card of suit led if player is last
* and there are no points in current trick.
*/
*card_to_play = effective_highest(player, trick.suit_led);
else
switch (trick.suit_led) {
case SPADES:
if ((trick.card_count == PLAYERS) && (trick.pts == 0)) {
/*
* Play the highest spade. Don't play queen if
* it will win the trick.
*/
*card_to_play = effective_highest(player, SPADES);
if (((*card_to_play)->rank == QUEEN) &&
(QUEEN > trick.highest_played))
*card_to_play = next_highest(player, QUEEN, SPADES);
} else {
find_queen(player,&p);
if (p && (trick.highest_played > QUEEN))
*card_to_play = p;
else
if (!queen_played &&
(find_true(hasqs[player]) == FALSE) &&
(spades_safe(player) ||
((highest(player, SPADES)->rank > QUEEN) &&
(trick.pts < PLAYERS)))) {
*card_to_play = effective_highest(player, SPADES);
if ((p == NULL) || (p == *card_to_play))
*card_to_play =
next_highest(player,max(trick.highest_played,QUEEN),SPADES);
}
}
break;
case CLUBS:
if (clubs_safe(player) && (trick.pts <= 0))
*card_to_play = effective_highest(player, CLUBS);
break;
case DIAMONDS:
if (MIKEYJ && (trick.pts < PLAYERS) && (trick.highest_played < JACK) &&
find_card(player,JACK,DIAMONDS) && queen_safe(player) &&
((trick.card_count == PLAYERS) ||
(find_false(voided[DIAMONDS]) == TRUE) ||
gotthejack(player))) {
*card_to_play = find_card(player,JACK,DIAMONDS);
} else if (MIKEYJ && (trick.pts < 0)) {
/* dump highest diamond if jd in trick */
*card_to_play = highest(player,DIAMONDS);
} else if (MIKEYJ && !jack_played && diamonds_safe(player)) {
*card_to_play = next_highest(player,JACK,DIAMONDS);
} else if ((!MIKEYJ || jack_played) && diamonds_safe(player)) {
*card_to_play = highest(player,DIAMONDS);
} else if (MIKEYJ && !jack_played && (trick.pts == 0)) {
*card_to_play = next_highest(player,JACK,DIAMONDS);
if ((*card_to_play == NULL) && find_card(player,JACK,DIAMONDS))
*card_to_play = highest(player,DIAMONDS);
} else if ((next_highest(player, trick.highest_played,
DIAMONDS) == NULL) &&
MIKEYJ &&
((trick.card_count == PLAYERS) ||
(find_false(voided[DIAMONDS]) == TRUE)) &&
find_card(player,JACK,DIAMONDS)) {
*card_to_play = find_card(player,JACK,DIAMONDS);
} else if ((next_highest(player, trick.highest_played,
DIAMONDS) == NULL) &&
(card_table[player].cards[DIAMONDS].length > 1) &&
(lowest(player,DIAMONDS)->rank == JACK) &&
MIKEYJ) {
*card_to_play = lowest(player,DIAMONDS)->llink;
}
break;
case HEARTS:
break;
}
if (*card_to_play == NULL)
*card_to_play = next_highest(player, trick.highest_played,
*suit_to_play);
if ((*card_to_play == NULL) &&
(find_false(voided[*suit_to_play]) == TRUE))
*card_to_play = highest(player, *suit_to_play);
if (*card_to_play == NULL) {
*card_to_play = card_table[player].cards[trick.suit_led].tail->llink;
/*
* Don't play the Q spades if:
* 1. The Queen is your lowest spade and
* 2. You have a higher spade.
*/
if (((*card_to_play)->rank == QUEEN) && (trick.suit_led == SPADES) &&
(card_table[player].cards[trick.suit_led].length > 1))
*card_to_play = card_table[player].cards[trick.suit_led].head->rlink;
}
if (((*card_to_play)->rank > trick.highest_played) &&
(*suit_to_play == trick.suit_led) &&
((*suit_to_play != SPADES) ||
((card_table[player].cards[trick.suit_led].head->rlink)->rank !=
QUEEN)) &&
((trick.card_count == PLAYERS) ||
(find_false(voided[trick.suit_led]) == TRUE)) &&
((*suit_to_play != DIAMONDS) || !MIKEYJ || jack_played))
*card_to_play = card_table[player].cards[trick.suit_led].head->rlink;
} else {
/* void */
if (round != 1) {
/*
* Play the queen of spades if player has it.
*/
find_queen(player,card_to_play);
if (*card_to_play)
*suit_to_play = SPADES;
if ((*card_to_play == NULL) &&
!queen_played &&
(card_table[player].cards[SPADES].length) &&
((effective_highest(player,SPADES))->rank > QUEEN)) {
*card_to_play = effective_highest(player,SPADES);
*suit_to_play = SPADES;
}
if ((*card_to_play == NULL) &&
(card_table[player].cards[HEARTS].length)) {
/* MIKEY: does this work? */
if (someone_is_shooting &&
(mikeyshooter != NONE) &&
(mikeyshooter != player)) {
if ((trick.high_player != mikeyshooter) &&
trick.played[mikeyshooter].suit)
*card_to_play = highest(player, HEARTS);
else
*card_to_play = next_highest(player, JACK, HEARTS);
if (*card_to_play == NULL)
*card_to_play = effective_lowest(player, HEARTS);
} else if ((mikeyshooter != NONE) &&
(mikeyshooter != player) &&
someone_is_shooting &&
(card_table[player].cards[HEARTS].length > 3)) {
*card_to_play = highest(player, HEARTS)->rlink;
} else {
*card_to_play = effective_highest(player, HEARTS);
}
*suit_to_play = HEARTS;
}
}
if (*card_to_play == NULL)
find_high_card(player, card_to_play, suit_to_play,
(round != 1),
(find_card(player,QUEEN,SPADES) != NULL),
(!MIKEYJ || jack_played));
}
}
stop_the_bastard(player, card_to_play, suit_to_play)
int player, *suit_to_play;
ptr *card_to_play;
{
if (card_table[player].cards[trick.suit_led].length) {
*suit_to_play = trick.suit_led;
if ((trick.suit_led == SPADES) && !queen_safe(player)) {
*card_to_play = next_highest(player, QUEEN, SPADES);
if (*card_to_play == NULL)
*card_to_play = effective_lowest(player, SPADES);
}
else
*card_to_play = effective_highest(player, trick.suit_led);
if ((trick.pts < PLAYERS) &&
!(((cards_out(trick.suit_led) <
(PLAYERS - player_count)) ||
trick.any_hearts) &&
((*card_to_play)->rank > trick.highest_played) &&
(mikeyshooter == trick.high_player)))
*card_to_play = effective_lowest(player, trick.suit_led);
}
else
if ((mikeyshooter != trick.high_player) &&
(mikeyshooter != NONE) &&
someone_is_shooting &&
(card_table[player].cards[HEARTS].length > 0)) {
*card_to_play = effective_highest(player, HEARTS);
*suit_to_play = HEARTS;
}
else
find_low_card(player, card_to_play, suit_to_play);
}
computer_pick(player, card_to_play, suit_to_play)
int player, *suit_to_play;
ptr *card_to_play;
{
*card_to_play = NULL;
/* MIKEY: computer too stupid to shoot */
if (someone_is_shooting && (player == mikeyshooter))
someone_is_shooting = FALSE;
if (someone_is_shooting && (player != mikeyshooter))
stop_the_bastard(player, card_to_play, suit_to_play);
else
pick_a_loser(player, card_to_play, suit_to_play);
}
lead(leader)
int leader;
{
int suit_to_play;
ptr card_to_play;
char buf[BUF_SIZE];
if (card_table[leader].player_kind == HUMAN) {
(void) sprintf(buf, "M%dYour lead:", LEAD_WINDOW);
send_buf(leader, buf);
read_lead(leader, &card_to_play, &suit_to_play);
}
if (card_table[leader].player_kind != HUMAN) /* If player left */
computer_lead(&card_to_play, &suit_to_play);
show_lead(card_to_play, suit_to_play);
}
play_next_card(player)
int player;
{
int suit_to_play;
ptr card_to_play;
char buf[BUF_SIZE];
if (card_table[player].player_kind == HUMAN) {
(void) sprintf(buf, "M%dYour play:", LEAD_WINDOW);
send_buf(player, buf);
read_next_card(player, &card_to_play, &suit_to_play);
}
if (card_table[player].player_kind != HUMAN) /* If player left */
computer_pick(player, &card_to_play, &suit_to_play);
if (suit_to_play != trick.suit_led) {
int each;
voided[trick.suit_led][player] = TRUE;
if (trick.suit_led == SPADES) {
for (each=1; each<=PLAYERS; each++) {
hasqs[each][player] = FALSE;
}
}
} else if ((card_to_play->rank >= JACK) && (trick.pts > 0) &&
(trick.card_count != PLAYERS)) {
voided[suit_to_play][player] = MAYBE;
}
if ((suit_to_play == trick.suit_led) &&
(suit_to_play == DIAMONDS) &&
(card_to_play->rank > JACK) &&
!jack_played) {
voided[DIAMONDS][player] = MAYBE;
}
if (trick.suit_led == SPADES &&
((suit_to_play != SPADES) ||
((trick.highest_played > QUEEN) &&
(card_to_play->rank != QUEEN)))) {
int each;
for (each=1; each<=PLAYERS; each++) {
hasqs[each][player] = FALSE;
}
}
show(player, card_to_play, suit_to_play);
}
find_winner(winner)
int *winner;
{
char mikeyshoot;
int maxscore, player, possibly_shooting;
#ifdef DEBUG
for (mikey=1; mikey<=PLAYERS; mikey++) {
fprintf(fp, "%c%c ", rnames[trick.played[mikey].rank],
*snames[trick.played[mikey].suit]);
}
fprintf(fp, "\n %d ", trick.high_player);
#endif
*winner = trick.high_player;
if (trick.any_hearts)
hearts_broken = TRUE;
points_played += trick.pts;
card_table[*winner].pts += trick.pts;
if (MIKEYJdintrick) {
MIKEYJder = *winner;
MIKEYJdintrick = FALSE;
}
if ((trick.pts != 0) && (trick.pts != -10))
if ((mikeyshooter == 0) || (mikeyshooter == *winner))
mikeyshooter = *winner;
else
mikeyshooter = NONE;
possibly_shooting = ((mikeyshooter != NONE) && (mikeyshooter != 0));
if (MIKEYJ && jack_played && !queen_played)
someone_is_shooting = (possibly_shooting &&
(card_table[*winner].pts >= HEARTS_PANIC-10));
else if (!jack_played && queen_played)
someone_is_shooting = (possibly_shooting &&
(card_table[*winner].pts >= HEARTS_PANIC+13));
else
someone_is_shooting = (possibly_shooting &&
(card_table[*winner].pts >= HEARTS_PANIC));
/* or if lead void */
trick.card_count = 1;
if ((*winner == leader) &&
(find_false(voided[trick.suit_led]) == TRUE))
someone_is_shooting = possibly_shooting;
mikeyshoot = TRUE;
maxscore = 0;
for (player = 1; player <= PLAYERS; player++) {
if ((maxscore != 0) && (card_table[player].pts != 0))
mikeyshoot = FALSE;
if ((card_table[player].pts != 0) &&
(card_table[player].pts != -10) &&
(maxscore == 0)) {
maxscore = card_table[player].pts;
}
}
someone_is_shooting = someone_is_shooting && mikeyshoot;
}
main()
{
int player;
int mikeytemp;
init();
new_game();
if (debug)
human_type = VOYEUR;
else
#ifdef DEBUG
human_type = VOYEUR;
#else
human_type = HUMAN;
#endif
if (debug)
mikeytemp = 100;
else
mikeytemp = PLAYERS;
get_first_player();
add_player();
#ifdef DEBUG
fp = fopen("hearts.log", "w");
if (fp == NULL) {
fprintf(stderr, "Can't open file hearts.log for writing\n");
exit(1);
}
for (mikeytemp=3; mikeytemp<=3; mikeytemp++) {
#else
for (;;) {
#endif
for (hand = 1; hand <= mikeytemp; hand++) {
int mikey;
#ifdef DEBUG
fprintf(fp,"\n--- New Hand ---\n ");
#endif
for(mikey = 1;mikey <= PLAYERS;mikey++) {
trick.played[mikey].rank = 0;
trick.played[mikey].suit = 0;
}
round = 0;
new_hand();
if (hand != PLAYERS)
pass_cards();
find_low_club(&leader);
for (mikey=1; mikey<=PLAYERS; mikey++) {
if (find_card(mikey,QUEEN,SPADES)) {
int each;
for (each=1; each<=PLAYERS; each++) {
hasqs[mikey][each] = (each == mikey);
}
}
}
for (round = 1; round <= NCARDS; round++) {
new_round();
lead(leader);
for (player_count = 1; player_count <= PLAYERS - 1; player_count++)
play_next_card(map_player(leader, player_count));
find_winner(&leader);
send_all_totals();
}
send_all_winner();
#ifndef DEBUG
if (!debug)
for (player = 1; player <= PLAYERS; player++)
if (card_table[player].player_kind == VOYEUR) {
card_table[player].player_kind = HUMAN;
player_mask |= 1 << player;
}
#endif
}
send_all_totals();
send_final_winner();
send_to_all("X");
new_game();
}
#ifdef DEBUG
fclose(fp);
#endif
}