home *** CD-ROM | disk | FTP | other *** search
- /*
- * TYPEFAST
- * Improve your typing the fun way
- * by Steve Spearman druco!spear 11/18/86
- */
-
- /* Fixes/Enhancements:
- * by Dave Tutelman att!mtunb!dmt 2/5/89
- * - Ported to MSDOS PCs (Turbo C & PCcurses).
- * Compile with #define MSDOS.
- * - Compile with #define WORDLIST, to use your own favorite words
- * (in WORDLIST.H), instead of those built into WORDS.C.
- * - Slowed down missiles so they stay visible @ >9600 baud.
- * - Fudged highrow to allow more time for longer words.
- */
-
- #include <stdio.h>
- #include <curses.h>
-
- /* non-int external functions used */
- extern long time();
-
- /*Declarations follow for non-int functions */
- extern void screeninit(),getsetup(),checkhit(),showscore(),cleartarget();
-
- #define MAXLEN 20 /*MAX word length */
- #define MAXSIZE 30 /*MAX word length + some fudge */
- #define MAXTARGET 10 /*MAX target words*/
- #define MAXMISS 10 /*Number of missed words to end game */
- #define MAXROW 23 /*Maximum row allowed */
-
- #define boolean int
-
- extern char *words[]; /* word list from other file */
- extern int initwords(); /* word list initializer from other file */
-
- struct word { /* the structure of each word on the screen*/
- char text[MAXLEN]; /* word text */
- int row; /* screen row position */
- int col; /* screen column position */
- int len; /* length of word */
- boolean active; /* indicates if word is active or not */
- };
- struct word target[MAXTARGET];
-
- /* the following are set in the 'getsetup' function */
- int addtarget, /* Add a new target every x hits*/
- movedown, /* move top row down every x hits */
- movebottom, /* move botom row down every x hits */
- startwords; /* Starting number of target words*/
- int lowrow; /* lowest row number for a word */
- int highrow; /* highest row number for a word */
-
- /* internal variables */
- int maxword; /* number of words in list */
- int targets=0; /* total active targets */
- int missed=0; /* total misses */
- int total=0; /* total hits */
- long strokes=0; /* total key stroke hits on words gotten*/
- long allstrokes=0; /* total key stroke hits including bad*/
- long starttime; /* time we started typing */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- extern char *optarg;
- int c;
- if (argc > 1) {
- printf("Usage: %s \n", argv[0]);
- exit(1);
- }
-
- initialize(); /* initialize curses */
- getsetup(); /* set difficulty variables */
- screeninit(); /* put up initial display */
- maxword = initwords(); /* get the word list */
-
- for (targets=0;targets < startwords; targets++) {
- newtarget(targets); /* set up a target word */
- }
- starttime = time ((long *) 0);
-
- while (missed < MAXMISS) {
-
- targetmove();
- refresh();
- napms(600); /* suspend for so many milliseconds */
- while ((c=getch()) >= 0) {
- if (c == '\033') { /* ESCAPE */
- doend(); /* end the game with states*/
- }
- checkhit(c);
- }
-
- }
- move(21,0);
- clrtoeol();
- mvprintw(21,0,"END of Game: you missed the maximum %d words",missed);
- doend();
- }
-
- /* DOEND
- *
- * Print out the ending statistics and exit gracefully
- */
- int
- doend()
- {
- mvprintw(22,0,"WPM on words typed correctly: %d",wpm(strokes));
- printw(" WPM on all words typed: %d",wpm(allstrokes));
- goodbye();
- }
-
- /* TARGETMOVE
- *
- * Moves all targets down a row and checks for any that have reached
- * the bottom. If they have, it is counted and a new word is created.
- */
- int
- targetmove()
- {
- register int num;
- for (num = 0; num < MAXTARGET; num++) {
- if ( ! target[num].active)
- continue;
- if (movetarget(num)) {
- newtarget(num);
- missed++;
- flash();
- showscore();
- }
- }
- }
-
- /* CLEARWORD
- *
- * This function clears out the screen position of the word
- * whose structure index is passed.
- */
- void
- clearword(num)
- int num;
- {
- register int i;
- move(target[num].row,target[num].col);
- for (i=0; i < target[num].len; i++) {
- addch(' ');
- }
- }
-
- /* MOVETARGET
- *
- * This move the word structure whose index is passed down one row.
- * If this move brings it to the edge of the screen, 1 is returned,
- * otherwise, 0 is returned.
- */
- int
- movetarget(num)
- int num;
- {
- clearword(num); /* clear the old word on screen */
- target[num].row++; /* move down a row */
- if(target[num].row >= MAXROW)
- return(1);
- mvprintw(target[num].row,target[num].col,target[num].text);
- return(0);
- }
-
-
- /* NEWTARGET
- *
- * This function produces a new word structure in the structure whose
- * index is passed. The word is chosen randomly and is placed randomly
- * subject to constraints of screen placement and word proximity.
- */
- int
- newtarget(num)
- int num;
- {
- register int i;
- int wordno;
- boolean bad;
- int safety;
- int samecol;
- int _highrow; /* "Fudged" value of highrow for this word. We
- * "give the typist a break" by one row for each
- * "nominal" word of 5 characters in the word. */
-
- wordno = (rand() % maxword); /* pick a word number */
- strcpy(target[num].text,words[wordno]); /* copy the word into struct */
- target[num].len=strlen(target[num].text); /* calculate length */
- _highrow = highrow - target[num].len % 5; /* fudge _highrow */
- bad = 1; /* flag that is cleared when we get a good word */
- safety = 0;
- while (bad) {
- if (++safety > 5000)
- error("New word not placable");
- /* try to get a good row and column */
- target[num].row= (rand() % (_highrow - lowrow + 1)) + lowrow;
- target[num].col= (rand() % (80-target[num].len));
- bad = 0;
- /* check all other words to make sure no overlaps */
- for (i=0; i< MAXTARGET; i++) {
- if ( (! target[i].active) || (i == num) )
- continue; /* don't check against self */
- samecol = 0;
- if ( (target[num].col <= target[i].col) &&
- ((target[num].col + target[num].len + 1) >= target[i].col))
- samecol = 1; /* columns touch/overlap */
- if ( (target[i].col <= target[num].col) &&
- ((target[i].col + target[i].len + 1) >= target[num].col))
- samecol = 1; /* columns touch/overlap */
- /* if columns overlap, check that the rows are */
- /* separated by at least one blank to avoid problems */
- /* with erasing or overwriting the other word during */
- /* movement */
- if ( samecol &&
- (abs(target[num].row - target[i].row) < 2)) {
- bad = 1;
- break;
- }
- }
- }
- target[num].active = 1;
- }
-
- /* CHECKHIT
- *
- * This routine is passed a char each time one is typed. It collects
- * words and compares the word typed with all words on the screen. If
- * a match is found, the match lowest on the screen is considered hit
- * and the routine gotit() is called to handle the hit.
- */
- void
- checkhit(c)
- char c;
- {
- static int count = 0; /* character counter */
- static char line[MAXSIZE]; /* the line you are typing */
- int couldhit[MAXTARGET]; /* some booleans */
- register int num;
- register int row; /* holds row of number we shot */
- int shot; /* holds the number of word we shot */
- /* check for a word separator to end the word */
- if ( (c != ' ') && (c != '\n') && ( c != '\r')) {
- allstrokes++; /* count all key strokes */
- /* character is not a separator, so just collect it */
- if (count >= (MAXSIZE - 2)) /* if line too long, */
- return; /* wait for next separator to begin again */
- line[count++] = c;
- return;
- }
- /* if we got here, we got a separator and have a word */
- if ( count == 0 ) /* null word */
- return;
- line[count] = '\0';
- count = 0; /*reset for next word */
- shot = -1;
- row = 0; /* keep track of largest row of word shot */
- for (num=0;num < MAXTARGET; num++) {
- if ( ! target[num].active ) /* only check active words */
- continue;
- /* check to see if the word matches */
- if (strncmp(target[num].text,line,MAXSIZE) == 0) {
- /* got a word! */
- /* check if this is lower on screen than any others*/
- if (target[num].row > row) {
- row = target[num].row;
- shot = num;
- }
- }
- }
- if ( shot == -1 )
- return;
- gotit(shot); /* handle the word we shot */
- }
-
- /* GOTIT
- *
- * We have gotten the word whose number is passed. Now produce any
- * appropriate effects and clear out the word. Also, update score
- * and create a new replacement word.
- */
- int
- gotit(num)
- int num;
- {
- register int i,x,y;
- char oldc = '\0';
- char missile =
- #ifdef MSDOS
- 0xEA; /* OMEGA looks like missile on PC screen */
- #else
- '^';
- #endif
-
- int incmove;
- total++; /* keep track of words gotten */
- x = target[num].col + (target[num].len / 2); /* center of word */
- y = target[num].row; /* row of word */
- strokes += target[num].len; /* count letters */
- incmove = 1;
- if (baudrate() < 9600)
- incmove = 2;
- if (baudrate() <= 1200)
- incmove = 4;
- if (baudrate() <= 300)
- incmove = 8;
- for (i = MAXROW ; i >= y; i -= incmove) { /* from the screen bottom to word */
- if (i < MAXROW) /* restore previous position */
- mvaddch(i + incmove,x,oldc);
- oldc = mvinch(i,x); /* store old char for restore */
- mvaddch(i,x,missile); /* put out 'missile' */
- refresh();
- if (baudrate() > 9600)
- napms (100); /* if REAL fast, slow it down */
- }
- if (oldc)
- mvaddch(i + incmove,x,oldc); /* clear last 'missile' */
- if (((total % addtarget) == 0 ) && ((targets + 1) < MAXTARGET))
- newtarget(targets++);
- if (((total % movedown) == 0 ) && (highrow > (lowrow + 1)) )
- lowrow++;
- if (((total % movebottom) == 0 ) && (highrow < (MAXROW - 2)) )
- highrow++;
- showscore();
- cleartarget(num);
- newtarget(num);
- }
-
- /* CLEARTARGET
- *
- * Make an explosion where the word was if baudrate allows, and clear
- * the screen at the word location
- */
- void
- cleartarget(num)
- int num;
- {
- register int i;
- int j;
- char c;
- #ifdef MSDOS
- char explode[]={ '*', '#', 0xDB, 0xB2, 0xB1, 0xB0, ' '};
- int jmax = 7; /* number of symbols in explosion */
- int naptime = 70; /* 500 msec / 7 symbols ~= 70 msec */
- #else
- char explode[]={ '*', '#', ' '};
- int jmax = 3; /* number of symbols in explosion */
- int naptime = 160; /* 500 msec / 3 symbols ~= 160 msec */
- #endif
-
- for (j = 0; j < jmax; j++) {
- if ((j < jmax) && (baudrate() < 4800))
- continue;
- c = explode [j];
- move(target[num].row,target[num].col);
- for (i=0;i< target[num].len; i++) {
- addch(c);
- }
- refresh();
- if (baudrate() > 9600)
- napms (naptime);/* if REAL fast, slow it down */
- }
- }
-
- /* SHOWSCORE
- *
- * Show the total words gotten and words missed
- */
- void
- showscore()
- {
- mvprintw(2,30,"Hits: %d",total);
- mvprintw(2,42,"Misses: %d",missed);
- }
-
- /* SCREENINIT
- *
- * Initialize the screen for the game
- */
- void
- screeninit()
- {
- clear();
- srand(time(0)); /* set up random numbers */
- standout();
- mvprintw(1,35,"**TYPEFAST**");
- mvprintw(MAXROW,1,"<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>");
- standend();
- showscore();
- refresh();
- }
-
- /* WPM
- *
- * Calculate total wpm by taking the number of key strokes passed,
- * calculating elapsed time, and figuring 5 chars per word.
- */
- int
- wpm(tstrokes)
- long tstrokes;
- {
- long totaltime;
- float result;
- int iresult;
- totaltime = time((long *) 0);
- totaltime -= starttime; /* figure total time in seconds */
- if (totaltime < 1)
- return(0);
- result = (tstrokes * 60.0)/ (5.0 * totaltime);
- iresult = (int) result;
- return(iresult);
- }
-
- /* GETSETUP
- *
- * This routine sets up the initial screen and gives instructions,
- * and prompts for game level. It then sets several game variables
- * for number of targets, locations, and how often variables are
- * changed to increase difficulty.
- */
- void
- getsetup()
- {
- char c;
- clear();
- standout();
- mvprintw(0,0,"<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>");
- mvprintw(MAXROW-1,0,"<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>");
- mvprintw(5,30,"The TYPEFAST Game");
- standend();
- mvprintw(12,10,"Do you need instructions? Press y or n (no RETURN)");
- refresh();
- while (((c = getch()) != 'y') && (c != 'n')) ;
- move(12,10);
- clrtoeol();
- if (c == 'y') {
- move(8,0);
- printw("Words will move down the screen toward the bottom. You must type\n");
- printw("each word before it reaches the bottom. End each word you type with\n");
- printw("a SPACE or a RETURN. When you successfully type a word in time a\n");
- printw("new one will take its place somewhere on the screen. The number\n");
- printw("of words will increase as you go along and the words will get closer\n");
- printw("to the bottom of the screen. At the end your Words Per Minute is shown.\n");
- }
- standout();
- mvprintw(16,10,"Press the letter of the level of difficult you wish (no RETURN)");
- standend();
- mvprintw(18,33,"1-Easy");
- mvprintw(19,33,"2-Normal");
- mvprintw(20,33,"3-Hard");
- mvprintw(21,33,"q-Quit");
- refresh();
- while (1) {
- c = getch();
- if ((c >= '1') && (c <= '3'))
- break;
- if (c == 'q')
- error("Game aborted as requested");
- }
- switch (c) {
- case '1':
- addtarget= 60; /*Add a new target every x hits*/
- movedown= 50; /*move top row down every x hits */
- movebottom=130; /*move botom row down every x hits */
- startwords= 2; /*Starting target words*/
- lowrow = 4; /* lowest row number for a word */
- highrow = 15; /* highest row number for a word */
- break;
- case '2':
- addtarget= 50; /*Add a new target every x hits*/
- movedown= 50; /*move top row down every x hits */
- movebottom=100; /*move botom row down every x hits */
- startwords= 3; /*Starting target words*/
- lowrow = 6; /* lowest row number for a word */
- highrow = 17; /* highest row number for a word */
- break;
- case '3':
- addtarget= 50; /*Add a new target every x hits*/
- movedown= 30; /*move top row down every x hits */
- movebottom= 60; /*move botom row down every x hits */
- startwords= 5; /*Starting target words*/
- lowrow = 10; /* lowest row number for a word */
- highrow = 18; /* highest row number for a word */
- break;
- default:
- error("Internal game switch error");
- }
- clear();
- }