/* hangman.c - a hangman game similar to one seen on some *nix machines.*
 *									*
 * hangman [wordlist]							*
 *	    if wordlist is not specified, reads words in current 	*
 *	    directory if it exists, else reads dict:words.		*
 *									*
 * hangman (C) 1989 by Gary L. Brant					*
 *									*
 * :ts=8								*/

#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include <stat.h>

char word[30];
char dead_man[][4] = {
	" O ",
	"/|\\",
	" | ",
	"/ \\"
};

char guessed[28];	/* letters already guessed */
int count = 0;		/* count of letters in guessed */
char blimp[30];		/* string of -'s length of word */
char dictionary[30];	/* file name of dictionary */
char user[32];		/* username for highscorefile */
char highhang[32];	/* username for highscorefile */
time_t high_time;
float curavg = 0.0;
float overavg = 0.0;
float highavg = 10.0;
int wordno = 0;
int highword = 0;
long totalwrong = 0L;
int done = 0;
int raw_mode = 0;	/* true if currently in raw mode */
FILE *dict, *fopen (), *highscore;
struct stat statbuf;
long size;
#ifdef	GERMAN
#define	PLACE_IN	12
#define	TOXXXER		toupper
char *datafile	= "Worte";
char *cant1	= "kann %s nicht öffnen\n";
char *cant2	= "kann das Wörterbuch nicht finden\n";
char *cant3	= "bekomme keine Info für %s\n";
char *cant4	= "kann bei %s nicht suchen\n";
char *nohigh	= "KEINE BESTENLISTE";
char *name	= "Dein Name: ";
char *noname	= "KEIN NAME";
char *valid	= "Ungültiger Versuch, ";
char *already	= "Bereits versucht: %c";
char *gotit	= "Richtig geraten!";
char *solution	= "Das Wort hieß:  ";
char *another	= "Noch ein Wort? ";
char *please	= "Bitte 'j' oder 'n' eingeben";
char *yet	= "Geraten:";
char *wort	= "Wort #:  %d";
char *current	= "aktuelle Quote : %8.4f";
char *overall	= "Gesamtergebnis : %8.4f";
char *score	= "%02der Bestenliste:  %s";
char *average	= "mit der Quote  : %8.4f (%d)";
char *ondate	= "am %s";
char *word_	= "Wort:  ";
char *guess_	= "Buchstabe:";
#else
#define	PLACE_IN	8
#define	TOXXXER		tolower
char *datafile	= "words";
char *cant1	= "can't open %s\n";
char *cant2	= "can't find dictionary\n";
char *cant3	= "can't get info for %s\n";
char *cant4	= "can't seek on %s\n";
char *nohigh	= "NO HIGHSCORES";
char *name	= "Your Name: ";
char *noname	= "NO NAME";
char *valid	= "Not a valid guess, ";
char *already	= "Already guessed %c";
char *gotit	= "You got it!";
char *solution	= "The word was:  ";
char *another	= "Another word? ";
char *please	= "Please type 'y' or 'n'";
char *yet	= "Guessed:  ";
char *wort	= "Word #:  %d";
char *current	= "Current Average: %8.4f";
char *overall	= "Overall Average: %8.4f";
char *score	= "%02d's best score:   %s";
char *average	= "with an average: %8.4f (%d)";
char *ondate	= "on %s";
char *word_	= "Word:  ";
char *guess_	= "Guess:";
#endif

main (argc, argv)
int argc;
char *argv[];
{   int i;

    if (argc > 1) {
	if ((dict = fopen (argv[1], "r")) == NULL) {
	    printf (cant1, argv[1]);
	    exit (20);
	} else
	    strcpy (dictionary, argv[1]);
    } else if ((dict = fopen (datafile, "r")) == NULL) {
	if ((dict = fopen ("dict:words", "r")) == NULL) {
	    printf (cant2);
	    exit (20);
	} else
	    strcpy (dictionary, "dict:words");
    } else
	strcpy (dictionary, datafile);
    if ((stat (dictionary, &statbuf)) == -1) {
	printf (cant3, dictionary);
	quit (10);
    }
    if ((highscore = fopen ("high.hang", "rb+")) == NULL) {
	if ((highscore = fopen ("high.hang", "w")) == NULL) exit(1);
	strcpy (highhang, nohigh);
        time(&high_time);
	highword = 0;
	highavg = 7.33333;
    } else	highin (1);
    size = statbuf.st_size;
    srand ((int) time ((long *) 0));	/* use time as random seed */

    username ();
    do {		/* loop until user says no more */
	getword ();	/* get word from dictionary */

	set_raw ();
	raw_mode = 1;
	hangman ();	/* play one word game */
	newgame ();
	set_con ();
	raw_mode = 0;
	hightest ();
    } while (!done);

    quit (0);
}

/* highscore manager */

username ()
{   int	ch = 0;
    char getcharacter ();

    set_raw ();
    raw_mode = 1;
    scr_clear ();
    scr_curs (2, 5);
    printf(name);
    do {
	fflush(stdout);
	user[ch] = getcharacter ();
	putchar(user[ch]);
	if(user[ch]!=8) ch++; else --ch;
    } while( user[ch-1]!=13 && ch<32 );
    user[ch-1] = 0;
    set_con ();
    raw_mode = 0;
}

/* read/write a highscore-entry */
no_name ()
{   strcpy (highhang, noname);
    time(&high_time);
    highword = wordno+1;
    highavg = 7.33333;
}

highin (int words)
{   if( fseek ( highscore,
		(words/10)*(32+sizeof(long)+sizeof(int)+sizeof(float)),
		SEEK_SET ) )
    {   no_name ();
	return (0);
    }
    if (fread (highhang, sizeof(char), 32, highscore)!=32)
    {   no_name ();
	return (0);
    }
    if (fread (&highavg, sizeof(float), 1, highscore)!=1)
    {   no_name ();
	return (0);
    }
    if (fread (&high_time, sizeof(long), 1, highscore)!=1)
    {   no_name ();
	return (0);
    }
    if (fread (&highword, sizeof(int), 1, highscore)!=1)
    {   no_name ();
	return (0);
    }
    return (1);
}

highout (int words)
{   int put;

    fseek (highscore, (words/10)*(32+sizeof(long)+sizeof(int)+sizeof(float)), SEEK_SET);
    put = fwrite (highhang, sizeof(char), 32, highscore);
    put = fwrite (&highavg, sizeof(float), 1, highscore);
    put = fwrite (&high_time, sizeof(long), 1, highscore);
    put = fwrite (&highword, sizeof(int), 1, highscore);
}

hightest ()
{   int	temp=1;

    if ((((wordno+1)/10)*10)-1==wordno)
    {	highin (wordno+1);	 return;	}
    if (curavg - highavg >= 0.0001 ) return();
    if (highavg - curavg <= 0.0001 && wordno <= highword) return();
    strcpy (highhang, user);
    highavg = curavg;
    highword = wordno;
    high_time = time (&high_time);
    highout(wordno+1);
}

/* get a random number. */

long getran ()
{   time_t time ();
    unsigned long i, j;

    i = rand ();
    j = rand ();
    i += 65536 * j;	/* fabricate a random long int */
    return (i);
}

/* get a word from the dictionary for use in the game */

getword ()
{
    register int i, len;
    long num;
    long position;

    for (;;) {
	num = getran ();	/* get an random long integer */ 
	position = num % size;

	if ((fseek (dict, position, 0)) != 0) {
	    printf (cant4, dictionary);
	    quit (10);
	}
	fgets (word, 29, dict);
	if (fgets (word, 29, dict) == NULL)
	    continue;

	if ((len = strlen (word) - 1) < 4)
	    continue;

	for (i = 0; i < len; i++)
#ifdef GERMAN
	    if (word[i] < 'A' || word[i] > 'Z')
#else
	    if (word[i] < 'a' || word[i] > 'z')
#endif
		goto nogood;
	word[len] = '\0';
	return;			/* return word found */
nogood:
	continue;
    }
}

/* play one word game, return as soon as guesses used up or word guessed.
 * Display a congratulatory message before exiting if player guessed the
 * word, else display the word.
 */

hangman ()
{   char guess;			/* the current guess */
    int i;
    int togo;			/* number of letters still to be guessed */
    int wrong = 0;		/* number of incorrect guesses */

    nextword ();
    redraw ();

    togo = strlen (word);
    while (togo > 0) {		/* haven't guessed all the characters */
	int dup = 0;		/* if letter was guessed before */
	for (;;) {		/* do until guess is a character */
	    scr_curs (12, PLACE_IN);
	    scr_eol ();
	    fflush (stdout);

	    guess = TOXXXER(getcharacter ());
	    putc (guess, stdout);
	    if (guess == '\f')	/* redraw screen if ^L */
		redraw	();
	    else if (!isalpha (guess)) { /* disallow non-alpha characters */
		scr_beep ();
		scr_curs (12, PLACE_IN);
		scr_eol ();
		scr_curs (13, 0);
		fputs (valid, stdout);
		if (guess < ' ') {
		    char bad[4];

		    bad[0] = '^';
		    bad[1] = guess + 'A' - 1;
		    bad[2] = '\0';
		    fputs (bad, stdout);
		} else
		    putc (guess, stdout);
	    } else
		break;			/* drop out of loop if alpha */
	}

	for (i = 0; guessed[i]; i++)
	    if (guess == guessed[i]) {
		dup = 1;
		break;
	    }
	scr_curs (13, 0);
	scr_eol ();
	if (dup) {			/* if previously guessed */
	    printf (already, guess);
	    dup = 0;
	} else {
	    int inword = 0;		/* if guess in word */
	    for (i = 0; word[i]; i++) {
		if (guess == word[i]) {
		    inword = 1;
		    togo--;
		    blimp[i] = guess;
		    scr_curs (11, i+8);
		    putc (guess, stdout);	/* place letter in word */
		    if (togo == 0) {		/* if word completed */
			insert (guess);
			scr_curs (13, 0);
			fputs (gotit, stdout);
			return;
		    }
		}
	    }
	    if (!inword) {
		wrong++;			/* scorekeeping */
		totalwrong++;
		curavg += 1.0 / ((float) wordno);
		scr_curs (6, 49);	/* update current average on screen */
		printf ("%8.4f", curavg);
		switch (wrong) {
		case 1: scr_curs (3, 10);
			putc ('O', stdout);
			dead_man[0][1] = 'O';
			break;
		case 2: scr_curs (4, 10);
			putc ('|', stdout);
			dead_man[1][1] = '|';
			break;
		case 3: scr_curs (5, 10);
			putc ('|', stdout);
			dead_man[2][1] = '|';
			break;
		case 4: scr_curs (6, 9);
			putc ('/', stdout);
			dead_man[3][0] = '/';
			break;
		case 5: scr_curs (4, 9);
			putc ('/', stdout);
			dead_man[1][0] = '/';
			break;
		case 6: scr_curs (4, 11);
			putc ('\\', stdout);
			dead_man[1][2] = '\\';
			break;
		case 7: scr_curs (6, 11);
			putc ('\\', stdout);
			dead_man[3][2] = '\\';
			scr_curs (13, 0);
			fputs (solution, stdout);
			fputs (word, stdout);
			return;
		}
	    }
	    insert (guess);
	}
    }
    return;
}

/* game over; inquire from player whether to play another */

newgame ()
{   char c;

    scr_curs (14, 0);
    fputs (another, stdout);
    for (;;) {
	fflush (stdout);
	c = tolower(getcharacter ());
	if (c == 'n') {
	    done = 1;
	    return;
	} else if (c == 'y' || c == 'j')
	    return;
	scr_curs (15, 0);
	fputs (please, stdout);
	scr_curs (14, 15);
    }
}

/* redraw display at beginning of new word or at user request (^L) */

redraw ()
{   int i;
    struct tm *localtime ();

    scr_clear ();

    scr_curs (1, 5);
    fputs ("______", stdout);

    scr_curs (2, 5);
    fputs ("|    |", stdout);

    scr_curs (3, 5);
    putc ('|', stdout);
    scr_curs (3, 10);
    putc (dead_man[0][1], stdout);
    scr_curs (3, 32);
    fputs (yet, stdout);
    fputs (guessed, stdout);

    scr_curs (4, 5);
    putc ('|', stdout);
    scr_curs (4, 9);
    for (i = 0; i < 3; i++)
	putc (dead_man[1][i], stdout);

    scr_curs (5, 5);
    putc ('|', stdout);
    scr_curs (5, 9);
    for (i = 0; i < 3; i++)
	putc (dead_man[2][i], stdout);
    scr_curs (5, 32);
    printf (wort, wordno);

    scr_curs (6, 5);
    putc ('|', stdout);
    scr_curs (6, 9);
    for (i = 0; i < 3; i++)
	putc (dead_man[3][i], stdout);
    scr_curs (6, 32);
    printf (current, curavg);

    scr_curs (7, 3);
    fputs ("__|_____", stdout);
    scr_curs (7, 32);
    printf (overall, overavg);

    scr_curs (8, 3);
    putc ('|', stdout);
    scr_curs (8, 10);
    fputs ("|___", stdout);

    scr_curs (9, 3);
    fputs ("|_________|", stdout);
    scr_curs (9, 32);
    printf (score, (wordno/10)*10, highhang);

    scr_curs (10, 32);
    printf (average, highavg, highword);
    scr_curs (11, 32);
    printf (ondate, ctime(&high_time));

    scr_curs (11, 1);
    fputs (word_, stdout);
    fputs (blimp, stdout);

    scr_curs (12, 0);
    fputs (guess_, stdout);

    fflush (stdout);
}


/* reset stuff for next word */

nextword ()
{
    int i, j;

    for (i = 0; i < 4; i++)
	for (j = 0; j < 3; j++)
	    dead_man[i][j] = ' ';

    for (i = 0; i < strlen (word); i++)
	blimp[i] = '-';
    blimp[i] = '\0';

    for (i = 0; i < 28; i++)
	guessed[i] = '\0';

    count = 0;
    wordno++;
    overavg = curavg;
    curavg = ((float) totalwrong) / ((float) wordno);
}

/* get user's input */

char getcharacter ()
{   while (!WaitForChar (Input (), 100L) &&
	   stdin->_bp >= stdin->_bend);
    return (getc (stdin));
}

/* insert letter just guessed into list of guessed letters; also update list
 * on screen for player.
 */

insert (guess)
register char guess;
{  if (count == 0) {			/* first guess ? */
	guessed[count++] = guess;
	scr_curs (3, 42);
	putc (guess, stdout);
    } else {
	register int i, j;
	for (i = 0; i < count; i++)
	    if (guessed[i] > guess)
		break;
	for (j = count; j > i; j--)
	    guessed[j] = guessed[j-1];
	guessed[i] = guess;		/* add the guess to list of */
					/* previously guessed letters*/
	scr_curs (3, i+42);
	fputs (&guessed[i], stdout);	/* update list on screen */
	count++;
    }
}

/* needed, since Manx version apparently doesn't work */

 scr_eol ()
{   static char clr[] = {0x9b, 'K', '\0'};

    fputs (clr, stdout);
}

/* in order to have a chance at cleaning up & exiting  guessed */

_abort ()
{   puts ("^C");
    quit (0);
}

quit (code)
int code;
{   if (dict)	fclose (dict);
    set_con ();
    exit (code);
}

_wb_parse () {}
