home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 332_01 / jotto.c < prev    next >
C/C++ Source or Header  |  1990-03-25  |  14KB  |  673 lines

  1. /*-                            -*- Fundamental -*-
  2.  *
  3.  *  Facility:            jotto(6)
  4.  *
  5.  *  File:            jotto.c
  6.  *
  7.  *  Associated files:        - [ none]
  8.  *
  9.  *  Description:        Plays the game of jotto
  10.  *
  11.  *  Notes:            No rules included.
  12.  *
  13.  *  Portability:        Edited to conform to X/Open Portability Guide,
  14.  *                ed. 3, 1988
  15.  *
  16.  *  Author:            David M. Fogg
  17.  *                2632 N.E. Fremont
  18.  *                Portland, OR 97212
  19.  *
  20.  *  Editor:            Anders Thulin
  21.  *                Rydsvagen 288
  22.  *                S-582 50 Linkoping
  23.  *                SWEDEN
  24.  *
  25.  *
  26.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  27.  *
  28.  *  Copyright (c) 1981 by David M. Fogg
  29.  *
  30.  *  Permission is herewith granted for noncommercial distribution
  31.  *  through the BDS User's Group; any and all forms of commercial
  32.  *  redistribution are strenuously unwished-for.
  33.  *
  34.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  35.  *
  36.  *  Edit history :
  37.  *
  38.  *  Vers  Ed   Date       By             Comments
  39.  *  ----  ---  ----------  ----------------  -------------------------------
  40.  *   1.0    0  1980-11-13  David M. Fogg     Creation day
  41.  *          1  1980-11-18  David M. Fogg     Creation ... finished
  42.  *          2  1980-11-10  David M. Fogg     Bugwork
  43.  *   1.1    3  1988-10-25  Anders Thulin     Many BDS-isms removed to make
  44.  *                         program K&R compatible. General
  45.  *                         cleanup for portability. Added
  46.  *                         some comments.
  47.  *                    
  48.  */
  49.  
  50. /* - - Configuration options: - - - - - - - - - - - - - - - - - - - - - - - */
  51.  
  52. /*
  53.  *    
  54.  *  The following environments are supported:
  55.  *
  56.  *    ANSI        ANSI C
  57.  *    BSD        Berkeley UNIX
  58.  *    SV2        AT&T UNIX System V.2
  59.  *    XPG3        X/Open Portability Guide, ed. 3
  60.  *    ZTC205        MS-DOS + Zortech C 2.05
  61.  *
  62.  *  If you have an ANSI C conforming compiler, define ANSI. Otherwise,
  63.  *  use the definition that best matches your environment.
  64.  *
  65.  */
  66.     
  67. #define    ANSI        0    /* 1 -> ANSI C, 0 -> K&R */
  68. #define    BSD        0
  69. #define    SV2        0
  70. #define    XPG2        0
  71. #define ZTC205        1
  72.  
  73. #if ANSI
  74. # include <ctype.h>
  75. # include <stdio.h>
  76. # include <stdlib.h>
  77. # include <string.h>
  78. # include <time.h>
  79. #endif
  80.  
  81. #if BSD
  82. # include <ctype.h>
  83. # include <stdio.h>
  84. # include <string.h>
  85.  typedef long  time_t;
  86.  extern void   exit();
  87.  extern int    rand();
  88.  extern void   srand();
  89.  extern time_t time();        
  90. #endif
  91.  
  92. #if SV2 
  93. # include <ctype.h>
  94. # include <stdio.h>
  95. # include <string.h>
  96.  typedef long  time_t;
  97.  extern void   exit();
  98.  extern int    rand();
  99.  extern void   srand();
  100.  extern time_t time();        
  101. #endif
  102.  
  103. #if XPG3
  104. # include <ctype.h>
  105. # include <stdio.h>
  106. # include <string.h>
  107.  typedef long  time_t;    /*[???] or should it be time_t from <sys/types.h> ? */
  108.  extern void   exit();
  109.  extern int    rand();
  110.  extern void   srand();
  111.  extern time_t time();    
  112. #endif
  113.  
  114. #if ZTC205
  115. # include <ctype.h>
  116. # include <stdio.h>
  117. # include <string.h>
  118.  typedef long  time_t;
  119.  extern void   exit();
  120.  extern int    rand();
  121.  extern void   srand();
  122.  extern time_t time();    
  123. #endif
  124.  
  125. # include <curses.h>
  126.  
  127. /*
  128.  *  Tweakables:
  129.  *
  130.  *  The reason ALPHA_SIZE has been declared as a macro is that some
  131.  *  compilers handle sizeof("<string>") as a synonym to sizeof(char *),
  132.  *  which is wrong.  ALPHA_SIZE should be defined to be the number
  133.  *  of characters in the string ALPHABET.
  134.  *
  135.  */
  136.  
  137. #define WORD_FILE    "jotto.wds"    /* default word file */
  138. #define MAXWORDS      600        /* max nr of words in word file */
  139.  
  140. #define    ALPHABET    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  141. #define    ALPHA_SIZE    26
  142.  
  143. /* Code for RETURN/ENTER key -- some systems use \r, some \n */
  144.  
  145. #define RETURN        '\n'
  146.  
  147. /*
  148.  *  Non-tweakables:
  149.  *
  150.  */
  151.  
  152. #ifndef TRUE
  153. # define    TRUE    1
  154. # define    FALSE    0
  155. #endif
  156.  
  157. #define LETS      5    /* letters/word */
  158. #define ISCORE      21    /* initial score */
  159.  
  160. /* screen layout parameters */
  161.  
  162. #define XTITLE      40    /* Title loc */
  163. #define YTITLE      1
  164. #define XPROMPT   33    /* prompt loc */
  165. #define YPROMPT   4
  166. #define XSCORE      3    /* score col */
  167. #define XGUESS      10    /* guess col */
  168. #define XMATCH      17    /* match col */
  169. #define XSTATE      20    /* status col */
  170. #define YHEAD      0    /* header row */
  171. #define YFIRST      2    /* first guess row */
  172. #define XLABELS   33    /* list labels col */
  173. #define XLISTS      42    /* lists cols */
  174. #define YYES      6    /* confirmed letters row */
  175. #define YNO      8    /* eliminated letters row */
  176. #define YPOSS      10    /* possible letters row */
  177. #define XINST      30    /* instructions loc */
  178. #define YINST      13
  179.  
  180. /*
  181.  *  Global variables
  182.  *
  183.  */
  184.  
  185. static int  wordok, giveup;
  186. static int  score;
  187. static char word[LETS+3];
  188. static char guess[LETS+1], gots[LETS+1];
  189. static int  count;
  190. static char guesslis[ISCORE+1][LETS+1];
  191. static int  changed;
  192.  
  193. static char nots[ALPHA_SIZE],
  194.             maybes[ALPHA_SIZE];
  195.  
  196. static char alphabet[] = ALPHABET;
  197.  
  198. /*
  199.  *  Local functions:
  200.  *
  201.  */
  202.  
  203. #ifdef __STDC__
  204.  static void dochange(void);
  205.  static void doguess(void);
  206.  static void doscreen(void);
  207.  static void dostate(int s);
  208.  static int  getlet(void);
  209.  static void getword(char *wd);
  210.  static int  inset(char *string, int ch);
  211.  static void inword(int x, int y, char *p, char *w);
  212.  static void modgot (int old, int new);
  213.  static int  nomore(void);
  214.  static void printset(char *s);
  215.  static void xfer (int l, char *src, char *dest);
  216. #else
  217.  static void dochange();
  218.  static void doguess();
  219.  static void doscreen();
  220.  static void dostate();
  221.  static int  getlet();
  222.  static void getword();
  223.  static int  inset();
  224.  static void inword();
  225.  static void modgot ();
  226.  static void printset();
  227.  static int  nomore();
  228.  static void xfer ();
  229. #endif
  230.  
  231. /*
  232.  *  Routine:        dochange
  233.  *
  234.  *  Description:    Read change commands from the terminal and
  235.  *            perform the changes.
  236.  *
  237.  */
  238.  
  239. static void dochange ()
  240. {
  241.    int  ch;
  242.    int sc;
  243.    char let;
  244.    static char legals[] = {'A', '+', '=', '_', 'N', '+', RETURN, '\0'};
  245.  
  246.    changed = 0;
  247.    wordok = FALSE;
  248.  
  249.    mvaddstr(YPROMPT, XPROMPT, "Change: "); refresh();
  250.    do {
  251.      if (islower(ch = getch())) {
  252.        ch = toupper(ch);
  253.      }    
  254.    } while (inset(legals, ch) == 0);
  255.    refresh();
  256.    if (ch == 'A')
  257.       return;
  258.    else
  259.       wordok = TRUE;
  260.  
  261.    while (ch != RETURN && changed < LETS) {
  262.       ++changed;
  263.       switch (ch) {
  264.      case '=': ch = '+'; break;
  265.      case '_': ch = '-';
  266.       }
  267.       addch(ch);
  268.       let = getlet(); addch(' ');
  269.       switch (ch) {
  270.      case '+': modgot(' ', let);        break;
  271.      case '-': modgot(let, ' ');        break;
  272.      case 'N': xfer(let, maybes, nots); break;
  273.      case 'M': xfer(let, nots, maybes); break;
  274.       }
  275.       refresh();
  276.       do {
  277.      ch = toupper(getch());
  278.       } while (inset(legals, ch) == 0);
  279.       refresh();
  280.    }
  281.    if (changed > 0) {
  282.       move(YPROMPT, XPROMPT); clrtoeol();
  283.       move(YYES, XLISTS); addstr(gots);
  284.       move(YNO, XLISTS); printset(nots);
  285.       move(YPOSS, XLISTS); printset(maybes);
  286.       for (sc = ISCORE; sc >= score ; --sc) dostate(sc);
  287.       dochange();
  288.    }
  289. }
  290.  
  291. /*
  292.  *  Routine:        doguess
  293.  *
  294.  *  Description:    Input guess from user and process it.
  295.  *
  296.  */
  297.  
  298. static void doguess ()    
  299. {
  300.   int i, j, y;
  301.   char str[LETS+1];
  302.  
  303.   /*  1.  Get guess from user:  */
  304.  
  305.   y = YFIRST + ISCORE - score;
  306.   move(y, XSCORE); printw("%4d", score);
  307.   inword(XGUESS-1, y, " ", guess);
  308.  
  309.   /*  2.  Process guess:  */
  310.  
  311.   if (strcmp(guess, "?????") == 0) {
  312.     giveup = TRUE;
  313.   } else {
  314.     giveup = FALSE;
  315.     strcpy(str, word); count = 0;
  316.     for (i = 0; i < LETS; ++i) {
  317.       if ((j = inset(str, guess[i])) > 0) {
  318.         ++count;
  319.         str[j-1] = tolower(str[j-1]);
  320.       }
  321.     }
  322.     printw("%3d", count);
  323.     strcpy(guesslis[score], guess);
  324.     dostate(score);
  325.  }
  326. }
  327.  
  328. /*
  329.  *  Routine:        doscreen
  330.  *
  331.  *  Description:    Clears the screen. Prints guess columns, some
  332.  *            help text and various other stuff.
  333.  *
  334.  */
  335.  
  336. static void doscreen()
  337. {
  338.   clear();
  339.  
  340.   mvaddstr(YTITLE, XTITLE, "--> J O T T O  <--");
  341.  
  342.   mvaddstr(YHEAD,   XSCORE, "Score  Guess  #  STATE");
  343.   mvaddstr(YHEAD+1, XSCORE, "=====  =====  =  =====");
  344.  
  345.   move(YYES, XLABELS+3);  addstr("YES:");
  346.  
  347.   move(YNO, XLABELS+4);   addstr("NO:");
  348.  
  349.   move(YPOSS, XLABELS+1); addstr("MAYBE:");
  350.   move(YPOSS, XLISTS);    printset(maybes);
  351.  
  352.   mvaddstr(YINST,    XINST, "<????\?> to 'Your word:' gets random word");
  353.   mvaddstr(YINST+1,  XINST, "<????\?> instead of guess aborts game");
  354.   mvaddstr(YINST+2,  XINST, "Change:");
  355.   mvaddstr(YINST+3,  XINST, "  <A> allows re-entry of bad word");
  356.   mvaddstr(YINST+4,  XINST, "  <+X> adds 'X' to YES list");
  357.   mvaddstr(YINST+5,  XINST, "  <-X> takes 'X' off YES list");
  358.   mvaddstr(YINST+6,  XINST, "  <NX> move 'X' MAYBE->NO");
  359.   mvaddstr(YINST+7,  XINST, "  <MX> move 'X' NO->MAYBE");
  360.   mvaddstr(YINST+8,  XINST, "  <RETURN> performs changes (if any)");
  361.   mvaddstr(YINST+10, XINST, "STATE: upcase=YES; locase=maybe; '.'=NO");
  362. }
  363.  
  364.  
  365. /*
  366.  *  Routine:        dostate
  367.  *
  368.  *  Description:    Print the 'state' of the guessed word for
  369.  *            score s.
  370.  *
  371.  */
  372.  
  373. static void dostate (s)
  374. int s;
  375. {
  376.    int i, j;
  377.    char ch;
  378.    char ges[LETS+1], got[LETS+1];
  379.  
  380.    move(YFIRST + ISCORE - s, XSTATE);
  381.    strcpy(ges, guesslis[s]);
  382.    strcpy(got, gots);
  383.  
  384.    for (i = 0; i < LETS; ++i) {
  385.       ch = ges[i];
  386.       if ((j = inset(got, ch)) > 0) {
  387.      addch(ch);
  388.      strcpy(got+j-1, got+j);
  389.       }
  390.       else
  391.      addch((inset(nots, ch) > 0) ? '.' : tolower(ch));
  392.    }
  393. }
  394.  
  395. /*
  396.  *  Routine:        getlet
  397.  *
  398.  *  Description:    Read a valid character from the terminal,
  399.  *            convert it to upper case, and return it.
  400.  *
  401.  *            Non-valid characters are not echoed.
  402.  *
  403.  */
  404.  
  405. static int getlet()
  406. {
  407.   int ch;
  408.  
  409.   do {
  410.     ch = getch();
  411.     if (islower(ch)) {
  412.       ch = toupper(ch);
  413.     }
  414.   } while (!isalpha(ch) && ch != '?');
  415.  
  416.   addch(ch);
  417.   refresh();
  418.   return (ch);
  419. }
  420.  
  421.  
  422. /*
  423.  *  Routine:        getword
  424.  *
  425.  *  Parameter:        wd    storage space for word
  426.  *
  427.  *  Description:    Reads a LETS-letter word from terminal.
  428.  *          
  429.  */
  430.  
  431. static void getword (wd)
  432. char *wd;
  433. {
  434.   int i;
  435.  
  436.   refresh();
  437.  
  438.   for (i = 0; i < LETS; ++i) {
  439.     wd[i] = getlet();
  440.   }
  441.  
  442.   wd[LETS] = NULL;
  443. }
  444.  
  445.  
  446. /*
  447.  *  Routine:        inset
  448.  *
  449.  *  Parameters:        str    a reference string
  450.  *            c    a character
  451.  *
  452.  *  Description:    Return the position of the first occurrence of
  453.  *            c in str. 
  454.  *
  455.  *            If c does not appear in str, 0 is returned.
  456.  *
  457.  *  Note:        inset("ABC", 'A') == 1
  458.  *
  459.  */
  460.  
  461. static int inset(str, c)
  462. char *str;
  463. int   c;
  464. {
  465.   char *p;
  466.  
  467.   if ((p = strchr(str, c)) == 0) {
  468.     return 0;
  469.   } else {
  470.     return p-str+1;
  471.   }
  472. }
  473.  
  474. /*
  475.  *  Routine:        inword
  476.  *
  477.  *  Parameters:        x, y    screen coordinates
  478.  *            p    prompt string
  479.  *            w    input word
  480.  *
  481.  *  Description:    Prints a prompt string, and reads one word from
  482.  *            the console. One word contains exactly LETS
  483.  *            letters.
  484.  *
  485.  */
  486.  
  487. static void inword (x, y, p, w)
  488. int x, y;
  489. char *p, *w;
  490. {
  491.   mvaddstr(y, x, p); getword(w);
  492. }
  493.  
  494. /*
  495.  *  Routine:        main
  496.  *
  497.  *  Description:    ...
  498.  *
  499.  */
  500.  
  501. int main (ac, av)
  502. int ac;
  503. char *av[];
  504. {
  505.   FILE *wf;
  506.   int numwords;
  507.   char wordlist[MAXWORDS+1][LETS+1];
  508.   char wfil[15];         /* word file name */
  509.  
  510.   /*  0.  Read the jotto words:  */
  511.  
  512.   if (ac < 2)                  /* set wordfile name */
  513.     strcpy(wfil, WORD_FILE);
  514.   else
  515.     strcpy(wfil, av[1]);
  516.  
  517.   if ((wf = fopen(wfil, "r")) == NULL) {    /* open word file */
  518.     fprintf(stderr, "jotto: failed to open word file '%s'\n", wfil);
  519.     exit(1);
  520.   }
  521.  
  522.   numwords = 0;              /* read wordfile */
  523.   while (fgets(word, sizeof(word), wf) != NULL) {
  524.     word[LETS] = '\0';
  525.     strcpy(wordlist[numwords++], word);
  526.   }
  527.   fclose(wf);
  528.  
  529.   /*  1.  Initialize various things:  */
  530.  
  531.   initscr();        /* init curses */
  532.   noecho();
  533.   cbreak();
  534.  
  535.   srand((unsigned int) time((time_t *) 0));
  536.  
  537.   /*  2.  Main loop:  */
  538.   
  539.   while (TRUE)  {    
  540.     score = ISCORE;
  541.     nots[0] = NULL;
  542.     strcpy(maybes, alphabet);
  543.     strcpy(gots, "     ");
  544.  
  545.     doscreen();
  546.     inword(XPROMPT, YPROMPT, "Your word: ", word);
  547.     if (strcmp(word, "?????") == 0) {
  548.       strcpy(word, wordlist[rand() % numwords]);
  549.     }
  550.     mvaddstr(YPROMPT, XPROMPT, "                    ");
  551.     do {
  552.       doguess();
  553.       if (strcmp(guess, word) != 0 && !giveup) dochange();
  554.       if (wordok) --score;
  555.     } while (strcmp(guess, word) != 0 && score > 0 && !giveup);
  556.     move(YPROMPT-1, XPROMPT);
  557.     if (strcmp(guess, word))
  558.       printw("The word was: %s.", word);
  559.     else
  560.       printw("Congrats: you got %d points!", ++score);
  561.  
  562.     if (nomore()) break;
  563.   }
  564.  
  565.   endwin();    
  566.  
  567.   return 0;
  568. }
  569.  
  570. /*
  571.  *  Routine:        modgot
  572.  *
  573.  *  Parameters:        old
  574.  *            new
  575.  *
  576.  *  Description:    if gots[] contains the character old, then
  577.  *            replace it with the character 'new'.
  578.  *
  579.  */
  580.  
  581. static void modgot (old, new)
  582. char old, new;
  583. {
  584.    int j;
  585.  
  586.    if ((j = inset(gots, old)) > 0) gots[--j] = new;
  587. }
  588.  
  589.  
  590. /*
  591.  *  Routine:        nomore
  592.  *
  593.  *  Description:    Ask if the user wishes another game. 
  594.  *            Return TRUE if he doesn't.
  595.  *
  596.  */
  597.  
  598. static int nomore ()
  599. {
  600.   int ch;
  601.  
  602.   mvaddstr(YPROMPT, XPROMPT, "Play again? [y/n] ");
  603.  
  604.   refresh();
  605.  
  606.   do {
  607.      if (islower(ch = getch())) {
  608.         ch = toupper(ch);
  609.      }
  610.   } while (ch != 'Y' && ch != 'N');
  611.   addch(ch);
  612.  
  613.   refresh();
  614.  
  615.   return (ch == 'N' ? TRUE : FALSE);
  616. }
  617.  
  618. /*
  619.  *  Routine:        printset
  620.  *
  621.  *  Parameter:        s     a string with uppercase letters, not
  622.  *                necessarily in alphabetical order.
  623.  *
  624.  *  Description:    Print the letters in s in alphabetical order
  625.  *            leaving spaces between non-consecutive letters.
  626.  *
  627.  *            Letters in s that also are in gots[] are printed
  628.  *            in upper case. Otherwise in lower case.
  629.  *
  630.  */
  631.  
  632. static void printset (s)
  633. char *s;
  634. {
  635.   int i;
  636.   char c;
  637.  
  638.   for (i = 0; i < ALPHA_SIZE; i++) {    
  639.     c = alphabet[i];
  640.     if (inset(s, c)) {
  641.       addch(inset(gots, c) ? c : tolower(c));
  642.     } else {
  643.       addch(' ');
  644.     }
  645.   }
  646. }
  647.  
  648. /*
  649.  *  Routine:        xfer
  650.  *
  651.  *  Parameters:        l    A character
  652.  *            src    The source string
  653.  *            dest    The destination string
  654.  *
  655.  *  Description:    Remove the character l from src and add it to
  656.  *            dest.
  657.  *
  658.  *            If l does not appear in src, do nothing.
  659.  *
  660.  */
  661.  
  662. static void xfer (l, src, dest)
  663. char l;
  664. char *src, *dest;
  665. {
  666.   int j;
  667.  
  668.   if ((j = inset(src, l)) > 0) {
  669.     strcpy(src+j-1, src+j);
  670.     dest[j = strlen(dest)] = l; dest[++j] = NULL;
  671.   }
  672. }
  673.