/*
 *	$Source: /afs/athena.mit.edu/user/j/i/jik/sipbsrc/src/mboggle/RCS/mbogglemaster.c,v $
 *	$Header: /afs/athena.mit.edu/user/j/i/jik/sipbsrc/src/mboggle/RCS/mbogglemaster.c,v 1.25 1992/08/16 20:27:21 jik Exp $
 */

#ifndef lint
static char *rcsid_mbogglemaster_c = "$Header: /afs/athena.mit.edu/user/j/i/jik/sipbsrc/src/mboggle/RCS/mbogglemaster.c,v 1.25 1992/08/16 20:27:21 jik Exp $";
#endif lint

#include <errno.h>
#include <stdio.h>
#include <strings.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <syslog.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include <netdb.h>
#ifdef HESIOD
#include <hesiod.h>
#endif

#include "boggle.h"

#define MAXLINE 1000

extern char *worddir;

/* The C compiler ought to know this, but doesn't. */
char *getenv();
char *malloc(), *realloc();
#define my_realloc(ptr, num) ((ptr) ? realloc(ptr, num) : malloc(num))
     
/* A player_ext is used to store info about a player that can be seen
   externally, i.e. by a query_game.  A player_int is used to store other
   information about a player that other players wouldn't be interested in.
   Keeping this data divided this way makes it easy to do query_response and
   broadcast_state. */

struct player_int {
	int		socket;
	int		num_of_words;
	int		current_word;
	char	words[MAX_NUM_OF_WORDS][MAX_WORD_LENGTH];
};

struct player_ext players_ext[MAX_NUM_OF_PLAYERS];
static struct player_ext players_netbyte[MAX_NUM_OF_PLAYERS];
struct player_int players_int[MAX_NUM_OF_PLAYERS];

void send_scored_word();

int dictionary_started = 0;     
int num_of_players, player_in_question, num_of_active_players;
char current_tray[5][5];
int is_child = 0;
fd_set permfds;
int max_fd;
int mboggle_socket;
int time_is_up;

int word_values[MAX_WORD_LENGTH] = {0, 0, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34,
				    55, 89, 144, 233, 477, 710, 1187, 1897, 3084, 4981, 8065, 13046, 21111,
				    34156};

static unsigned long nlen;		/* for network byte order */

reap_children()
{
     union wait status;

     while (wait3(&status, WNOHANG, 0) > 0) /* empty */;
}

main(argc, argv)
int argc;
char *argv[];
{
     int c;
     extern char *optarg;
     u_short port = 0;

     signal(SIGCHLD, reap_children);

     openlog("mbogglemaster", LOG_PID, LOG_DAEMON);

     while ((c = getopt(argc, argv, "w:p:")) != EOF) {
	  switch (c) {
	  case 'p':
	       port = htons(atoi(optarg));
	       break;
	  case 'w':
	       worddir = optarg;
	       break;
	  default:
	       syslog(LOG_ERR, "invalid command-line option; exiting.");
	       exit(1);
	  }
     }

     mboggle_socket = get_mboggle_socket(port);
     listen_loop();
}

listen_loop()
{
     int nfds, retval;
     fd_set readfds;
     int i;

     FD_ZERO(&permfds);
     FD_SET(mboggle_socket, &permfds);
     max_fd = mboggle_socket;
     num_of_players = 0;
     srandom(getpid());

     while (1) {
	  readfds = permfds;
	  nfds = select(max_fd + 1, &readfds, 0, 0, 0);
	  if (nfds < 0) {
	       if (errno == EINTR) {
		    if (time_is_up) {
			 do_stuff();
		    }
		    continue;
	       }
	       syslog(LOG_ERR, "select: %m; exiting.");
	       exit(1);
	  }
	  if (FD_ISSET(mboggle_socket, &readfds)) {
	       int new;
	       if (! new_player_is_ok()) {
		    if ((retval = fork()) < 0) {
			 syslog(LOG_ERR, "forking for new daemon: %m");
		    }
		    else if (retval != 0) {
			 continue;
		    }
		    else {
			 clear_everything();
			 is_child++;
		    }
	       }
	       new = accept(mboggle_socket, 0, 0);
	       if (new < 0) {
		    continue;
	       }
	       new_player(new);
	       nfds--;
	  }
	  while (nfds > 0) {
	       for (i = 0; i < num_of_players; i++) {
		    if (FD_ISSET(players_int[i].socket, &readfds)) {
			 listen_to_player(i);
		    }
		    nfds--;
		    continue;
	       }
	       break;
	  }
     }
}

get_mboggle_socket(port)
u_short port;
{
     struct servent *service;
     struct sockaddr_in addr;
     int mboggle_socket;
     int true = 1;

#ifdef HESIOD
     if (port == 0) {
	  service = hes_getservbyname("mboggle", "tcp");
	  if (service) {
	       port = service->s_port;
	  }
     }
#endif

     if (port == 0) {
	  service = getservbyname("mboggle", "tcp");
	  if (service) {
	       port = service->s_port;
	  }       
     }

#ifdef MBOGGLE_PORT
     if (port == 0) {
	  port = htons(MBOGGLE_PORT);
     }
#endif

     if (port == 0) {
	  syslog(LOG_ERR, "couldn't determine network port number; exiting.");
	  exit(1);
     }

     bzero((char *)&addr, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = port;
     addr.sin_addr.s_addr = INADDR_ANY;

     mboggle_socket = socket(AF_INET, SOCK_STREAM, 0);
     if (mboggle_socket < 0) {
	  syslog(LOG_ERR, "socket: %m; exiting.");
	  exit(1);
     }
     if (setsockopt(mboggle_socket, SOL_SOCKET, SO_REUSEADDR, (char *)true,
		    sizeof(true)) < 0) {
	  syslog(LOG_ERR, "setsockopt: %m");
     }
     if (bind(mboggle_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
	  syslog(LOG_ERR, "bind: %m; exiting.");
	  exit(1);
     }
     if (listen(mboggle_socket, 5) < 0) {
	  syslog(LOG_ERR, "listen: %m; exiting.");
	  exit(1);
     }

     return(mboggle_socket);
}


clear_everything()
{
     int i;

     for (i = 0; i < num_of_players; i++) {
	  FD_CLR(players_int[i].socket, &permfds);
	  close(players_int[i].socket);
     }
     num_of_players = 0;
     max_fd = mboggle_socket;
}
    
do_zephyr()
{
#ifdef STARTZEPHYR
	int child_pid;
#endif
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering do_zephyr()\n");
#endif
#ifdef STARTZEPHYR
	if ((child_pid = fork()) == -1)
	     syslog(LOG_ERR, "Couldn't fork to send zephyr message: %m");
	else if (! child_pid) {
	     execl(STARTZEPHYR, "zwrite", "-q", "-d", "-i",
		   "boggle", "-m", "Someone is playing boggle!", 0);
	     /* should never get here */
	     syslog(LOG_ERR, "Exec of zwrite failed: %m");
	     exit(0);
	}
#endif
}


int new_player_is_ok()
{
	extern int num_of_players;
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering new_player_is_ok()\n");
#endif
	return(num_of_players < MAX_NUM_OF_PLAYERS);
}


/* ARGSUSED */
new_player(sock)
int sock;
{
	extern struct player_int players_int[];
	extern struct player_ext players_ext[];
	extern int num_of_players;
	extern char current_tray[5][5];
	
	int name_length;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering new_player()\n");
#endif
	if (safe_read(sock, (char *)&name_length, sizeof(name_length)) < 0) {
		return;
	}
	nlen = ntohl(name_length);
	if (safe_read(sock, (char *)players_ext[num_of_players].name,
		      (int) nlen) < 0) {
	     return;
	}
	FD_SET(sock, &permfds);
	if (sock > max_fd) {
	     max_fd = sock;
	}
	players_int[num_of_players].socket		= sock;
	players_int[num_of_players].num_of_words	= 0;
	players_int[num_of_players].current_word	= 0;
	players_ext[num_of_players].state		= PLAYER_VIEWING;
	players_ext[num_of_players].tray_score	= 0;
	players_ext[num_of_players].total_score	= 0;
	players_ext[num_of_players].ease_rating	= 0;
	players_ext[num_of_players].num_of_games	= 0;
	num_of_players++;
	if (num_of_players == 1) {
	     do_zephyr();
	}

	do_stuff();
}


/* ARGSUSED */
fix_to_net(players, num)
struct player_ext players[];
int num;
{
	extern struct player_ext players_ext[];
	extern int num_of_players;
	register int i;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering fix_to_net()\n");
#endif
	for (i = 0; i < num_of_players; i++) {
		players[i].ease_rating = htonl(players_ext[i].ease_rating);
		players[i].tray_score = htons(players_ext[i].tray_score);
		players[i].total_score = htons(players_ext[i].total_score);
	}
}


listen_to_player(player_num)
{
     extern struct player_ext players_ext[];
	
     char boggle_code;
	
#ifdef DEBUG
     syslog(LOG_DEBUG, "Entering listen_to_player()\n");
#endif
	
     /* Find out what he wants. */
     if (safe_read(players_int[player_num].socket,
		   &boggle_code, sizeof(boggle_code)) < 0) {
	  get_rid_of_player(player_num);
	  return;
     }

     if (players_ext[player_num].state != PLAYER_IN_QUESTION) {
	  players_ext[player_num].state = boggle_code;
     }

     broadcast_state_update(player_num, boggle_code);

     do_stuff();
}


do_stuff()
{
    static int state_of_game = SOG_WAITING;

    if (num_of_players == 0) {
	 state_of_game = SOG_WAITING;
	 return;
    }

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering do_stuff()\n");
#endif
    switch (state_of_game) {
	  case SOG_WAITING:
	    goto waiting;
	  case SOG_PLAYING:
	    goto playing;
	  case SOG_SCORING:
	    goto scoring;
	  default:
	    syslog(LOG_ERR, "game in bad state");
	    exit(1);
    }

    while (1) {
	waiting:
	    invite_everyone();
	    if (someone_is(PLAYER_INVITED)) break;
	    start_game();
	    state_of_game = SOG_PLAYING;
	playing:
	    if (time_is_up) {
		 int i;
		 char boggle_code = YOURE_DONE;
		 for (i = 0; i < num_of_players; i++) {
		      if (players_ext[i].state == PLAYER_PLAYING) {
			   write(players_int[i].socket, (char *) &boggle_code,
				 sizeof(boggle_code));
		      }
		 }
	    }
	    if (someone_is(PLAYER_PLAYING)) break;
	    get_words_from_players();
	    continue_scoring();
	    state_of_game = SOG_SCORING;
	scoring:
	    if (player_in_question >= 0) {
		if (someone_is(PLAYER_DECIDING)) break;
		tally_votes();
		if (player_in_question >= 0) break;
		continue_scoring();
		if (player_in_question >= 0) break;
	    }
	    accumulate_scores();
	    state_of_game = SOG_WAITING;
    }

    broadcast_state();
}


invite_everyone()
{
	extern struct player_ext players_ext[];
	extern struct player_int players_int[];
	extern int num_of_players;

	register int i;
	char boggle_code;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering invite_everyone()\n");
#endif
	boggle_code = WANT_TO_PLAY;
	for (i = 0; i < num_of_players; i++) {
		switch (players_ext[i].state) {
		      case PLAYER_READY:
		      case PLAYER_PASSING:
		      case PLAYER_INVITED:
			break;
		      default:
			write(players_int[i].socket, &boggle_code, sizeof(boggle_code));
			players_ext[i].state = PLAYER_INVITED;
			break;
		}
	}
}


time_up()
{
     time_is_up = 1;
}

start_game()
{
	extern struct player_int players_int[];
	extern struct player_ext players_ext[];
	extern int num_of_players, num_of_active_players;
	extern char current_tray[5][5];

	char boggle_code;
	register int i, sock;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering start_game()\n");
#endif
	if (! dictionary_started) {
	     initialize_hash_tables(4, 25);
	     dictionary_started++;
	}
	shake_tray(current_tray);
	num_of_active_players = 0;
	for (i = 0; i < num_of_players; i++) {
		sock = players_int[i].socket;
		if (players_ext[i].state == PLAYER_PASSING) {
			boggle_code = CURRENT_TRAY;
			players_ext[i].state = PLAYER_VIEWING;
		}
		else {
			boggle_code = NEW_GAME;
			players_ext[i].state = PLAYER_PLAYING;
			num_of_active_players++;
		}
		write(sock, &boggle_code, sizeof(boggle_code));
		write(sock, (char *)current_tray, 25 * sizeof(char));
	}
	time_is_up = 0;
	signal(SIGALRM, time_up);
#ifdef SHORT_TIMER
	alarm(SHORT_TIMER+10);
#else
	alarm(190);
#endif
}


continue_scoring()
{
	extern int num_of_active_players, player_in_question;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering continue_scoring()\n");
#endif
	switch (num_of_active_players) {
	      case 0:
		player_in_question = -1;
		break;
	      case 1:
		score_one_player();
		break;
	      default:
		score_multiple_players();
		break;
	}
}


score_one_player()
{
	extern struct player_ext players_ext[];
	extern struct player_int players_int[];
	extern int player_in_question;
	
	register struct player_int *pi;
	register int current_word, num_of_words;
	int player_num;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering score_one_player()\n");
#endif
	for (player_num= 0; player_num < num_of_players; player_num++) {
		if (players_ext[player_num].state != PLAYER_VIEWING) break;
	}
	
	pi = &players_int[player_num];
	current_word = pi->current_word;
	num_of_words = pi->num_of_words;

	while (current_word < num_of_words) {
		if (check_word(pi->words[current_word])) {
			give_player_word(player_num, pi->words[current_word]);
		}
		current_word++;
	}

	pi->current_word = current_word;
	player_in_question = -1;
}


score_multiple_players()
{
	extern struct player_ext players_ext[];
	extern struct player_int players_int[];
	extern int num_of_players, player_in_question;

	register int i, sock;
	struct player_int *piq_int;
	struct player_ext *piq_ext;
	char *word, boggle_code;
	int wordlen;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering score_multiple_players()\n");
#endif
	if ((player_in_question = next_disputed_word()) >= 0) {

	/* We need a vote.  Each person needs to be sent the name of the person
	   who thought up the word and the word itself.  The viewers and the
	   person involved get the message with the THEY_ARE_VOTING code; everyone
	   else gets it with the WE_ARE_VOTING code. */

		piq_int = &players_int[player_in_question];
		piq_ext = &players_ext[player_in_question];

		piq_ext->state = PLAYER_IN_QUESTION;

	/* Add one to the word length so that we will include the trailing null. */

		word = piq_int->words[piq_int->current_word];
		wordlen = strlen(word) + 1;

		nlen = htonl(wordlen);
		for (i = 0; i < num_of_players; i++) {
			if ((i == player_in_question) || (players_ext[i].state == PLAYER_VIEWING)) {
				boggle_code = THEY_ARE_VOTING;
			}
			else {
				players_ext[i].state = PLAYER_DECIDING;
				boggle_code = WE_ARE_VOTING;
			}
			sock = players_int[i].socket;
			write(sock, &boggle_code, sizeof(boggle_code));
			write(sock, piq_ext->name, MAX_NAME_LENGTH);
			write(sock, &nlen, sizeof(nlen));
			write(sock, word, wordlen);
		}
	}
}


tally_votes()
{
	extern struct player_int players_int[];
	extern int player_in_question, num_of_players;

	register int i, yes_votes, no_votes;
	struct player_int *piq_int;
	char *word;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering tally_votes()\n");
#endif
	yes_votes = 0;
	no_votes = 0;
	for (i = 0; i < num_of_players; i++) {
		switch (players_ext[i].state) {
		      case PLAYER_VOTES_YES:
			yes_votes++;
			break;
		      case PLAYER_VOTES_NO:
			no_votes++;
			break;
		      case PLAYER_DECIDING:
			return;
		}
	}

	if (no_votes == 0) {
	/* If no one objects to the word, let him have it, whether or not
	   someone actively agreed. */
		piq_int = &players_int[player_in_question];
		word = piq_int->words[piq_int->current_word++];
		give_player_word(player_in_question, word);
		player_in_question = -1;
	}
	else if (yes_votes == 0) {
	/* Someone objected, and no one else seemed to like it either.
	   Don't give the player any points, just go on to the next
	   word. */
		players_int[player_in_question].current_word++;
		player_in_question = -1;
	}
}


int next_disputed_word()
{
	extern struct player_int players_int[];
	extern int num_of_players;

	register int i;
	register struct player_int *pi;
	int unique, player_in_question, cmp;
	char *word_in_question;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering next_disputed_word()\n");
#endif
	while (1) {

		player_in_question = -1;
		word_in_question = "~~~~~~~~~~~~~~~~~~~~~~~~~~";

		/* First, find the player with the first word in alphabetical order. */
		for (i = 0; i < num_of_players; i++) {
			pi = &players_int[i];
			if (pi->current_word < pi->num_of_words) {
				cmp = strcmp(pi->words[pi->current_word], word_in_question);
				if (cmp < 0) {
					unique = 1;
					player_in_question = i;
					word_in_question = pi->words[pi->current_word];
				}
				else if (cmp == 0) {
					unique = 0;
				}
			}
		}
		
		/* If no one had any word, we are done. */
		if (player_in_question < 0) return(-1);
    
		/* If we found a person, and his word was unique, return him. */
		if (unique) {
			if (check_word(word_in_question)) {
				give_player_word(player_in_question, word_in_question);
				players_int[player_in_question].current_word++;
				continue;
			}
			else return(player_in_question);
		}

		players_int[player_in_question].current_word++;
		for (i = player_in_question + 1; i < num_of_players; i++) {
			pi = &players_int[i];
			if (pi->current_word < pi->num_of_words) {
				if (strcmp(pi->words[pi->current_word], word_in_question) == 0) {
					pi->current_word += 1;
				}
			}
		}
	}
}

give_player_word(player_num, word)
     int player_num;
     char *word;
{
	extern struct player_int players_int[];
	extern struct player_ext players_ext[];
	extern int word_values[], num_of_players, num_of_active_players;
	
	register int val, i, sock;
	register float ease;
	int wordlen;
	char boggle_code;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering give_player_word()\n");
#endif
	wordlen = strlen(word);
	val = word_values[wordlen];
	players_ext[player_num].tray_score += val;
	
	ease = ((float) val) / num_of_active_players;
	for (i = 0; i < num_of_players; i++) {
		if (players_ext[i].state != PLAYER_VIEWING) {
			players_ext[i].ease_rating += (int) (ease * 100);
		}
	}
	
	wordlen++;
	nlen = htonl(wordlen);

	boggle_code = WINNING_WORD;
	sock = players_int[player_num].socket;
	write(sock, &boggle_code, sizeof(boggle_code));
	write(sock, &nlen, sizeof(nlen));
	write(sock, word, wordlen);

	send_scored_word(word);
}


accumulate_scores()
{
	extern struct player_ext players_ext[];
	extern int num_of_players;

	register struct player_ext *pe;
	register int i;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering accumulate_scores()\n");
#endif
	for (i = 0; i < num_of_players; i++) {
		pe = &players_ext[i];
		if (pe->state != PLAYER_VIEWING) {
			pe->total_score += pe->tray_score;
			pe->num_of_games++;
		}
	}
}


get_words_from_players()
{
	extern struct player_int players_int[];
	extern struct player_ext players_ext[];
	extern int num_of_players;
	
	struct player_int *p;
	char boggle_code;
	int num_of_words;
	register int sock, i;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering get_words_from_players()\n");
#endif
	boggle_code = SEND_WORDS;
	for (i = 0; i < num_of_players; i++) {
		if (players_ext[i].state == PLAYER_DONE) {
			p = &players_int[i];
			sock = p->socket;
			write(sock, &boggle_code, sizeof(boggle_code));
			if (safe_read(sock, (char *)&nlen,
				      sizeof(nlen)) < 0) {
					      get_rid_of_player(i);
					      return;
				      }
			num_of_words = ntohl(nlen);
			if (num_of_words > 0) {
				if (safe_read(sock, (char *)p->words,
					      num_of_words * MAX_WORD_LENGTH) < 0) {
						      get_rid_of_player(i);
						      return;
					      }
			}
			p->num_of_words = num_of_words;
#ifdef DEBUG
			syslog(LOG_DEBUG, "Got %d words from player %d",
			       num_of_words, i);
#endif			
			p->current_word = 0;
			players_ext[i].tray_score = 0;
		}
	}
}


int find_player_by_socket(sock)
     int sock;
{
	extern struct player_int players_int[];
	extern int num_of_players;
	
	register int i;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering find_player_by_socket()\n");
#endif
	for (i = 0; i < num_of_players; i++) {
		if (players_int[i].socket == sock) return(i);
	}
	return(-1);
}


char cubes[25][6] = {
	{'p', 'r', 'y', 'f', 'i', 's'},
	{'c', 'l', 'p', 'e', 'i', 't'},
	{'a', 'e', 'e', 'e', 'e', 'm'},
	{'t', 'o', 'u', 'o', 'o', 't'},
	{'e', 'o', 't', 'm', 't', 't'},
	{'p', 'r', 'y', 'i', 'r', 'r'},
	{'h', 'n', 'o', 'd', 'l', 'r'},
	{'e', 'n', 's', 's', 's', 'u'},
	{'a', 'g', 'u', 'e', 'e', 'm'},
	{'a', 'f', 's', 'a', 'a', 'r'},
	{'h', 'l', 'o', 'd', 'h', 'r'},
	{'a', 'm', 'n', 'e', 'g', 'n'},
	{'j', 'q', 'x', 'b', 'k', 'z'},
	{'d', 'o', 't', 'd', 'h', 'n'},
	{'c', 'p', 't', 'e', 'i', 's'},
	{'c', 'c', 's', 'e', 'n', 't'},
	{'a', 'e', 'e', 'a', 'e', 'e'},
	{'d', 'n', 'r', 'h', 'l', 'o'},
	{'e', 'i', 't', 'i', 'i', 't'},
	{'a', 'f', 'r', 'i', 's', 'y'},
	{'i', 'i', 't', 'c', 'e', 'l'},
	{'a', 'e', 'n', 'd', 'n', 'n'},
	{'r', 'o', 'v', 'g', 'r', 'w'},
	{'a', 'f', 'r', 'a', 'i', 's'},
	{'n', 'o', 't', 'o', 'u', 'w'}};


shake_tray(tray)
     char tray[5][5];
{
	register int x, y, i, cubes_taken, cubes_remaining, cube_number;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering shake_tray()\n");
#endif
	cubes_taken = 0;
	cubes_remaining = 25;
	for (x = 0; x < 5; x++) {
		for (y = 0; y < 5; y++) {
			cube_number = (int) random() % cubes_remaining;
			for (i = 0; i <= cube_number; i++) {
				if ((1 << i) & cubes_taken) {
					cube_number++;
				}
			}
			tray[x][y] = cubes[cube_number][((int)random()) % 6];
			cubes_taken |= (1 << cube_number);
			cubes_remaining--;
		}
	}
}


get_rid_of_player(player_num)
     int player_num;
{
     int i;
     extern struct player_int players_int[];
     extern struct player_ext players_ext[];
     extern int num_of_players, player_in_question, num_of_active_players;
	
#ifdef DEBUG
     syslog(LOG_DEBUG, "Entering get_rid_of_player()\n");
#endif
     if (players_ext[player_num].state != PLAYER_VIEWING)
	  num_of_active_players--;
	
     if (player_num == player_in_question)
	  player_in_question = -1;
	
     --num_of_players;
     if (is_child && num_of_players == 0) {
	  exit(0);
     }

     /* If we are about to die, kill the dictionary. */
     /*	if (num_of_players == 0) {
      *      kill_dictionary();
      * }
      */
	
     FD_CLR(players_int[player_num].socket, &permfds);
     (void) close(players_int[player_num].socket);

     /* Fill in the hole. */
     players_int[player_num] = players_int[num_of_players];
     players_ext[player_num] = players_ext[num_of_players];

     max_fd = mboggle_socket;
     for (i = 0; i < num_of_players; i++) {
	  if (players_int[i].socket > max_fd) {
	       max_fd = players_int[i].socket;
	  }
     }

     if (num_of_players) {
	  do_stuff();
     }
}


broadcast_state()
{
	extern struct player_ext players_ext[];
	extern struct player_int players_int[];
	extern int num_of_players;
	
	static char buf[4096];
	register int i, len;
	
	/* This routine is the main reason that player_ext and player_int
           are kept separately. */
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering broadcast_state()\n");
#endif
	bcopy(players_ext, players_netbyte,
	      num_of_players * sizeof(struct player_ext));
	fix_to_net(players_netbyte, num_of_players);

	buf[0] = GAME_STATE;
	nlen = htonl(num_of_players);
	bcopy((char *)&nlen, buf + 1, sizeof(nlen));
	bcopy((char *)players_netbyte, buf + 1 + sizeof(num_of_players), num_of_players * sizeof(struct player_ext));
	
	len = 1 + sizeof(num_of_players) + (num_of_players * sizeof(struct player_ext));
	
	for (i = 0; i < num_of_players; i++) {
		write(players_int[i].socket, buf, len);
	}
}


broadcast_state_update(player_num, state)
     int player_num;
     int state;
{
	extern struct player_int players_int[];
	extern int num_of_players;

	static char buf[3] = {STATE_UPDATE, '\0', '\0'};
	register int i;

#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering broadcast_state_update()\n");
#endif
	buf[1] = (char) player_num;
	buf[2] = (char) state;
	for (i = 0; i < num_of_players; i++) {
		write(players_int[i].socket, buf, 3);
	}
}


broadcast_all_state_updates()
{
	extern struct player_ext players_ext[];
	extern struct player_int players_int[];
	extern int num_of_players;
	
	static char buf[3 * MAX_NUM_OF_PLAYERS];
	register int i, len;
	char *ptr;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering broadcast_all_state_updates()\n");
#endif
	ptr = buf;
	for (i = 0; i < num_of_players; i++) {
		ptr[0] = STATE_UPDATE;
		ptr[1] = i;
		ptr[2] = players_ext[i].state;
		ptr += 3;
	}
	
	len = 3 * num_of_players;
	for (i = 0; i < num_of_players; i++) {
		write(players_int[i].socket, buf, len);
	}
}


int someone_is(state)
     int state;
{
	extern int num_of_players;
	extern struct player_ext players_ext[];
	
	register int i;
	
#ifdef DEBUG
	syslog(LOG_DEBUG, "Entering someone_is()\n");
#endif
	for (i = 0; i < num_of_players; i++) {
		if (players_ext[i].state == state) return(1);
	}
	return(0);
}


void send_scored_word(word)
char *word;
{
     char message;
     int i;
     
#ifdef DEBUG
     syslog(LOG_DEBUG, "Entering send_scored_word(), sending %s\n", word);
#endif

     message = SCORED_WORD;
     
     for (i = 0; i < num_of_players; i++) {
	  write(players_int[i].socket, &message, sizeof(char));
	  write(players_int[i].socket, word, MAX_WORD_LENGTH);
     }
}

