spear@druco.ATT.COM (Steve Spearman) (02/01/89)
Here's a little screen-oriented typing game I wrote a while back. It's curses-based and known to run on System V. No man page but has help. ---cut here--- #To extract the files contained herein, just execute #this file via the command: # sh thisfile #where 'thisfile' is the name of this file echo extracting game.c cat >game.c <<'!FuNkYsTuFf' /* * TYPEFAST * Improve your typing the fun way * by druco!spear 11/18/86 */ /* Fixes/Enhancements: */ #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; extern int opterr; int c; opterr = 0; /* suppress the 'getopt' error messages */ while ((c = getopt(argc, argv, "?")) != EOF) switch (c) { case '?' : default : 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 on all words typed: %d",wpm(strokes),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; 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 */ 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'; 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,'^'); /* put out 'missile' */ refresh(); } 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; c = '*'; /* char to produce an 'explosion' */ for (j = 0; j <= 2; j++) { if ((j < 2) && (baudrate() < 4800)) continue; if (j == 1) c = '#'; if (j == 2) c = ' '; /* last time we clear the spot */ move(target[num].row,target[num].col); for (i=0;i< target[num].len; i++) { addch(c); } refresh(); } } /* 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(); } !FuNkYsTuFf echo extracting makefile cat >makefile <<'!FuNkYsTuFf' # Makefile for typefast OBJ=game.o utils.o words.o all: $(OBJ) cc -o typefast $(OBJ) -lcurses game.o: game.c cc -c -O game.c !FuNkYsTuFf echo extracting utils.c cat >utils.c <<'!FuNkYsTuFf' #include <stdio.h> #include <curses.h> #include <sys/signal.h> /* game - utilities * * by Steve Spearman */ /******************************************************** * * error () * * Error() halts with an error message which is passed * as an argument ********************************************************/ int error(reason) char reason[]; { move(22,0); clrtoeol(); printw("Fatal Game Error - %s\n",reason); refresh(); nodelay(stdscr,FALSE); endwin(); exit(1); } int goodbye() { move(23,0); refresh(); nodelay(stdscr,FALSE); endwin(); exit(0); } int sighandle() { error("Unexpected signal received"); } int initialize() { initscr(); /* initialize screen*/ cbreak(); /* get characters in immediate mode*/ nodelay(stdscr,TRUE); noecho(); /* don't echo input */ if (clear()==ERR) error("Your terminal to too dumb"); /* clear the screen*/ if (clrtoeol()==ERR) error("Your terminal to too dumb"); /* check for function*/ if (move(23,79)==ERR) error("Your terminal to too small"); /* check for size*/ signal(SIGINT,sighandle); signal(SIGQUIT,sighandle); signal(SIGILL,sighandle); signal(SIGBUS,sighandle); signal(SIGTRAP,sighandle); signal(SIGIOT,sighandle); signal(SIGEMT,sighandle); signal(SIGSEGV,sighandle); signal(SIGSYS,sighandle); signal(SIGTERM,sighandle); } int mygetchar(c) int *c; { if (read(0,c,1) <= 0) return(0); *c &= 0177; return(1); } !FuNkYsTuFf echo extracting words.c cat >words.c <<'!FuNkYsTuFf' /* FASTTYPE file words.c * * This file contains the word read-in and initialization functions */ /* WORDS * The default list of words to type. * Must terminate with a "\0" */ char *words[]={"now","how","are","there","sew","tough", "and", "and", "and", "all", "are", "the", "the", "the", "we","us", "will", "did", "why", "when", "thus", "hard", "easy", "small", "move", "say", "tell", "try", "keep", "do", "concrete","abstract","real","ideal", "accept","reject", "grow","new","renew","fresh", "rapid","change","careful","broad", "massive","huge", "doubt","dubious","certain", "past","present","future", "obvious","consider","finish","complete", "actual","add","adequate","afford", "worse","bad","basic","fun","brief", "uniform", "vital","viable", "worth", "clear","lucid","simple", "stud","fox","quick","brown","dog","lazy","lament", "command","entire","earth","purple","his","coward", "wonder","where","why","and","blue","book","letter", "girl","boy","stood","read","ponder","summary","escape", "terminal","game","type","show","status","state","line", "print","bell","lock","keyboard","shift","reset","normal", "video","reverse","follow","number","speaker","always", "able","ability", "about", "above", "absolute", "abstract", "accept", "accomplish", "accord","as", "accurate", "across", "active", "at", "activity", "actual", "adapt", "add", "adequate", "adjust", "advent", "affirm", "afford", "after", "agenda", "aggregate", "agree", "allocate", "alternate", "although", "ambiguous", "amount", "amply", "analogous", "analysis", "anomaly", "apparent", "appear", "apply", "approval", "approximate", "arbitrary", "argument", "as", "assign", "associate", "assume", "at", "attach", "attend", "attendant", "attitude", "attractive", "authorize", "automatic", "awfully", "bad", "badly", "barely", "bare", "basis", "because", "beginning", "behind", "below", "beneficial", "benefit", "better", "blatant", "brief", "broad", "broadly", "by", "candidate", "less", "capable", "careful", "carefully", "care", "casual", "category", "central", "certain", "certainly", "change", "chart", "charter", "cleanly", "clear", "close", "coarse", "collateral", "collect", "command", "commence", "commit", "comparably", "competent", "complaint", "complete", "complicate", "comprehend", "concern", "concise", "conclusion", "confess", "confide", "consider", "constant", "constraint", "construct", "contact", "contrary", "convenient", "correct", "criteria", "crude", "curious", "current", "date", "deadline", "debate", "decide", "decision", "deeply", "deficient", "defined", "delivery", "demand", "depend", "development", "differ", "direct", "disposal", "diverse", "doubt", "doubtful", "dubious", "durable", "dynamic", "early", "effect", "effective", "efficient", "elegant", "employ", "enclosed", "encourage", "endeavor", "end", "enhance", "entertain", "entire", "entirely", "environment", "epistomological", "equally", "equipment", "equitable", "erroneous", "essence", "essential", "eternal", "evaluate", "evenly", "event", "evidence", "evident", "evidently", "exact", "exactly", "excellent", "excess", "existence", "experience", "experiment", "explicit", "expression", "extreme", "failure", "fair", "faith", "fault", "fear", "field", "full", "file", "final", "finance", "finish", "finite", "firmly", "follow", "for", "force", "forecast", "foresee", "formal", "frank", "free", "fresh", "from", "full", "fully", "fundamental", "further", "future", "general", "global", "goal", "good", "gradual", "great", "ground", "grow", "guidance", "guide", "harm", "help", "hierarchy", "high", "hopeful", "huge", "idea", "ideal", "ideally", "identical", "idly", "if", "imaginable", "immediate", "immensely", "impact", "impediment", "important", "in", "incomplete", "increment", "independent", "infallibly", "inform", "initial", "input", "instant", "insurance", "intelligent", "interest", "interface", "intuitive", "inverse", "issuance", "issue", "judgment", "just", "large", "lastly", "likely", "literal", "local", "loosely", "lucid", "major", "manage", "mark", "massive", "material", "material", "matter", "meaningful", "measurable", "measurement", "men", "method", "mildly", "mile", "mind", "minimal", "minor", "model", "moderate", "module", "moment", "move", "natural", "nature", "near", "necessary", "necessity", "need", "new", "next", "normal", "note", "objective", "obvious", "occasion", "of", "official", "only", "opinion", "opportunity", "opposite", "ordinary", "organize", "outcome", "output", "over", "owner", "partial", "past", "perceive", "permission", "person", "personal", "plain", "plan", "please", "policy", "political", "ponder", "portal", "power", "practice", "prefer", "premise", "present", "primary", "principal", "priority", "probably", "procedure", "process", "progress", "project", "prolong", "promptly", "proposal", "protocol", "purchase", "pure", "purpose", "quality", "quantity", "quickly", "quite", "quota", "radical", "random", "rapid", "rather", "real", "reality", "reason", "recent", "recruit", "redundant", "reference", "refine", "register", "regret", "reject", "relative", "reliably", "renew", "repair", "report", "request", "resource", "result", "robust", "routine", "schedule", "separate", "service", "short", "simple", "since", "size", "sponsor", "standard", "statement", "steady", "strike", "strong", "success", "suggest", "super", "system", "task", "team", "temporal", "time", "to", "to", "to", "topical", "total", "trade", "travel", "tremendous", "trivial", "true", "truth", "typical", "ultimate", "under", "unfortunate", "unify", "unique", "useful", "usual", "vague", "valuable", "very", "viable", "view", "vital", "weak", "well", "with", "work", "worse", "worth", "\0"}; /* leave this here */ /* INITWORDS * * This routine gets the word list and sets the maximum * word number in its return value */ int initwords() { register int maxwd; maxwd = 0; while (*words[maxwd] != '\0') { maxwd++; if (maxwd > 10000) /* safety check */ error("Could not find word list end"); } return(maxwd); } !FuNkYsTuFf