Newsgroups: comp.sources.unix From: lm03_cif@uhura.cc.rochester.edu (Larry Moss) Subject: v25i090: letters - game to improve typing skills Sender: sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: lm03_cif@uhura.cc.rochester.edu (Larry Moss) Posting-Number: Volume 25, Issue 90 Archive-Name: letters Here is "letters", the "Letter Invader" game. It is pretty cute. It is less stressful than Tetris, but it might be a good diversion while waiting for something to compile. Abstract (lifted from the man page): Letters is based on Letter Invaders which was around in the PC environment several years ago. It in turn was based on the popular arcade style game, Space Invaders. For those not familiar with Space Invaders (please let me know if you're one of these people and let me know what planet you've been living on :-) the idea is to blast aliens out of the sky as they attempt to land on and "kill" you. Since this is a game to improve typing skills, the aliens are words selected randomly from the dictionary. You blast the aliens out of the sky by typing them correctly. Nick #! /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 'config.h' <<'END_OF_FILE' X/* X * configurable stuff in letters. Most things here probably shouldn't need X * to be changed but are here because someone may want to tinker with this X * stuff to affect the way it performs on different systems. The stuff X * most likely to require changes is at the top of the file. X */ X#ifndef DICTIONARY X#define DICTIONARY "/usr/dict/words" X#endif X X#ifndef HIGHSCORES X#define HIGHSCORES "letters.high" X#endif X X/* X * probably best to leave these so it's the same everywhere. Otherwise, X * anyone with an xterminal is likely to get higher scores. X */ X#ifdef AMIGA X#define SCREENLENGTH 22 X#else X#define SCREENLENGTH 23 X#endif X#define SCREENWIDTH 80 X X/* X * initial delay in usecs before words move to the next line X */ X#define START_DELAY 750000 X X/* X * this implements "graduated" (non-linear) decreasing delay times: X * each level, delay gets reduced by smaller and smaller amounts X * (eventually, when delay would get below PAUSE, it is simply set to PAUSE) X * X * if you change START_DELAY or DELAY_CHANGE, DECEL must be tuned carefully, X * otherwise DELAY(lev) will drop suddenly to PAUSE at some point X */ X#define DELAY_CHANGE 60000 X#define DECEL 1200 X#define DELAY(lev) ( ((lev)*DECEL > DELAY_CHANGE/2) ? PAUSE :\ X (START_DELAY-(DELAY_CHANGE-(lev)*DECEL)*(lev)) ) X X/* X * number of words to be completed before level change X */ X#define LEVEL_CHANGE 15 X X/* X * length of pause before reading keyboard again (in usecs). There has to X * be some pause. X */ X#define PAUSE 10000 X X/* X * This is how likely it is that another word will appear on the screen X * while words are falling. there isa 1/ADDWORD chance of a new word X */ X#define ADDWORD 6 X X/* X * length of words in bonus round X */ X#define BONUSLENGTH 10 END_OF_FILE if test 1720 -ne `wc -c <'config.h'`; then echo shar: \"'config.h'\" unpacked with wrong size! fi # end of 'config.h' fi if test -f 'highscore.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'highscore.c'\" else echo shar: Extracting \"'highscore.c'\" \(3677 characters\) sed "s/^X//" >'highscore.c' <<'END_OF_FILE' X/* X * read and update high score file for Letter Invaders X * X * copyright 1991 by Larry Moss (lm03_cif@uhura.cc.rochester.edu) X */ X X#include X#ifdef AMIGA X# include X# include X#else X# include X#endif X#include X#include X#include "config.h" X#include "term.h" X#include "terms.h" X#include "kinput.h" X Xstruct score_rec { X char name[9]; X int level, words, score; X}; X Xstatic char highscores[] = HIGHSCORES; X Xextern unsigned score, word_count, level; X Xstatic struct score_rec high_scores[10]; Xstatic struct stat s_buf; Xtime_t readtime; X Xvoid read_scores() { X int i; X FILE *fp; X X /* X * get the last modified time so we know later if we have to reread X * the scores before saving a new file. X */ X if(stat(highscores, &s_buf) == -1) { X fprintf(stderr, "Cannot stat %s: ", highscores); X perror(""); X exit(1); X } X X readtime = s_buf.st_mtime; X X if((fp = fopen(highscores, "r")) == NULL) { X fprintf(stderr, "Cannot open %s: ", highscores); X perror(""); X exit(1); X } X X for(i = 0; i < 10; i++) { X fscanf(fp, "%s%d%d%d", high_scores[i].name, X &high_scores[i].level, &high_scores[i].words, X &high_scores[i].score); X } X X fclose(fp); X} X Xint write_scores() { X int i; X FILE *fp; X X /* X * check to make sure the high score list has not been modified X * since we read it. X */ X if(stat(highscores, &s_buf) == -1) { X fprintf(stderr, "Cannot stat %s: ", highscores); X perror(""); X setterm(ORIG); X exit(1); X } X X if(s_buf.st_mtime > readtime) X return -1; X X if((fp = fopen(highscores, "w")) == NULL) { X fprintf(stderr, "Cannot write to %s: ", highscores); X perror(""); X setterm(ORIG); X exit(1); X } X X for(i = 0; i < 10; i++) { X fprintf(fp, "%s %d %d %d", high_scores[i].name, X high_scores[i].level, high_scores[i].words, X high_scores[i].score); X } X X fclose(fp); X} X X X Xvoid update_scores() { X int i, j; X#ifdef AMIGA X char *u; X#else X struct passwd *p; X#endif X X for(i = 0; i < 10; i++) X if(score > high_scores[i].score) { X for(j = 10; j > i; j--) { X strcpy(high_scores[j].name, high_scores[j-1].name); X high_scores[j].words = high_scores[j-1].words; X high_scores[j].score = high_scores[j-1].score; X high_scores[j].level = high_scores[j-1].level; X } X#ifdef AMIGA X if((u = getenv("USER")) == NULL) { X char buffer[128],*p; X clrdisp(); X gotoxy(18, 5); X cooked(stdin); X fputs("Enter your name (8 char. max, no spaces): ",stdout); X fflush(stdout); X gets(buffer); X if((p=strchr(buffer,' '))) X *p='\0'; X if(!buffer[0]) X strcpy(buffer,"nobody"); X strncpy(high_scores[i].name, buffer, 8); X raw(stdin); X } else X strcpy(high_scores[i].name, u); X#else X if((p = getpwuid(getuid())) == NULL) X strcpy(high_scores[i].name, "nobody"); X else X strcpy(high_scores[i].name, p->pw_name); X#endif X high_scores[i].score = score; X high_scores[i].words = word_count; X high_scores[i].level = level; X if(write_scores() == -1) { X read_scores(); X update_scores(); X } X break; X } X} X Xvoid show_scores() { X int i; X X clrdisp(); X gotoxy(18, 5); X highlight(1); X printf("Top Ten Scores for Letter Invaders"); X highlight(0); X gotoxy(20, 7); X underline(1); X printf(" name level words score"); X underline(0); X X for(i = 0; i < 10; i++) { X gotoxy(18, 8 + i); X printf("%3d %-10s%5d%6d%6d", i+1, high_scores[i].name, X high_scores[i].level, high_scores[i].words, X high_scores[i].score); X } X X printf("\n"); X#ifdef AMIGA X { X char bogus; X fputs("\nPress RETURN... ",stdout); X fflush(stdout); X bogus=getchar(); X } X#endif X} END_OF_FILE if test 3677 -ne `wc -c <'highscore.c'`; then echo shar: \"'highscore.c'\" unpacked with wrong size! fi # end of 'highscore.c' fi if test -f 'kinput.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'kinput.c'\" else echo shar: Extracting \"'kinput.c'\" \(3666 characters\) sed "s/^X//" >'kinput.c' <<'END_OF_FILE' X/* X * do non-blocking keyboard reads X * X * copyright 1991 by Larry Moss (lm03_cif@uhura.cc.rochester.edu) X */ X X#include X#include X#include X/* I know some systems prefer time.h, but I'm not sure which */ X#include X#ifdef SYSV X#include X#else /* SYSV */ X#include X#endif /* SYSV */ X X#include "kinput.h" X#include "terms.h" X#include "term.h" X X#define SCREENLENGTH 24 X Xextern unsigned int score, word_count, level; X Xint key_pressed(); X Xstatic interrupt(); X X#ifndef NOJOB Xstatic pause(); Xstatic cont(); X#endif X Xstatic die(); X X/* X * This function will return -1 if no key is available, or the key X * that was pressed by the user. It is checking stdin, without blocking. X */ Xint key_pressed() X{ X#ifdef SYSV X int chars_read; X static char keypressed; X X chars_read = read(0, &keypressed, 1); X if (chars_read == 1) X return((int)keypressed); X return(-1); X#else /* SYSV */ X int mask = 1, chars_read; X static char keypressed; X struct timeval waittime; X X waittime.tv_sec=0; X waittime.tv_usec=4; X if (select(1, &mask, 0, 0, &waittime)) { X chars_read = read(0, &keypressed, 1); X if (chars_read == 1) X return((int)keypressed); X } X return(-1); X#endif /* SYSV */ X} X X/* X * Set the terminal to cbreak mode, turn off echo and prevent special X * characters from affecting the input. We will handle EOF and other fun X * stuff our own way. Backup copies of the current setup will be kept X * to insure the terminal gets returned to its initial state. X */ Xvoid setterm(setting) Xint setting; X{ X#ifdef SYSV X struct termio termrec; X static struct termio old_termrec; X#else /* SYSV */ X struct sgttyb termrec; X struct tchars trec; X static struct tchars old_trec; X static struct sgttyb old_termrec; X#endif /* SYSV */ X X if(setting == NEW) { X signal(SIGINT, interrupt); X signal(SIGQUIT, die); X signal(SIGTERM, die); X#ifndef NOJOB X signal(SIGTSTP, pause); X signal(SIGCONT, cont); X#endif /* NOJOB */ X X#ifdef SYSV X ioctl(0, TCGETA, &termrec); X old_termrec = termrec; X termrec.c_iflag &= ~(IGNPAR|PARMRK|INLCR|IGNCR|ICRNL); X termrec.c_iflag |= BRKINT; X termrec.c_lflag &= ~(ICANON|ECHO); X termrec.c_cc[VTIME] = 0; X termrec.c_cc[VMIN] = 0; X ioctl(0, TCSETAF, &termrec); X#else /* SYSV */ X ioctl(0, TIOCGETP, &termrec); X old_termrec = termrec; X termrec.sg_flags |= CBREAK; X termrec.sg_flags &= ~ECHO; X ioctl(0, TIOCSETP, &termrec); X X ioctl(0, TIOCGETC, &trec); X old_trec = trec; X trec.t_eofc = (char) -1; X trec.t_quitc = (char) -1; X ioctl(0, TIOCSETC, &trec); X#endif /* SYSV */ X } X else { X signal(SIGINT, SIG_DFL); X signal(SIGQUIT, SIG_DFL); X signal(SIGTERM, SIG_DFL); X#ifndef NOJOB X signal(SIGTSTP, SIG_DFL); X#endif /* NOJOB */ X X#ifdef SYSV X ioctl(0, TCSETAF, &old_termrec); X#else /* SYSV */ X ioctl(0, TIOCSETP, &old_termrec); X ioctl(0, TIOCSETC, &old_trec); X#endif /* SYSV */ X } X} X X X/* X * Interrupt handlers X */ X X#ifndef NOJOB Xstatic pause(sig) Xint sig; X{ X#ifdef SYSV X signal(sig, interrupt); X#endif /* SYSV */ X gotoxy(0, SCREENLENGTH); X setterm(ORIG); X putchar('\n'); X kill(getpid(), SIGSTOP); X} X Xstatic cont(sig) Xint sig; X{ X#ifdef SYSV X signal(sig, interrupt); X#endif /* SYSV */ X setterm(NEW); X redraw(); X} X X#endif /* NOJOB */ X Xstatic interrupt(sig) Xint sig; X{ X char c; X X setterm(ORIG); X printf("\n\rare you sure you want to quit? "); X if((c = getchar()) == 'y' || c == 'Y') { X clrdisp(); X printf("\n\nfinal: score = %u\twords = %u\t level = %d\n", X score, word_count, level); X highlight(0); X exit(1); X } else { X#ifdef SYSV X signal(sig, interrupt); X#endif /* SYSV */ X setterm(NEW); X redraw(); X } X} X Xstatic die(sig) Xint sig; X{ X setterm(ORIG); X clrdisp(); X highlight(0); X exit(1); X} END_OF_FILE if test 3666 -ne `wc -c <'kinput.c'`; then echo shar: \"'kinput.c'\" unpacked with wrong size! fi # end of 'kinput.c' fi if test -f 'kinput.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'kinput.h'\" else echo shar: Extracting \"'kinput.h'\" \(323 characters\) sed "s/^X//" >'kinput.h' <<'END_OF_FILE' X/* X * header file for: X * kinput.c: Larry Moss (lm03_cif@cc.rochester.edu) X * Fall, 1990 X * X */ X X#define BACKSPACE fputs("\b \b", stdout) X#define DEL 127 X#define WERASE 23 X#define KILL 21 X#define TAB 9 X#define ESC 27 X#define CTL(c) (c & 037) X#define MAXLENGTH 1024 X#define NEW 1 X#define ORIG 0 X Xvoid setterm(); END_OF_FILE if test 323 -ne `wc -c <'kinput.h'`; then echo shar: \"'kinput.h'\" unpacked with wrong size! fi # end of 'kinput.h' fi if test -f 'letters.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'letters.c'\" else echo shar: Extracting \"'letters.c'\" \(12891 characters\) sed "s/^X//" >'letters.c' <<'END_OF_FILE' X/* X * letters.c: A simple game to help improve typing skills. X * X * copyright 1991 Larry Moss (lm03_cif@uhura.cc.rochester.edu) X */ X X#define CTRL(c) (c & 037) X#define TRUE 1 X#define FALSE 0 X X#include X#include X#include X#ifdef AMIGA X# include X#endif X#include "config.h" X#include "kinput.h" X#include "terms.h" X#include "term.h" X X#if defined(SYSV) X# define srandom srand48 X# define random lrand48 X#endif X Xstruct s_word { X struct s_word *nextword; X int posx; X int posy; X int length; X int drop; X int matches; X char word[1]; /* extensible */ X}; X Xint move(); Xvoid putword(); Xint game(); Xvoid redraw(); Xvoid erase(); Xvoid status(); Xvoid new_level(); Xvoid banner(); Xstruct s_word *newword(); Xstruct s_word *searchstr(), *searchchar(); Xvoid kill_word(); Xint (*ding)(); X Xchar *malloc(); Xvoid free(); X X/* X * There are too many globals for my taste, but I took the easy way out in X * a few places X */ Xunsigned int score = 0; Xunsigned int level = 0; Xint levels_played = -1; Xunsigned int word_count = 0; Xstatic int lives = 2; Xstatic long delay; Xstruct s_word *words, *lastword, *prev_word; Xint bonus = FALSE; /* to determine if we're in a bonus round */ Xint wpm = 0; Xint letters = 0; X Xvoid usage(progname) Xchar *progname; X{ X fprintf(stderr, "Usage: %s [-q | -b] [-l#]\n", progname); X fprintf(stderr, " %s [-h]\n", progname); X exit(0); X} X Xvoid main(argc, argv) Xint argc; Xchar *argv[]; X{ X char *progname; X int foo; X X /* X * make sure the person is on a tty X */ X if(!isatty(0)) { X fputs("where are you?\n", stderr); X exit(1); X } X X /* X * this should also really check to make sure it's being played on X * the terminal of the person running it. People around here have X * the habit of redirecting random programs to other screens. X */ X if(!isatty(1)) { X fputs("This game can only be played on a terminal!\n", stderr); X exit(0); X } X X /* X * default bell sound X */ X ding = quiet; X X /* X * initialize display stuff X */ X init_term(); /* termcap stuff */ X X /* X * get name of program X */ X#ifdef AMIGA X if(argc==0) { /* called from Workbench */ X progname="letters"; X argv[1]=NULL; X } else X#endif X progname = argv[0]; X X /* X * check for options X */ X while(*++argv) { X if((*argv)[0] == '-') { X switch((*argv)[1]) { X case 'b': X ding = bell; X break; X case 'q': X ding = quiet; X break; X case 'h': X read_scores(); X show_scores(); X exit(0); X break; X case 'l': X sscanf(&argv[0][2], "%d", &level); X if(DELAY(level) < PAUSE) { X fprintf(stderr, "You may not start at level %d\n", level); X exit(0); X } X break; X default: X usage(progname); X } X } else { X usage(progname); X } X } X X /* X * get stuff initialized X */ X#ifdef AMIGA X fixedwin(argc,progname); X#endif X setterm(NEW); /* signal stuff, keyboard stuff */ X srandom(getpid()); X clrdisp(); X new_level(); X status(); X words = NULL; X X for(;;) { X /* X * allocate memory for the first word and then find a word X * if there are no others on the screen. There must always X * be at least one word active. X */ X if(words == NULL) { X lastword = words = newword((struct s_word *)NULL); X prev_word = NULL; X putword(lastword); X } X X if(game() == 0) { X /* X * all finished. print score and clean up. X */ X gotoxy(0, SCREENLENGTH); X fflush(stdout); X putchar('\n'); X break; X } X } X X read_scores(); X update_scores(); X sleep(2); X show_scores(); X printf("\n\nfinal: score = %u\twords = %u\t level = %d\n", X score, word_count, level); X X /* X * flush keyboard and quit. X */ X while(key_pressed() != -1); X setterm(ORIG); X exit(0); X} X X/* X * move all words down 1 or more lines. X * return the number of words that have fallen off the bottom of the screen X */ Xint move() X{ X struct s_word *wordp, *next; X int died; X X died = 0; X for(wordp = words; wordp != NULL; wordp = next) { X next = wordp->nextword; X erase(wordp); X wordp->posy += wordp->drop; X X if(wordp->posy >= SCREENLENGTH) { X kill_word(wordp); X died++; X } X else { X putword(wordp); X } X } X return died; X} X X/* X * erase a word on the screen by printing the correct number of blanks X */ Xvoid erase(wordp) Xstruct s_word *wordp; X{ X int i; X X gotoxy(wordp->posx, wordp->posy); X for(i = 0; i < wordp->length; i++) X putchar(' '); X} X X/* X * write the word to the screen with already typed letters highlighted X */ Xvoid putword(wordp) Xstruct s_word *wordp; X{ X int i; X X gotoxy(wordp->posx, wordp->posy); X X /* X * print the letters in the word that have so far been matched X * correctly. X */ X highlight(1); X for(i = 0; i < wordp->matches; i++) X putchar(wordp->word[i]); X highlight(0); X X /* X * print the rest of the word. X */ X for(i = wordp->matches; i < wordp->length; i++) X putchar(wordp->word[i]); X} X X/* X * Here's the main routine of the actual game. X */ Xint game() X{ X int key; X unsigned i; X int died; X struct s_word *curr_word, *temp_word; X X /* X * look to see if we already have a partial match, if not X * set the current word pointer to the first word X */ X for(curr_word = words; curr_word; curr_word = curr_word->nextword) X if (curr_word->matches > 0) X break; X if (!curr_word) X curr_word = words; X X while(curr_word->matches < curr_word->length) { X for(i = 0; i < delay; i+= PAUSE) { X while((curr_word->matches != curr_word->length) && X ((key = key_pressed()) != -1)) { X if(key == CTRL('L')) { X redraw(); X continue; X } X if(key == CTRL('N')) { X level++; X delay = DELAY(level); X if(delay < PAUSE) X delay = PAUSE; X status(); X continue; X } X /* X * This stuff deals with collecting letters X * for a word that has already been X * started. It's kind of clumsy the way X * it's being done now and should be X * cleaned up, but the obvious combination X * of erase() and putword() generate too X * much output to be used at 2400 baud. (I X * can't play too often at work) X */ X if(curr_word->matches > 0 && X key == curr_word->word[curr_word->matches]) { X gotoxy(curr_word->posx + curr_word->matches, curr_word->posy); X highlight(1); X putchar(curr_word->word[curr_word->matches]); X highlight(0); X gotoxy(SCREENWIDTH,SCREENLENGTH); X fflush(stdout); X curr_word->matches++; X /* X * fill the word with characters to X * "explode" it. X */ X if(curr_word->matches >= curr_word->length) X for (i = 0; ilength; i++) X curr_word->word[i] = '-'; X continue; X X } else if(temp_word = searchstr(key, X curr_word->word, X curr_word->matches)) { X erase(temp_word); X temp_word->matches = curr_word->matches; X curr_word->matches = 0; X putword(curr_word); X curr_word = temp_word; X curr_word->matches++; X } else if(temp_word = searchchar(key)) { X erase(temp_word); X curr_word->matches = 0; X putword(curr_word); X curr_word = temp_word; X curr_word->matches++; X } else { X ding(); X curr_word->matches = 0; X } X erase(curr_word); X putword(curr_word); X gotoxy(SCREENWIDTH,SCREENLENGTH); X X fflush(stdout); X } X usleep(PAUSE); X } X X died = move(); /* NB: move may invalidate curr_word */ X if (died > 0) X { X /* X * we only subtract lives if a word reaches the X * bottom in a normal round. If a word reaches X * bottom during bonus play, just end the bonus X * round. X */ X if(bonus == FALSE) X lives -= died; X else if(died > 0) X new_level(); X X if (lives < 0) X lives = 0; X status(); X gotoxy(SCREENWIDTH,SCREENLENGTH); X fflush(stdout); X return (lives != 0); X } else { X gotoxy(SCREENWIDTH,SCREENLENGTH); X fflush(stdout); X } X if((random() % ADDWORD) == 0) { X lastword = newword(lastword); X putword(lastword); X } X } X X /* X * all letters in the word have been correctly typed. X */ X X /* X * erase the word X */ X if(curr_word->length == curr_word->matches) { X ding(); ding(); X erase(curr_word); X } X X /* X * add on an appropriate score. X */ X score += curr_word->length + (2 * level); X letters+= curr_word->length; X word_count++; X status(); X X /* X * delete the completed word and revise pointers. X */ X kill_word(curr_word); X X /* X * increment the level if it's time. X */ X if(word_count % LEVEL_CHANGE == 0) X new_level(); X X return 1; X} X X X/* X * clear the screen and redraw it X */ Xvoid redraw() { X clrdisp(); X status(); X fflush(stdout); X} X X/* X * display the status line in inverse video X */ Xvoid status() { X static char line[SCREENWIDTH]; X int i; X X sprintf(line, "Score: %-7uLevel: %-3uWords: %-6uLives: %-3dWPM: %-4d", X score, level, word_count, lives, wpm); X X /* X * fill the line with spaces X */ X for(i = strlen(line); i < SCREENWIDTH - 2; i++) X line[i] = ' '; X X highlight(1); X gotoxy(0, SCREENLENGTH); X fputs(line, stdout); X highlight(0); X} X X/* X * do stuff to change levels. This is where special rounds can be stuck in. X */ Xvoid new_level() X{ X struct s_word *next, *wordp; X static time_t last_time = 0L; X time_t curr_time; X X /* X * update the words per minute X */ X time(&curr_time); X wpm = (letters / 5) / ((curr_time - last_time) / 60.0); X last_time = curr_time; X letters = 0; X X /* X * if we're inside a bonus round we don't need to change anything X * else so just take us out of the bonus round and exit this routine X */ X if(bonus == TRUE) { X bonus = FALSE; X banner("Bonus round finished"); X X /* X * erase all existing words so we can go back to a normal X * round X */ X for(wordp = words; wordp != NULL; wordp = next) { X next = wordp->nextword; X kill_word(wordp); X } X X status(); X return; X } X X levels_played++; X X /* X * If you start at a level other than 1, the level does not X * actually change until you've completed a number of levels equal X * to the starting level. X */ X if(level <= levels_played) X level++; X X delay = DELAY(level); X X /* X * no one should ever reach a level where there is no delay, but X * just to be safe ... X */ X if(delay < PAUSE) X delay = PAUSE; X X if((levels_played % 3 == 0) && (levels_played != 0)) { X bonus = TRUE; X X /* X * erase all existing words so we can have a bonus round X */ X for(wordp = words; wordp != NULL; wordp = next) { X next = wordp->nextword; X kill_word(wordp); X } X X banner("Prepare for bonus words"); X lives++; X } X X status(); X} X X/* X * allocate memory for a new word and get it all set up to use. X */ Xstruct s_word *newword(wordp) Xstruct s_word *wordp; X{ X struct s_word *nword; X char *word, *getword(), *bonusword(); X int length; X X if(bonus == TRUE) X word = bonusword(); X else X word = getword(); X X length = strlen(word); X X nword = (struct s_word *)malloc(sizeof(struct s_word) + length); X if(nword == (struct s_word *)0) { X perror("\nmalloc"); X setterm(ORIG); X exit(1); X } X X strncpy(nword->word, word, length); X nword->length = length; X nword->drop = length > 6 ? 1 : length > 3 ? 2 : 3; X nword->matches = 0; X nword->posx = random() % ((SCREENWIDTH - 1) - nword->length); X nword->posy = 0; X nword->nextword = NULL; X X if(wordp != NULL) X wordp->nextword = nword; X X return nword; X} X X/* X * look at the first characters in each of the words to find one which X * one matches the amount of stuff typed so far X */ Xstruct s_word *searchstr(key, str, len) Xchar *str; Xint len, key; X{ X struct s_word *wordp, *best; X X for(best = NULL, prev_word = NULL, wordp = words; X wordp != NULL; X prev_word = wordp, wordp = wordp->nextword) { X if(wordp->length > len X && strncmp(wordp->word, str, len) == 0 X && wordp->word[len] == key X && (!best || best->posy < wordp->posy)) X best = wordp; X } X X return best; X} X X X/* X * look at the first character in each of the words to see if any match the X * one that was typed. X */ Xstruct s_word *searchchar(key) Xchar key; X{ X struct s_word *wordp, *best; X X for(best = NULL, prev_word = NULL, wordp = words; wordp != NULL; X prev_word = wordp, wordp = wordp->nextword) { X if(wordp->word[0] == key X && (!best || best->posy < wordp->posy)) X best = wordp; X } X X return best; X} X Xvoid kill_word(wordp) Xstruct s_word *wordp; X{ X struct s_word *temp, *prev = NULL; X X /* X * check to see if the current word is the first one on our list X */ X if(wordp != words) X for(prev = words, temp = words->nextword; temp != wordp;) { X prev = temp; X temp = temp->nextword; X } X X if(prev != NULL) { X prev->nextword = wordp->nextword; X } else X words = wordp->nextword; X X if(wordp->nextword != NULL) X wordp->nextword = wordp->nextword->nextword; X X if(wordp == lastword) X lastword = prev; X X free((char *)wordp); X} X X X/* X * momentarily display a banner message across the screen and eliminate any X * random keystrokes form the last round X */ Xvoid banner(text) Xchar *text; X{ X /* X * display banner message X */ X clrdisp(); X gotoxy((SCREENWIDTH - strlen(text))/2, 10); X sleep(3); X puts(text); X gotoxy(SCREENWIDTH,SCREENLENGTH); X fflush(stdout); X sleep(2); X clrdisp(); X X /* X * flush keyboard X */ X while(key_pressed() != -1); X} END_OF_FILE if test 12891 -ne `wc -c <'letters.c'`; then echo shar: \"'letters.c'\" unpacked with wrong size! fi # end of 'letters.c' fi if test -f 'term.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'term.c'\" else echo shar: Extracting \"'term.c'\" \(3649 characters\) sed "s/^X//" >'term.c' <<'END_OF_FILE' X/* X * this program will attempt to draw random stuff on the screen by using the X * termcap library. Much of this code was evolved from term.c from the nn X * sources. X * X * Larry Moss (lm03_cif@uhura.cc.rochester.edu) X * Kim Storm deserves a fair amount of credit for this. It may not look X * too much like his code, but his code was at least more understandable X * than the documentation I was trying to work from and I'd like to give X * credit for making it easier for me to write. X * X * I wrote a fair amount of this based on man pages, then used Kim's code as X * a reference to make things work. Some of this actually ended up looking X * more like his code than I planned on. I hope I haven't gone overboard. X */ X X#include X#ifndef NEXT X# ifdef AMIGA X# include X# include "amitermcap.h" X# else X# include X# endif X#endif X#include X#include "term.h" X Xchar *tgoto(); Xchar PC, *BC, *UP; X Xchar *term_name; Xchar XBC[64], XUP[64]; Xchar bell_str[256] = "\007"; Xchar cursor_home[64]; Xchar clear_screen[64]; Xchar cursor_address[128]; Xchar enter_standout_mode[64], exit_standout_mode[64]; Xchar enter_underline_mode[64], exit_underline_mode[64]; X Xoutc(c) X{ X putchar(c); X} X Xbell() { X putp(bell_str); X} X Xquiet() { X} X Xint Lines, Columns; /* screen size */ Xint garbage_size; /* number of garbage chars left from so */ Xint double_garbage; /* space needed to enter&exit standout mode */ Xint STANDOUT; /* terminal got standout mode */ Xint TWRAP; /* terminal got automatic margins */ X X/* X * used to get the actual terminal control string. X */ Xopt_cap(cap, buf) Xchar *cap, *buf; X{ X char *tgetstr(); X X *buf = '\0'; X return tgetstr(cap, &buf) != NULL; X} X X/* X * call opt_cap to get control string. report if the terminal lacks that X * capability. X */ Xget_cap(cap, buf) Xchar *cap, *buf; X{ X if (!opt_cap(cap, buf)) X fprintf(stderr, "TERMCAP entry for %s has no '%s' capability\n", X term_name, cap); X} X X/* X * set everythign up. find the necessary strings to control the terminal. X */ Xinit_term() X{ X char tbuf[1024]; X X#ifndef AMIGA X /* X * get terminal type from the environment or have the user enter it X */ X if ((term_name = (char *)getenv("TERM")) == NULL) { X fprintf(stderr, "No TERM variable in environment\n"); X fprintf(stderr, "Enter terminal type to use: "); X scanf("%s", term_name = (char *)malloc(30 * sizeof(char))); X } X#endif X X /* X * get the termcap entry for the terminal above X */ X if (tgetent(tbuf, term_name) <= 0) { X fprintf(stderr, "Unknown terminal type: %s\n", term_name); X exit(1); X } X X /* X * get the padding character for the terminal X */ X opt_cap("pc", cursor_address); /* temp. usage */ X PC = cursor_address[0]; X X get_cap("cm", cursor_address); X if (!opt_cap("ho", cursor_home)) X strcpy(cursor_home, tgoto(cursor_address, 0, 0)); X X get_cap("cl", clear_screen); X X Lines = tgetnum("li"); X Columns = tgetnum("co"); X X opt_cap("so", enter_standout_mode); X opt_cap("se", exit_standout_mode); X X opt_cap("us", enter_underline_mode); X opt_cap("ue", exit_underline_mode); X X garbage_size = tgetnum("sg"); X X TWRAP = tgetflag("am"); X X STANDOUT = HAS_CAP(enter_standout_mode); X if (STANDOUT) { X if (garbage_size < 0) X garbage_size = 0; X double_garbage = 2 * garbage_size; X } else X garbage_size = double_garbage = 0; X} X Xunderline(on) X{ X if (garbage_size) X return 0; X if (!HAS_CAP(enter_underline_mode)) X return 0; X putp(on ? enter_underline_mode : exit_underline_mode); X return 1; X} X Xhighlight(on) X{ X if (garbage_size) X return 0; X if (!HAS_CAP(enter_standout_mode)) X return 0; X putp(on ? enter_standout_mode : exit_standout_mode); X return 1; X} END_OF_FILE if test 3649 -ne `wc -c <'term.c'`; then echo shar: \"'term.c'\" unpacked with wrong size! fi # end of 'term.c' fi if test -f 'term.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'term.h'\" else echo shar: Extracting \"'term.h'\" \(397 characters\) sed "s/^X//" >'term.h' <<'END_OF_FILE' X/* X * header file to be used with term.c. defines some macros to place the X * cursor in various places on the screen. X * X * Larry Moss (lm03_cif@uhura.cc.rochester.edu) X */ X X#define putp(str) tputs(str, 0, outc) X#define HAS_CAP(str) (*str) X#define clrdisp() tputs(clear_screen, Lines, outc) X#define home() putp(cursor_home) X#define gotoxy(c, l) putp(tgoto(cursor_address, c, l)) X X#define SP ' ' END_OF_FILE if test 397 -ne `wc -c <'term.h'`; then echo shar: \"'term.h'\" unpacked with wrong size! fi # end of 'term.h' fi if test -f 'terms.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'terms.h'\" else echo shar: Extracting \"'terms.h'\" \(723 characters\) sed "s/^X//" >'terms.h' <<'END_OF_FILE' X/* X * header file for term.c X */ X Xextern char *tgoto(); Xextern char PC, *BC, *UP; X Xextern char *term_name; Xextern char XBC[], XUP[]; Xextern char bell_str[]; Xextern char *cursor_home; Xextern char clear_screen[]; Xextern char cursor_address[]; Xextern char enter_standout_mode[], exit_standout_mode[]; Xextern char enter_underline_mode[], exit_underline_mode[]; X Xextern outc(); Xextern bell(); Xextern quiet(); X Xextern int Lines, Columns; /* screen size */ Xextern int garbage_size; /* number of garbage chars left from so */ Xextern int double_garbage; /* space needed to enter&exit standout mode */ Xextern int STANDOUT; /* terminal got standout mode */ Xextern int TWRAP; /* terminal got automatic margins */ END_OF_FILE if test 723 -ne `wc -c <'terms.h'`; then echo shar: \"'terms.h'\" unpacked with wrong size! fi # end of 'terms.h' fi if test -f 'usleep.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'usleep.c'\" else echo shar: Extracting \"'usleep.c'\" \(615 characters\) sed "s/^X//" >'usleep.c' <<'END_OF_FILE' X/* X * I grabbed this from the FAQ liston comp.unix.questions X */ X/* X * usleep -- support routine for 4.2BSD system call emulations X * X * last edit: 29-Oct-1984 D A Gwyn X */ X Xextern int select(); X X Xint usleep(usec) /* returns 0 if ok, else -1 */ Xlong usec; /* delay in microseconds */ X{ X static struct { /* `timeval' */ X long tv_sec; /* seconds */ X long tv_usec; /* microsecs */ X } delay; /* _select() timeout */ X X delay.tv_sec = usec / 1000000L; X delay.tv_usec = usec % 1000000L; X X return select(0, (long *) 0, (long *) 0, (long *) 0, &delay); X} END_OF_FILE if test 615 -ne `wc -c <'usleep.c'`; then echo shar: \"'usleep.c'\" unpacked with wrong size! fi # end of 'usleep.c' fi if test -f 'usleep5.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'usleep5.c'\" else echo shar: Extracting \"'usleep5.c'\" \(1575 characters\) sed "s/^X//" >'usleep5.c' <<'END_OF_FILE' X /* X * subseconds sleeps for System V - or anything that has poll() Don X * Libes, 4/1/1991 X * X * The BSD analog to this function is defined in terms of microseconds X * while poll() is defined in terms of milliseconds. For X * compatibility, this function provides accuracy "over the long run" X * by truncating actual requests to milliseconds and accumulating X * microseconds across calls with the idea that you are probably X * calling it in a tight loop, and that over the long run, the error X * will even out. X * X * If you aren't calling it in a tight loop, then you almost certainly X * aren't making microsecond-resolution requests anyway, in which X * case you don't care about microseconds. And if you did, you X * wouldn't be using UNIX anyway because random system indigestion X * (i.e., scheduling) can make mincemeat out of any timing code. X * X * Returns 0 if successful timeout, -1 if unsuccessful. X * X */ X X#include X X int X usleep(usec) X unsigned int usec; /* microseconds */ X { X static subtotal = 0; /* microseconds */ X int msec; /* milliseconds */ X X /* X * 'foo' is only here because some versions of 5.3 have a bug X * where the first argument to poll() is checked for a valid X * memory address even if the second argument is 0. X */ X struct pollfd foo; X X subtotal += usec; X /* if less then 1 msec request, do nothing but remember it */ X if (subtotal < 1000) X return (0); X msec = subtotal / 1000; X subtotal = subtotal % 1000; X return poll(&foo, (unsigned long) 0, msec); X } END_OF_FILE if test 1575 -ne `wc -c <'usleep5.c'`; then echo shar: \"'usleep5.c'\" unpacked with wrong size! fi # end of 'usleep5.c' fi if test -f 'word.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'word.c'\" else echo shar: Extracting \"'word.c'\" \(1479 characters\) sed "s/^X//" >'word.c' <<'END_OF_FILE' X/* X * find a random word in a dictionary file as part of letters. X * X * copyright 1991 Larry Moss (lm03_cif@uhura.cc.rochester.edu) X */ X X#include X#include X#include X#include "config.h" X#include "kinput.h" X X#ifdef SYSV X# define random lrand48 X#endif X Xchar *getword() X{ X static char buf[512]; X static FILE *fp = NULL; X static struct stat s_buf; X X /* X * This is stuff that only needs to get done once. X */ X if(fp == NULL) { X char *dictionary = DICTIONARY; X X /* X * open the dictionary file X */ X if((fp = fopen(dictionary, "r")) == NULL) { X fprintf(stderr, "can't open file: %s.\n", dictionary); X setterm(ORIG); X exit(1); X } X X /* X * Get length of dictionary in bytes so we can pick a X * random entry in it. X */ X if(stat(dictionary, &s_buf) == -1) { X perror("stat"); X setterm(ORIG); X exit(1); X } X } X X /* X * pick a random place in the dictionary X */ X fseek(fp, random() % s_buf.st_size, 0); X X /* X * read until the end of a line, then read the next word. X */ X fscanf(fp, "%*s%s", buf); X X /* X * Since we're reading two words at a time it's possible to go past X * the end of the file. If that happens, use the first word in the X * dictionary. X */ X if(buf == NULL) { X fseek(fp, 0L, 0); X fscanf(fp, "%s", buf); X } X X return buf; X} X X Xchar *bonusword() X{ X static char buf[BONUSLENGTH + 1]; X int i; X X for(i = 0; i < BONUSLENGTH; i++) X buf[i] = (char)(random() % 94) + 33; X X buf[BONUSLENGTH] = 0; X X return buf; X} END_OF_FILE if test 1479 -ne `wc -c <'word.c'`; then echo shar: \"'word.c'\" unpacked with wrong size! fi # end of 'word.c' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(2243 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# X# Makefile for letters, a game to help improve typing skills X# X# copyright 1991 by Larry Moss (lm03_cif@uhura.cc.rochester.edu) X# X XCC = cc X#CC = gcc X#SYSTYPE = SYSV X#SYSTYPE = NEXT XSYSTYPE = BSD X# if you don't have job control add -DNOJOB to CFLAGS XCFLAGS = -O -D$(SYSTYPE) -DHIGHSCORES=\"$(LIBDIR)/letters.high\" \ X -DDICTIONARY=\"$(DICTIONARY)\" XLDFLAGS = -ltermcap XBINDIR = $(HOME)/bin-sun4 XMANDIR = $(HOME)/man/man$(MANEXT) XMANEXT = 6 XLIBDIR = $(HOME)/lib XDICTIONARY = /usr/dict/words X#DICTIONARY = dictfile X X# next line only needed if you need to create a dictionary file. The files X# in this directory will be used to make a wordlist. XDOCDIR = $(HOME)/man X X# If your machine doesn't have usleep uncomment it in the following line. X# I know this includes ultrix 4.2 and hp-ux 7.? and many sys V based machines. X# Don't know about others. if you need usleep and your machine does not have X# select, change usleep.o to usleep5.o (mostly sysV machines). XOBJS = letters.o kinput.o term.o word.o highscore.o # usleep.o X X# The following line will stop gcc from complaining about the arguments X# sun's make uses. It shouldn't bother anyone else. X.c.o: X $(CC) $(CFLAGS) -c $< X Xletters: $(OBJS) X $(CC) $(CFLAGS) $(OBJS) -o letters $(LDFLAGS) X Xletters.o word.o kinput.o: \ X kinput.h Xletters.o term.o highscore.o kinput.o: \ X term.h Xletters.o highscore.o kinput.o: \ X terms.h Xword.o highscore.o letters.o: \ X config.h X Xinstall: letters X install -s -m 2755 letters $(BINDIR) X sed -e 's;LIBDIR;$(LIBDIR);' -e 's;DICTIONARY;$(DICTIONARY);'\ X letters.man > letters.$(MANEXT) X install -c -m 0644 letters.$(MANEXT) $(MANDIR)/letters.$(MANEXT) X if [ ! -f $(LIBDIR)/letters.high ] ;then \ X install -c -m 0664 letters.high $(LIBDIR) ;fi X Xinstall_hpux: letters X install -c $(BINDIR) letters X install -c $(MANDIR) letters.man X install -c $(LIBDIR) letters.high X chmod 0666 $(LIBDIR)/letters.high X Xclean: X rm -f rm *.o letters letters.$(MANEXT) X Xshar: X shar -o letters.shar *.[ch] Makefile letters.man letters.high README X Xtar: X tar cf letters.tar *.[ch] Makefile letters.man letters.high README X Xdict: X awk '{ for (i = 0; i <= NF; ++i)\ X if($$i ~ /^[a-zA-Z][a-z]*$$/) { print $$i } }'\ X `find $(DOCDIR) -type f -print` |\ X sort -u > $(DICTIONARY) END_OF_FILE if test 2243 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'letters.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'letters.man'\" else echo shar: Extracting \"'letters.man'\" \(4572 characters\) sed "s/^X//" >'letters.man' <<'END_OF_FILE' X.TH LETTERS 6 "23 SEPT 1991" X.SH NAME Xletters \- a game to improve typing skills X.SH SYNOPSIS X\fBletters\fP [-q | -b] [-l#] X.br X\fBletters\fP [-h] X.SH DESCRIPTION X\fBLetters\fP is based on \fBLetter Invaders\fP which was around in the PC Xenvironment several years ago. It in turn was based on the popular Xarcade style game, \fBSpace Invaders\fP. For those not familiar with X\fBSpace Invaders\fP \ (please let me know if you're one of these people Xand let me know what planet you've been living on :-) the idea is to blast Xaliens out of the sky as they attempt to land on and "kill" you. Since Xthis is a game to improve typing skills, the aliens are words selected Xrandomly from the dictionary. You blast the aliens out of the sky by Xtyping them correctly. X.PP XPlaying the game is very straight forward. Type words as they appear on Xthe screen. They will slowly drop until you have either typed them Xcorrectly or they reach the status line on the bottom of the screen. If Xa word makes it to the bottom of the screen you lose one "life". This is Xactually a non-violent game. The words "kill" and "life" are only used Xin this context for historical reasons. X.PP XIf at any point you type a letter incorrectly the entire word is reset. XThe backspace key will not save you if you're a sloppy typist. XIt is not necessary to type words in the order they appear on the Xscreen. In fact, that is often not the Xbest action to take. Very short words fall faster than longer ones. XThe program will attempt to determine the word you are Xtrying to type by matching what you've typed with the first letter(s) of Xall other words on the screen. Unlike in previous versions the program Xtries to determine what word you are typing by more than the first Xletter. The highlight bar doesn't move to a new word until it is clear Xwhich word is being typed. X.PP XIf you successfully complete 3 rounds of play you get a bonus life and Xyou get the opportunity to play a bonus round. The bonus round is played Xthe same as all other rounds but the words are strings of random Xprintable characters. The round is played at the same speed as whatever Xlevel you were on and likewise points are computed the same as in that Xround. since this is just a bonus round, you can only gain points. You Xcannot die. The round lasts as long as all other rounds (15 words) Xunless a word reaches the bottom of the screen. X.SH "Special Keys" X.IP Xctrl-L Redraw screen. X.IP Xctrl-C Exit from the game. You will be prompted before exiting to make Xsure that's really what you wanted to do. Since it prompts you this can Xalso be used as a method of pausing the game. This is actually whatever Xyour interrupt character is. Ctrl-C is just most common. X.IP Xctrl-N Skip to next level. The game does correctly keep track of how Xmany levels you have completed. (See '-l' under \fBOPTIONS\fP) X.IP Xctrl-Z Works as it should. If your system has job control it will stop Xthe process. X.SH OPTIONS X.IP X-q quiet mode (default) - turn off the obnoxious beep X.IP X-h Show high scores. X.IP X-b Beep when words are completed or mistyped. X.IP X-l# # is the level number that you want to start at. The level will Xnot increase until you have completed the number of rounds equal to your Xstarting level. In other words, if you start on level 5, you need to Xplay through 5 rounds before the level increases to 6 (and the speed Xincreases and scoring changes). X.SH SCORING XA word's point value = (# of letters) + 2 * (current level). No points Xare added for partially typed words. X.SH "STATUS LINE" XIt's fairly obvious what most of the things on the status line are. The Xlast thing on the line however is words per minute. This is computed at Xthe end of each round based on 5 letter words. It's not a particularly Xaccurate measure of your typing speed, but I think it's an interesting Xenough statistic to justify filling up the empty space on the status line. X.SH FILES XDICTIONARY X.br XLIBDIR/letters.high X.SH BUGS X"Transmogrifier" doesn't appear frequently. X.SH AUTHORS XLarry Moss (lm03_cif@uhura.cc.rochester.edu) - original game, UNIX version X.br XBrent Nordquist (brent@limabean.veggard.mn.org) - amiga version, also Xfixed some of bugs. X.SH THANKS XChris Uppal (chrisu@sco.com) for contributing enormously to the Xprogram with a couple of bug fixes, variable speed words, some Xperformance improvements, SYSV compatibility, and many suggestions. X.sp XMark Levinson (mrle_cif@uhura.cc.rochester.edu) for pointing out the bug Xlisted above. X.sp XThanks also to everyone else that's sent me mail with comments and/or Xsuggestions. END_OF_FILE if test 4572 -ne `wc -c <'letters.man'`; then echo shar: \"'letters.man'\" unpacked with wrong size! fi # end of 'letters.man' fi if test -f 'letters.high' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'letters.high'\" else echo shar: Extracting \"'letters.high'\" \(175 characters\) sed "s/^X//" >'letters.high' <<'END_OF_FILE' Xhfir_cif 9 121 871hfir_cif 8 115 850hfir_cif 8 106 818hfir_cif 8 113 803lm03_cif 6 82 633lm03_cif 6 85 611jcm8_cif 6 78 557jcm8_cif 5 74 522jcm8_cif 4 47 366jcm8_cif 4 47 352 END_OF_FILE if test 175 -ne `wc -c <'letters.high'`; then echo shar: \"'letters.high'\" unpacked with wrong size! fi # end of 'letters.high' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(3963 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XLetter Invaders v2.1 XA game to help improve typing skills. X===================================== X XCopying: X XPlease put this on as many different machines as you like. If you make Xchanges or bug fixes, please mail them to me so they can be incorporated Xinto later versions. Feel free to use the entire program or portions of Xit in any way you like as long as I get credit for what I've done. X X XInstallation: X XInstallation of letters should be fairly simple. A few lines in the XMakefile should be changed to reflect the directories where the files Xshould be installed. Config.h may (but shouldn't) require a couple of Xchanges. After making the minor changes, "make install" or "make Xinstall_hpux" should do all the work. The hpux installation could be Ximproved, but it is enough to get it up and running. If your system does Xnot have /usr/dict/words or any other large collection of words you can Xuse "make dict" to create a wordlist. Well, it works for me, but I use Xawk to do it so I know someone is bound to have a problem. X XSo far it has been compiled and run under SunOS 4.0 and 4.1.1, SCO Xenix, XULTRIX 4.2, hpux 7.05, and NeXT (mach, I guess) and a large number of Xothers. When compiling it on the NeXT it produces a number of warnings Xbut there are no serious problems (I'd fix it, but my access to a NeXT is Xfairly limited so I just made sure it compiles). Please let me know Xabout any other platforms that it gets compiled on or fails to get Xcompiled on. X XThe game has been ported to the amiga. A large portion of the code Xnecessary to make it compile on the amiga is not included with the UNIX Xdistribution. All UNIX specific and Amiga specific stuff has been kept Xseparate so that it will be easy to keep versions the same if/when the Xgame is improved. X XI'd like to mention that there is a minix version of the game available. XThe changes to make it compile under minix are not included in this Xpackage (although there have been enough changes to accomodate other Xsystems that it might not be too much work any more to get it to compile Xon minix). Credit for the minix version (and comments and questions) to XWim van Dorst (baron@wiesje.hobby.nl). I've been told the minix version Xcan be foundon your favorite minix archive site. X Xauthors X------- XLarry Moss (lm03_cif@uhura.cc.rochester.edu) - original game, UNIX version XBrent J. Nordquist (brent@limabean.veggard.mn.org) - amiga version X X XThanks to: X---------- X XD. A. Gwyn. I have used his code for usleep() to get it to compile on Xmachines that don't have it. Since the code is included in the FAQ Xposted to comp.unix.questions I assume this is ok. If someone knows Xotherwise please let me know. X XKim Storm. I used his nn code as a reference when trying to figure out Xhow to use the termcap library. X XChris Uppal (chrisu@sco.com). He has contributed enormously to the Xprogram with a couple of bug fixes, variable speed words, some Xperformance improvements, SYSV compatibility, and many suggestions. X XWim van Dorst (baron@wiesje.hobby.nl). Creator of the minix version of Xthe game. X X X XFuture plans: X XI had at one time planned to have bonus words fly horizontally across Xscreen, but I never bothered with implementing it. Instead, I just got a Xsuggestion to have the bonus words come down like the others, but Xunderlined. When a bonus word is typed correctly it will take out the Xwhole screen with it. This could add a bit of strategy to the game. X Xinteresting things should happen at higher levels. Now it just keeps Xgetting faster. I've gotten the suggestion of possibly having some words Xon the screen change randomly to force you to watch the screen more Xcarefully. X XI've considered using this as an excuse to learn X programming. I figure Xthat way words can really explode. This will only happen if I find Xmyself with a lot of free time. X XFrequently mistyped words should come back later in the game. This would Xcertainly make the game more useful as a typing tutor. END_OF_FILE if test 3963 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi echo shar: End of shell archive. exit 0