[comp.sources.games] v03i084: jumble2 - play the game of jumble

games-request@tekred.TEK.COM (02/19/88)

Submitted by: Dan Heller (argv) <island!argv@sun.com>
Comp.sources.games: Volume 3, Issue 84
Archive-name: jumble2

	[A previously posted game "jumble" only unscrambled words
	 that you fed it. This program plays the whole game, feeding
	 you the jumbled words and letting you try and unscramble
	 them. I compiled and ran this on a Sun 3/60.  -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile jumble.c
# Wrapped by billr@saab on Thu Feb 18 16:45:48 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(287 characters\)
sed "s/^X//" >README <<'END_OF_README'
XJumble --  the game in which you unscramble words to compete for
Xpoints on a high score list of 10 and 4 levels of
Xdifficulty to choose from. Words are found in /usr/dict/words,
Xthe basic dictionary found on all UNIX systems.
X
XThis program was written by Dan Heller.
Xisland!argv@sun.com
END_OF_README
if test 287 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(258 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X# simple makefile for jumble
Xjumble: jumble.c
X	cc -o jumble jumble.c
X
Xinstall: jumble
X	cp jumble /usr/games
X	# note: this path is wired into the code
X	# edit it if you change the path
X	touch /usr/games/lib/jumble.score
X	chmod 666 /usr/games/lib/jumble.score
END_OF_Makefile
if test 258 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f jumble.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"jumble.c\"
else
echo shar: Extracting \"jumble.c\" \(19914 characters\)
sed "s/^X//" >jumble.c <<'END_OF_jumble.c'
X/*
X * Jumble --  the game in which you unscramble words to compete for
X *   points on a high score list of 10 and 4 levels of
X *   difficulty to choose from. Words are found in /usr/dict/words,
X *   the basic dictionary found on all UNIX systems.
X * 
X *   This program was written by Dan Heller.
X *   island!argv@sun.com
X *
X *   compile normally, but if you're on a machine with 16 bit ints, use
X *   random() at the end of this file.
X * 
X */
X
X/* Include files */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <sys/time.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/*  convenience  */
X#define upper(c) (isupper(c) ? c : toupper(c))
X#define lower(c) (islower(c) ? c : tolower(c))
X#define Upper(c) (c = upper(c))
X#define Lower(c) (c = lower(c))
X#define when break;case
X#define otherwise break;default
X
X/* constants */
X#define time_left  (int)(start_time + death_time - now) /* time left to guess */
X#define NAMELEN		30                    /* length of name for scores */
X#define WORDS_NEEDED     4            /* words needed to get on score file */
X#define the_pope_is_catholic 1      /* while(1) is boring */
X
X/* other files */
X#define WORDS 		"/usr/dict/words"             /* where to get words */
X#define SCOREFILE	"/usr/games/lib/jumble.scores" /* where scores live  */
X
Xchar *getlogin();		    /* return the login name of user */
Xchar *ordinate();                   /* returns "st", "nd", etc. for numbers */
Xchar jumble_word[40];               /* what the word we're jumbling is */
Xchar jumbled[40];                   /* what the jumbled word is */
Xint debug;                          /* debug mode -- lets you cheat */
Xint num_of_words;                   /* how many words player chose to play */
Xint words_given;                    /* how many words we've given this round */
Xint passes;                         /* how many passes the player has used */
Xint total_passes;   		    /* number of passes player has */
Xint gotit;                          /* did player guess the jumbled word? */
Xint level;                          /* the difficulty of play player chose */
Xint length;                         /* length of the current jumbled word */
Xunsigned death_time;                /* time you have to unscramble words */
Xtime_t start_time;                  /* the time of the start of each round */
Xtime_t now;                         /* what time it is now */
Xlong random();			    /* see the end of this file */
Xlong DICTSIZE;                      /* the size of the dictionary */
XFILE *dictionary;                   /* file pointer to the dictionary */
X
Xunsigned alarm(), extra_credit();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    char c;
X    struct stat buf;
X    int owner, quit(), lose();
X    srandom(getpid());  /* if you're on a 16 bit machine change to srand() */
X    if (!(dictionary = fopen(WORDS, "r")))     /* open the dictionary */
X	printf("Sorry, can't think of any words right now.\n"), quit();
X    fstat(fileno(dictionary), &buf); /* get the file status of dictionary  */
X    DICTSIZE = buf.st_size;          /* got the size of the dictionary     */
X    signal(SIGALRM, lose);           /* tell the alarm what function to go */
X				     /* to if time runs out                */
X    signal(SIGINT, quit);            /* if they ctrl-c let em quit         */
X    signal(SIGQUIT,quit);            /* same as above                      */
X    owner = (stat(argv[0], &buf) != -1 && buf.st_uid == getuid());
X			 /* is the player the owner of the program? */
X    debug = (owner && argc > 1 && !strcmp(argv[1], "-x"));
X	 /* if they used a -x flag and they're the owner, enter debug mode */
X    if (argc > 1 && (!strcmp(argv[1], "-s") || !strcmp(argv[1], "-n")))
X	high_score(1,argv[1][1] == 'n' && owner), exit(0);
X	 /* if the player just wants to see the high scores or, if the owner  */
X	 /* wants to see the account names of those on the high scores list   */
X    printf("Do you want directions? ");
X    while ((c = getchar()) == ' ' || c == '\t');
X    if(c == 'y' || c == 'Y')
X	help(argv); 			      /* give help if wanted */
X    while(c != '\n')
X	c = getchar();   		      /* flush the input line */
X    get_level();                              /* find out how badly to kill */
X    while(the_pope_is_catholic) {             /* let's play */
X	get_num_of_words();                   /* how many words this round? */
X	gotit = 1;                            /* so far they're doing fine  */
X	words_given = passes = 0;             /* basic variables get values */
X	if ((total_passes = (num_of_words - (-1*level + 5))) < 0)
X	    total_passes = 0;
X	death_time = (long)55.0 * num_of_words * (1+level / 1.5);
X	(void) time(&start_time);             /* game starts now */
X	(void) alarm(death_time);             /* alarm goes off in deathtime */
X	printf("%d word%s coming.\n\n", 
X		num_of_words, (num_of_words > 1) ? "s" : "");
X	printf("You have %d pass%s.\n", 
X	     total_passes, (total_passes == 1) ? "" : "es");
X	while (gotit > 0 && words_given < num_of_words) {
X	    find_a_word();
X	    mixit();
X	    words_given++;   /* we're giving another word... */
X	    printit();
X	    if(!letemguess()) words_given--;
X		  /* letemguess returns 0 if they passed this word */
X	    else if(words_given - WORDS_NEEDED > 0) {
X		printf("\nExtra time for extra words: ");
X		/* reset alarm */
X		(void) alarm(alarm((unsigned)0) + extra_credit());
X	    }
X	/* they got it so give them extra credit for words
X					 gotten after words needed */
X	}
X	if (gotit != -1) while(passes < total_passes) {
X	    words_given++;  /* for each pass, give away a free word */
X	    printf("Bonus for unused pass #%d: ",++passes);
X	    start_time += extra_credit();
X	}
X	time(&now); /* get current time and check to see if the current */
X		    /* score made it.  This is based on time taken to   */
X		    /* unscramble all the words */
X	and_now();  /* we'll talk about it when we get there */
X    }
X}
X
X/*
X * get the number of words they wish to unscramble.  They have to
X * choose at least WORDS_NEEDED to get on the high score file and
X * have to choose at least 1 to play (or to exit procedure)
X */
Xget_num_of_words()
X{
X    char buf[15];
X    num_of_words = 0;
X    printf("You need at least %d words to get on the top ten.\n", WORDS_NEEDED);
X    printf("You get extra time for unscrambling each word over the %d%s word.\n"
X	,WORDS_NEEDED, ordinate(WORDS_NEEDED));
X    printf("How many words do you want? ");
X    while(num_of_words <= 0)
X	if(strlen(gets(buf)) < 1 || (num_of_words = atoi(buf)) <= 0) 
X	    printf("Use numbers (greater than 0), please: ");
X    putchar('\n');
X}
X
X/*
X * just a bunch of print statments describing how to play
X */
Xhelp(argv)
Xchar **argv;
X{
X    /* get their login name and scramble it */
X    (void) strcpy(jumble_word, getlogin());
X    mixit(); /* scramble it */
X    puts("\nJumble is a game in which you have to guess what the");
X    puts("jumbled word is supposed to be. For example, you would");
X    printf("have %d seconds to unscramble \"%s\" to \"%s\".\n",
X	    strlen(jumbled) * 9, jumbled, jumble_word);
X    puts("\nTyping a '?' gives your time left and reprints the jumbled word.");
X    puts("Typing a 'q' forfeits that round and tell you the current word.");
X    puts("Entering 'p' (pass) reveals the current word and goes to the next.");
X    printf("You get one free pass for every %d%s word.\n", 
X			WORDS_NEEDED, ordinate(WORDS_NEEDED));
X    printf("For each word after the %d%s that you successfully unscramble,\n", 
X				    WORDS_NEEDED, ordinate(WORDS_NEEDED));
X    puts("you get bonus time. That time increases more for each bonus word.");
X    puts("Some words can be constructed different ways. If your guess is a");
X    puts("word that works with the given letters but isn't accepted,");
X    puts("try a different word.");
X    puts("Since you are not penalized for wrong guesses, it is advisable");
X    puts("to retype words to get the letters in a different order.");
X    puts("You can see the high scores without playing by typing:");
X    printf("%% %s -s\n",argv[0]);
X}
X
X/*
X * get one of four differnt difficulty levels
X * level expert finds words that are long and gives you little time
X * to unscramble them and easy gives you little words with lots of
X * time to unscramble them and the other levels are somewhere in between
X * see find_a_word() for details
X */
Xget_level()
X{
X    char c;
X    level = 0;
X    puts("\nDifficulty levels:");
X    printf("(E)xpert, (H)ard, (M)oderate or (S)imple? ");
X    while(!level)
X	switch(c = getchar()) {
X	    when ' ' : case '\t' : break;
X	    when 'E' : case 'e' :  /* Expert */
X		 level = 1;
X	    when 'H' : case 'h' :  /* Hard */
X		 level = 2;
X	    when 'M' : case 'm' :  /* Moderate */
X		 level = 3;
X	    when 'S' : case 's' :  /* Simple */
X		 level = 4;
X	    otherwise : while(c != '\n') c = getchar(); 
X				printf("E, H, M, or S: ");
X	}
X    while(getchar() != '\n');  /* flush input stream */
X    putchar('\n');
X}
X
X/*
X * finds a random word in the dictionary.
X * first, go to random spot in the dictionary
X * then, read till the next word --  we will probably have landed
X * in the middle of a word so read till a carriage return.
X * Now, we either got to a new word, or the end of the dictionary, 
X * in which case we'll try again.
X * Else, we have our word in "jumble_word".
X * Next, find out how long the word is and if it's appropriate for 
X * the level of play, set local variable "Ok" to true and exit.
X * we don't want words with upper case letters or numbers.
X */
Xfind_a_word()
X{
X    int c, ok;
X    printf("Jumbling a word...");
X    do  {
X	ok = 0;
X	(void) fseek(dictionary, (random() % DICTSIZE), 0); 
X	while((c = getc(dictionary)) != EOF && c != '\n') ;
X	if(fscanf(dictionary, "%s", jumble_word) == EOF)
X	    { clearerr(dictionary); continue; }
X	if((length = strlen(jumble_word)) >= 5)
X	    switch(level)  {         
X		when 4 : if(length <= 6) ok = 1;
X		when 3 : if(length <= 7) ok = 1;
X		when 2 : if(length <= 8) ok = 1;
X		when 1 : ok = 1;  /* anything goes */
X	    }
X    } while (isupper(jumble_word[0]) || isdigit(jumble_word[0]) || !ok);
X    putchar('\n');
X}
X
X/*
X * mixit scrambles a word 
X * "tries" is static -- the function is recursive; if it can't scramble
X * a word the first time, we call it again from within itself.
X * First, convert the entire word to upper case letters, so that we know
X * which letters have been used and which haven't been used.  When
X * searching for a letter to randomly pick, find one that is upper case
X * put it in a random place in the jumbled word variable and change it
X * to lower case.
X * Sometimes, altho, not often, we randomly scramble a word 5 times and
X * each time, it comes out the same as the regular word.
X */
Xmixit()
X{
X    static int tries = 0;
X    int count = 0, rnd_num;
X    length = count = strlen(jumble_word);
X    while( count-- )
X	Upper(jumble_word[count]);
X    count = 0;  /* reset count; -1 after loop */
X    while( count < length ) {
X	do rnd_num = random() % length;
X	while(islower(jumble_word[rnd_num]));
X	jumbled[count++] = Lower(jumble_word[rnd_num]);
X    }
X    jumbled[count] = 0; /* terminate string with a NULL character */
X    if (!strcmp(jumble_word, jumbled)) tries++; /* is it different? */
X    if (tries > 5) printf("I can't seem to jumble this word: %s\n",jumbled);
X    else if(tries) mixit();
X    tries = 0;
X}
X
X/*
X * prints info -- the word you need to unscramble, how much time left
X * before you lose, etc...
X */
Xprintit()
X{
X    int count = num_of_words - words_given;
X    time(&now);
X    printf(" Time: %D min. %D sec.\n%d word%s left. ",
X	   time_left / 60, time_left % 60, count, (count != 1) ? "s" : "");
X    printf("'q' to quit");
X    if(passes < total_passes)
X	printf(", 'p' to pass (%d)", total_passes - passes);
X    puts(".");
X    printf("\n Word #%d: \"%s\".\n", words_given, jumbled);
X}
X
X/*
X * letemguess -- if time runs out, they lose. if they type a '?'
X * goto printit above. if 'q', terminate this round and tell them
X * the word. if they guess it, great.  if in debug mode, user types
X * 'x' and the jumbled word will be printed in parens.
X * if they type "pass", and they have passes, tell them word and
X * continue with game.
X */
Xletemguess()
X{
X    char guess[80];
X    gotit = 0;
X    printf("--->  Guess: ");
X    while(!gotit && gets(guess))
X	if (time_left <= 0) return 1;
X	else if (!strlen(guess)) printf("Guess: ");
X	else if (!strcmp(guess, "?")) printit(), printf("Guess: ");
X	else if (!strcmp(guess, "q") || !strcmp(guess, "Q")) gotit = -1;
X	else if(!strcmp(guess, jumble_word)) gotit = 1;
X	else if(debug && !strcmp(guess,"x")) printf("(%s)\n",jumble_word);
X	else if(!strcmp(guess,"p"))
X	    if (passes >= total_passes)
X		printf("You have no passes.\nGuess: ");
X	    else {
X		passes++;
X		printf("Word was: \"%s\"\nYou have %d pass%s left.\n\n",
X		      jumble_word, total_passes - passes, 
X		      (total_passes - passes == 1) ? "" : "es");
X		gotit = 1; /* gotit = 1 so as not to exit main loop */
X		return 0;  /* return 0 if passed. */
X	    }
X	else printf("Nope. Guess: ");
X    if(strcmp(guess, jumble_word))
X	clearerr(stdin), gotit = -1; /* in case some jerk ^D's */
X    time(&now);            /* get current time */
X    if (gotit == -1) lose();
X    else printf("You got it!\n");
X    return 1;
X}
X
X/*
X * they lost either by no more time left, or they typed 'q' above.
X * set alarm(0) to turn off alarm clock and get the current time.
X * if the time_left is 0, time ran out...
X * tell them word.
X */
Xlose()
X{
X    (void) alarm(0);
X    (void) time(&now);
X    if (gotit == 1) return;  /* we're in limbo, between words, whatever... */
X    if (time_left < 1)
X	puts("Time ran out.");
X    printf("The word was \"%s\".\n", jumble_word);
X    if (time_left < 1)
X	fprintf(stderr, "--Hit RETURN--"), gotit = -1;
X}
X
X/*
X * we're done with the last round, what does player want to do next?
X */
Xand_now()
X{
X    if (gotit > 0) {
X	int foo = (death_time - time_left);
X	printf("Your score: %d word%s in %D min. %D sec.  (%2.2f words/min.)\n",
X		 num_of_words, (num_of_words == 1) ? "" : "s", foo / 60,
X		 foo % 60, (float)(words_given / (foo / 60.0)));
X	high_score(0,0);
X    }
X    printf("\nRETURN to Play again, (C)hange skill level, (S)cores, (Q)uit: ");
X    while(the_pope_is_catholic)
X	switch(getchar()) {
X	    case ' ' : case '\t' : break;
X	    case '\n' :           /* play again, no changes */
X		return;
X	    case 'C' : case 'c' : /* get new level of play */
X		while(getchar() != '\n'); get_level(); return;
X	    case 'Q' : case 'q' : /* quit */
X		quit();
X	    case 'S' : case 's' : /* look at the scores */
X		high_score(1,0);
X	    default : while(getchar() != '\n');
X		printf("<cr>, 'c', 's', or 'q': ");
X	}
X}
X
Xstruct scores {
X    char sc_name[NAMELEN];       /* this is what they put on the high scores */
X    char sc_login[8];            /* this is for their login names */
X    long sc_time;                /* time taken for that game      */
X    int sc_words;                /* how many words did they play with */
X    int sc_level;                /* the level of play for that person */
X} top_ten[10];                   /* we only want 10 high scores.  */
X
Xhigh_score(Read, names)
Xshort Read, names;   /* should we Read the scores, or attempt to enter one */
X{
X    struct scores *scp, *temp; /* scp is a score pointer, and temp is */
X			       /* for temporary storage */
X    FILE *scorefp; /* file descriptor of the score file */
X
X    /*
X     * fill all ten entries with nothing in case the high score file
X     * isn't completely filled.
X     */
X    for (scp = top_ten; scp < &top_ten[10]; scp++) {
X	scp->sc_name[0] = 0;
X	scp->sc_login[0] = 0;
X	scp->sc_time = 0;
X	scp->sc_words = 0;
X	scp->sc_level = 0;
X    }
X
X    /*
X     * open score file for concurrent read/write access and
X     * read the top ten file into the array 
X     */
X    if ((scorefp = fopen(SCOREFILE, "r+")) == 0)
X	{ perror(SCOREFILE); return; }
X    (void) fread((char *)top_ten, sizeof(struct scores), 10, scorefp);
X
X    if (Read) {      /* Print the list */
X	printf("Top Jumblers:\n");
X	printf("Level\tWords\t\tTime\t    words/min\tName\n");
X        for (scp = top_ten; scp < &top_ten[10]; scp++) 
X		/* print only those scores that are valid (not 0)   */
X	    if (scp->sc_words >= WORDS_NEEDED && scp->sc_login[0]) {
X	        printf("%s\t%3d\t  %2D Min. %2D sec.     %2.2f\t%s",
X		(scp->sc_level == 1) ? 
X		    "Expert" : (scp->sc_level == 2) ?
X		    "Hard"   : (scp->sc_level == 3) ?
X		    "Medium" : "Easy",
X		scp->sc_words, scp->sc_time / 60, scp->sc_time % 60, 
X		(float)(scp->sc_words / (scp->sc_time / 60.0)), scp->sc_name);
X		if (names)
X		    printf("(%s)", scp->sc_login);
X		putchar('\n');
X	    }
X	    else break;
X    }
X    /* check to see if current score made it */
X    else if (num_of_words >= WORDS_NEEDED) {
X	int count = 0;
X	signal(SIGQUIT, SIG_IGN); /* we don't want them to leave just yet */
X	signal(SIGINT, SIG_IGN);
X	for (scp = top_ten; scp < &top_ten[10]; scp++)
X	    if (++count && value_cmp(scp))   /* check function value_cmp */
X		break;      /* if it returns true, current score made it */
X	if (scp < &top_ten[10]) {
X	    /*
X	     * move all the other scores down one in the list
X	     */
X	    for (temp = &top_ten[9]; temp > scp; temp--)
X		*temp = *(temp-1);
X	    /* 
X	     * enter currnet values into list
X	     * and get his name
X	     */
X	    scp->sc_words = num_of_words;
X	    scp->sc_level = level;
X	    scp->sc_time = death_time - time_left;
X	    (void) strcpy(scp->sc_login, getlogin());
X	    printf("You made the top ten! (#%d) Enter a name (%d chars): ",
X			  count, NAMELEN);
X	    fgets(scp->sc_name, NAMELEN, stdin);
X	    scp->sc_name[strlen(scp->sc_name) - 1] = 0;
X	    /*
X	     * if they didn't type anything, don't enter the score
X	     * else, rewind the file and enter the new score list
X	     */
X	    if (!strlen(scp->sc_name)) 
X		printf("No name, no entry.\n");
X	    else {
X		rewind(scorefp);
X	if (fwrite((char *)top_ten, sizeof(struct scores), 10, scorefp) <= 0)
X		    perror(SCOREFILE);
X	    }
X	}
X	/*
X	 * They didn't make the top ten, so apologize
X	 * Reset the signal interrupt handlers and close the score file
X	 */
X	else printf("Good job. Sorry you didn't make the top ten.\n");
X	signal(SIGQUIT, quit);
X	signal(SIGINT, quit);
X	(void) fclose(scorefp);
X    }
X}
X
X/*
X * used to compare the current values of the game just played with
X * whatever the values are in the current high score entry. If the
X * time for the entry is 0, return true because it's a garbage score.
X * Also, chekc for difficulty levels -- more difficult levels always
X * win over less difficult levels.
X * next, check to see if the average words per minute is better than
X * that of the current score entry.
X */
Xvalue_cmp(entry)
Xstruct scores *entry;
X{
X    if(!entry->sc_time) return 1;
X    if(level != entry->sc_level) return(level < entry->sc_level);
X    return (num_of_words / (float)(death_time - time_left) >=
X	 entry->sc_words / (float)(entry->sc_time));
X}
X
X/* quit -- ignore signals, close files, go away */
Xquit()
X{
X    signal(SIGQUIT, SIG_IGN);
X    signal(SIGINT,  SIG_IGN);
X    putchar('\n');
X    (void) fclose(dictionary);
X    exit(0);
X}
X
X/*
X * extra crdit given to those brave enough to request more words than
X * needed as incentive to play for higher scores.  The extra credit is
X * given in time (seconds).
X * return the actual amount of time awarded
X */
Xunsigned
Xextra_credit()
X{
X    int extra;
X    if (level == 4) extra = 1 + random() % 5;
X    else extra = ((words_given - WORDS_NEEDED) * ((-1 * level) + 4) * 3);
X    printf("%d min. and %d sec. bonus time.\n", extra / 60, extra % 60);
X    return extra;
X}
X
X/* ordinate() adds "th" etc... on the end of numbers */
Xchar *
Xordinate(num)
Xint num;
X{
X    if(num % 100 < 21 && num % 100 > 3) return "th";
X    switch(num % 10) {
X	case 1 : return "st";
X	case 2 : return "nd";
X	case 3 : return "rd";
X	default : return "th";
X    }
X}
X
X/*
XIf you're on a PDP, use this! if You're on a sun, it'll crash your system!
Xlong random()
X{
X    return rand() * 65535L + rand() * 256L  + rand();
X}
X*/
X
END_OF_jumble.c
if test 19914 -ne `wc -c <jumble.c`; then
    echo shar: \"jumble.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0