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