home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 1 / ARM_CLUB_CD.iso / contents / apps / languages / progs / curses / examples / c / saa < prev    next >
Encoding:
Text File  |  1991-12-01  |  15.4 KB  |  716 lines

  1. /* Streets and Alleys */
  2. /* This is saa.c version 1.2 of 91/06/13. */
  3.  
  4. /* Streets and Alleys is a game of solitaire.  This implementation is
  5. in ANSI C and uses the curses package.  You can play games with less
  6. than fifty two cards by starting this program with a command line
  7. argument giving the number of ranks to be used. */
  8.  
  9. static char copyright[] = "Copyright 1991 by John D. Ramsdell.";
  10. /* Permission to use, copy, modify, and distribute this software and
  11. its documentation for any purpose and without fee is hereby granted,
  12. provided that the above copyright notice appear in all copies.  John
  13. Ramsdell makes no representations about the suitability of this
  14. software for any purpose.  It is provided "as is" without express or
  15. implied warranty.  */
  16.  
  17. static char what[] = "@(#)saa.c    1.2";
  18.  
  19. /* If your machine does not have an ANSI C compiler, but it does have
  20. the curses package, there is a good chance you can get this program
  21. running by defining NON_ANSI_C. */
  22.  
  23. #if defined NON_ANSI_C
  24. #include <sys/types.h>
  25. #else
  26. #include <stdlib.h>
  27. #endif
  28. #include <time.h>
  29. #include <curses.h>
  30.  
  31. /* If your compiler knows about inlining, don't define inline,
  32. however, this program is so quick it really does not matter. */
  33. #if !defined __GNU__ 
  34. #define inline 
  35. #endif
  36.  
  37. #define nranks 13
  38. #define nsuits 4
  39. #define ncards (nranks * nsuits)
  40.  
  41. #define minranks 5
  42. /* Actual number of cards used in this game. */
  43. int cards;
  44. char *program_name;
  45.  
  46. /* Characters used to print suits. */
  47. #define club 'C'
  48. #define diamond 'D'
  49. #define heart 'H'
  50. #define spade 'S'
  51.  
  52. /* A card is a natural number less then fifty six. */
  53. /* 0, 1, 2, and 3 are used for null cards. */
  54.  
  55. typedef int card_t;
  56.  
  57. static inline int card2rank(card)
  58.      card_t card;
  59. {
  60.   return card / nsuits;
  61. }
  62.   
  63. static inline int card2suit(card)
  64.      card_t card;
  65. {
  66.   return card % nsuits;
  67. }
  68.  
  69. card_t deck[ncards];
  70.  
  71. void shuffle()
  72. {
  73.   int i;
  74.   time_t the_time;
  75.   struct tm *now;
  76.   unsigned int seed;
  77.   if (time(&the_time) == (time_t) -1) {
  78.     endwin();
  79.     fprintf(stderr,
  80.         "%s:\tCannot initialize random number generator using the timer.\n",
  81.         program_name);
  82.     exit (1);
  83.   }
  84.   now = localtime(&the_time);
  85.   seed = now->tm_sec + 60 * now->tm_min + 60 * 60 * now->tm_hour;
  86.   srand(seed);
  87.   for (i = 0; i < cards; i++) deck[i] = i + nsuits;
  88.   for (i = cards - 1; i > 0; i--) {
  89.     int j = rand() % (i + 1);
  90.     int k = deck[i];
  91.     deck[i] = deck[j];
  92.     deck[j] = k;
  93.   }
  94. }
  95.  
  96. /* A pile of cards is represented by a linked list. */
  97. typedef struct card_cell {
  98.   card_t card;
  99.   struct card_cell *rest;
  100. }  card_cell_t;
  101.  
  102. /* The card heap can be preallocated 
  103.    because the number of cards is bounded. */
  104. card_cell_t card_cell[ncards];
  105.  
  106. typedef card_cell_t *card_list_t;
  107.  
  108. #define NIL ((card_list_t) NULL)
  109.  
  110. card_list_t free_list;
  111.  
  112. void free_list_init()
  113. {
  114.   int i;
  115.   free_list = card_cell;
  116.   for (i = 0; i < ncards-1; i++)
  117.     card_cell[i].rest = card_cell + i + 1;
  118.   card_cell[ncards-1].rest = NIL;
  119. }
  120.  
  121. card_list_t cons(card, list)
  122.      card_t card;
  123.      card_list_t list;
  124. {
  125.   card_list_t result = free_list;
  126.   if (result == NIL) {
  127.     endwin();
  128.     fprintf(stderr, "%s:\tCannot get space.\n", program_name);
  129.     exit (1);
  130.   }
  131.   else free_list = result->rest;
  132.   result->card = card;
  133.   result->rest = list;
  134.   return result;
  135. }
  136.  
  137. void dispose(list)
  138.      card_list_t list;
  139. {
  140.   list->rest = free_list;
  141.   free_list = list;
  142. }
  143.  
  144. int length(list)
  145.      card_list_t list;
  146. {
  147.   int len = 0;
  148.   for(; list != NIL; list = list->rest, len++);
  149.   return len;
  150. }
  151.  
  152. /* The state of the game is given by the board. */ 
  153. #define nstacks (2 * nsuits)
  154.  
  155. typedef struct {
  156.   card_list_t stack[nstacks];
  157.   card_t foundation[nsuits];
  158. } board_t;
  159.   
  160. board_t board;
  161.  
  162. static inline void push_card(c, p)
  163.      card_t c;
  164.      int p;
  165. {
  166.   board.stack[p] = cons(c, board.stack[p]);
  167. }
  168.  
  169. static inline int is_nil(p)
  170.      int p;
  171. {
  172.   return board.stack[p] == NIL;
  173. }
  174.  
  175. static inline card_t top_card(p)
  176.      int p;
  177. {
  178.   return board.stack[p]->card;
  179. }
  180.  
  181. static inline void pop_card(p)
  182.      int p;
  183. {
  184.   card_list_t list = board.stack[p];
  185.   board.stack[p] = list->rest;
  186.   dispose(list);
  187. }
  188.  
  189. static inline card_t foundation_ref(r)
  190.      int r;
  191. {
  192.   return board.foundation[r];
  193. }
  194.  
  195. static inline card_t foundation_set(r, c)
  196.      int r;
  197.      card_t c;
  198. {
  199.   board.foundation[r] = c;
  200. }
  201.  
  202. void deal()
  203. {
  204.   int i; int j;
  205.   free_list_init();
  206.   shuffle();
  207.   for (j = 0; j < nstacks; j++) board.stack[j] = NIL;
  208.   for (j = 0; j < nsuits; j++) foundation_set(j, j);
  209.   for (i = 0, j = 0; i < cards; i++, j = (j + 1) % nstacks)
  210.     push_card(deck[i], j);
  211. }
  212.  
  213. /* Procedures used in the display begin with "show_". */
  214.   
  215. void show_suit(suit)
  216.   int suit;
  217. {
  218.   switch (suit) {
  219.   case 0: addch(club); break;
  220.   case 1: addch(diamond); break;
  221.   case 2: addch(heart); break;
  222.   case 3: addch(spade); break;
  223.   default: addch('?'); break;
  224.   }
  225. }
  226.  
  227. void show_rank(rank)
  228.      int rank;
  229. {
  230.   switch (rank) {
  231.   case 0: addch('-'); break;
  232.   case 1: addch('A'); break;
  233.   case 2:
  234.   case 3:
  235.   case 4:
  236.   case 5:
  237.   case 6:
  238.   case 7:
  239.   case 8: 
  240.   case 9: addch(rank+'0'); break;
  241.   case 10: addch('T'); break;
  242.   case 11: addch('J'); break;
  243.   case 12: addch('Q'); break;
  244.   case 13: addch('K'); break;
  245.   default: addch('?');
  246.   }
  247. }
  248.  
  249. void show_card(card)
  250.      card_t card;
  251. {
  252.   show_suit(card2suit(card));
  253.   show_rank(card2rank(card));
  254. }
  255.  
  256. /* Board display routines. */
  257. #define prompt_height 1
  258. #define status_height 1
  259. #define command_height 2
  260. #define board_height 19
  261. #define title_height 1
  262.  
  263. #define stack_indent 11
  264. #define card_size 6
  265.  
  266. int prompt_row, status_row, command_row, board_row, title_row;
  267.  
  268. void init_show()
  269. {
  270.   prompt_row = LINES - prompt_height;
  271.   status_row = prompt_row - status_height;
  272.   command_row = status_row - command_height;
  273.   board_row = command_row - board_height;
  274.   title_row = board_row - title_height;
  275. }
  276.   
  277. void clear_status()
  278. {
  279.   move(status_row, stack_indent);
  280.   clrtoeol();
  281. }
  282.  
  283. void clear_prompt()
  284. {
  285.   move(prompt_row, stack_indent);
  286.   clrtoeol();
  287. }
  288.  
  289. void goto_stack_top(p, h)
  290.      int p, h;
  291. {
  292.   move(command_row - h,
  293.        stack_indent + card_size * (p + 1));
  294. }
  295.  
  296. void goto_foundation(s)
  297.      int s;
  298. {
  299.   goto_stack_top(-1, 2 * (s + 1));
  300. }
  301.  
  302. void erase_top_of_stack(p)
  303.      int p;
  304. {
  305.   int len = length(board.stack[p]);
  306.   goto_stack_top(p, len);
  307.   addstr("  ");
  308. }
  309.  
  310. void show_top_of_stack(p)
  311.      int p;
  312. {
  313.   int len = length(board.stack[p]);
  314.   goto_stack_top(p, len);
  315.   show_card(top_card(p));
  316. }
  317.  
  318. void show_foundation(s)
  319.      int s;
  320. {
  321.   goto_foundation(s);
  322.   show_card(foundation_ref(s));
  323. }
  324.  
  325. void show_board()
  326. {
  327.   int i; int len; card_list_t list;
  328.   for (i = 0; i < nsuits; i++) show_foundation(i);
  329.   for (i = 0; i < nstacks; i++) {
  330.     list = board.stack[i];
  331.     len = length(list);
  332.     for (; list != NIL; list = list->rest, len--) {
  333.       goto_stack_top(i, len);
  334.       show_card(list->card);
  335.     }
  336.   }
  337. }
  338.  
  339. int show_game()
  340. {
  341.   int i;
  342.   clear();
  343.   move(title_row, stack_indent);
  344.   addstr("Streets and Alleys");
  345.   show_board();
  346.   move(command_row, 0);
  347.   addstr("Commands:");
  348.   for (i = -1; i < nstacks; i++) {
  349.     goto_stack_top(i, 0);
  350.     addch(i+'1');
  351.     addch(',');
  352.   }
  353.   goto_stack_top(8, 0);
  354.   addstr("q, r, s, or ?.");
  355.   move(status_row, 0);
  356.   addstr("Status:");
  357.   clear_status();
  358.   addstr("Fresh display.  Type ? for help.");
  359.   move(prompt_row, 0);
  360.   addstr("Prompt:");
  361.   return 0;
  362. }
  363.  
  364. char *help[] = {
  365.   "\tStreets and Alleys version 1.2\n\n",
  366.   "There are eight stacks of cards and a foundation for each suit.  A\n",
  367.   "card may be moved from the top of a stack to its foundation or to\n",
  368.   "the top of another stack.  The object of the game is to order the\n",
  369.   "cards in each stack so that each card is covered only by cards of\n",
  370.   "lesser rank. The ace has the smallest rank and the king has the\n",
  371.   "greatest rank.\n",
  372.   "\n",
  373.   "A card may be moved to its foundation when the card's predecessor of\n",
  374.   "the same suit is there.  A card may be moved to a stack when the top\n",
  375.   "card of the stack has rank one greater than the card being moved.  A\n",
  376.   "card can always be moved to an empty stack.\n",
  377.   "\n",
  378.   "Commands:\t\t\t\tCommand Aliases:\n",
  379.   "\n",
  380.   "  0\tSelect a foundation.\t\t  <space> = 0,\n",
  381.   "  1-8\tSelect a stack.\t\t\t  j = 1, k = 2, l = 3, ; = 4,\n",
  382.   "  q\tQuit the game.\t\t\t  u = 5, i = 6, o = 7, p = 8.\n",
  383.   "  r\tRestore a game from a file.\n",
  384.   "  s\tSave a game in a file.\n",
  385.   "  ?\tPrint this help and then refresh screen.\n",
  386. };
  387.   
  388. int show_help()
  389. {
  390.   char **h;
  391.   clear();
  392.   for (h = help; h < help + sizeof(help)/sizeof(char *); h++)
  393.     addstr(*h);
  394.   move(prompt_row, 0);
  395.   addstr("Type any character to continue. ");
  396.   refresh();
  397.   (void) getch();
  398.   return show_game();
  399. }
  400.  
  401. /* Byron Burke suggested adding the ability to save and restore games,
  402. so you could save a game, try some things, and restore the game if
  403. things didn't work.  Thanks Byron.  */
  404.  
  405. #if !defined SAVE_FILE_NAME
  406. #define SAVE_FILE_NAME "saa.sav"
  407. #endif
  408.  
  409. #if !defined MAGIC_NUMBER
  410. #define MAGIC_NUMBER 13921
  411. #endif
  412.  
  413. int bad_read(f)
  414.      FILE *f;
  415. {
  416.   addstr("Restore error: Read error.");
  417.   fclose(f);
  418.   return 1;            /* generate new game. */
  419. }
  420.  
  421. int restore_game()
  422. {
  423.   int i;
  424.   FILE *f;
  425.   clear_status();
  426.   clear_prompt();
  427.   addstr("Type space to restore game in file ");
  428.   addstr(SAVE_FILE_NAME);
  429.   addstr(". ");
  430.   refresh();
  431.   clear_status();
  432.   if (' ' != getch()) {
  433.     addstr("The restoration of the old game was aborted.");
  434.     return 0;
  435.   }
  436.   if (NULL == (f = fopen(SAVE_FILE_NAME, "r"))) {
  437.     addstr("Restore error: Cannot open ");
  438.     addstr(SAVE_FILE_NAME);
  439.     addstr(".  Game not restored.");
  440.     return 0;
  441.   }
  442.   if (1 != fread(&i, sizeof(int), 1, f) || i != MAGIC_NUMBER) {
  443.     addstr("Restore error: Bad save file format.");
  444.     fclose(f);
  445.     return 0;
  446.   }
  447.   free_list_init();        /* read cards. */
  448.   if (1 != fread(&cards, sizeof(int), 1, f)) return bad_read(f);
  449.   for (i = 0; i < nsuits; i++)    /* read foundations. */
  450.     if (1 != fread(board.foundation + i, sizeof(card_t), 1, f))
  451.       return bad_read(f);
  452.   for (i = 0; i < nstacks; i++) { /* read stacks. */
  453.     int len;
  454.     if (1 != fread(&len, sizeof(int), 1, f)) return bad_read(f);
  455.     board.stack[i] = NIL;
  456.     for (; len > 0; len--) {
  457.       card_t c;
  458.       if (1 != fread(&c, sizeof(card_t), 1, f)) return bad_read(f);
  459.       push_card(c, i);
  460.     }
  461.   }
  462.   fclose(f);
  463.   return show_game();
  464. }
  465.  
  466. int bad_write()
  467. {
  468.   addstr("Save error: Write failed.  Game not saved.");
  469.   return 0;
  470. }
  471.  
  472. int save_game()
  473. {
  474.   int i;
  475.   FILE *f;
  476.   card_t stack[board_height];
  477.   clear_status();
  478.   clear_prompt();
  479.   addstr("Type space to save game in file ");
  480.   addstr(SAVE_FILE_NAME);
  481.   addstr(". ");
  482.   refresh();
  483.   clear_status();
  484.   if (' ' != getch()) {
  485.     addstr("The saving of the game was aborted.");
  486.     return 0;
  487.   }
  488.   if (NULL == (f = fopen(SAVE_FILE_NAME, "w"))) {
  489.     addstr("Save error: Cannot open ");
  490.     addstr(SAVE_FILE_NAME);
  491.     addstr(".  Game not saved.");
  492.     return 0;
  493.   }
  494.   i = MAGIC_NUMBER;
  495.   if (1 != fwrite(&i, sizeof(int), 1, f)) return bad_write();
  496.   if (1 != fwrite(&cards, sizeof(cards), 1, f)) return bad_write();
  497.   for (i = 0; i < nsuits; i++)    /* write foundations. */
  498.     if (1 != fwrite(board.foundation + i, sizeof(card_t), 1, f))
  499.       return bad_write();
  500.   for (i = 0; i < nstacks; i++) { /* write stacks. */
  501.     card_list_t list = board.stack[i];
  502.     int j = 0;
  503.     for (; list != NIL; list = list->rest, j++)
  504.       stack[j] = list->card;
  505.     if (1 != fwrite(&j, sizeof(int), 1, f)) return bad_write();
  506.     for (j--; j >= 0; j--)
  507.       if (1 != fwrite(stack + j, sizeof(card_t), 1, f))
  508.     return bad_write();
  509.   }
  510.   fclose(f);
  511.   addstr("Game saved.");
  512.   return 0;
  513. }
  514.  
  515. int is_stack_done(p)
  516.      int p;
  517. {
  518.   card_list_t list;
  519.   if (is_nil(p)) return 1;
  520.   for (list = board.stack[p]; list->rest != NIL; list = list->rest) 
  521.     if (card2rank(list->card) >= card2rank(list->rest->card)) return 0;
  522.   return 1;
  523. }
  524.  
  525. int is_done()            /* The game is done when all cards */
  526. {                /* have been moved to the foundation, */
  527.   int i;            /* or every stack is ordered by rank. */
  528.   for (i = 0; i < nstacks; i++)
  529.     if (!is_stack_done(i)) return 0;
  530.   return 1;
  531. }
  532.  
  533. /* Procedures that read input and move cards. */
  534.  
  535. int move_to_foundation(from)
  536.      int from;
  537. {
  538.   card_t c = top_card(from);
  539.   int to = card2suit(c);
  540.   if (c == nsuits + foundation_ref(to)) {
  541.     erase_top_of_stack(from);
  542.     pop_card(from);
  543.     foundation_set(to, c);
  544.     show_foundation(to);
  545.     clear_status();
  546.     addstr("The ");
  547.     show_card(c);
  548.     addstr(" was");
  549.   }
  550.   else {
  551.     clear_status();
  552.     addstr("The ");
  553.     show_card(c);
  554.     addstr(" cannot be");
  555.   }
  556.   addstr(" moved to the foundation.");
  557.   return 0;
  558. }
  559.  
  560. int move_to_stack(from, to)
  561.      int from, to;
  562. {
  563.   card_t c = top_card(from);
  564.   if (is_nil(to) || card2rank(top_card(to)) == 1 + card2rank(c)) {
  565.     erase_top_of_stack(from);
  566.     pop_card(from);
  567.     push_card(c, to);
  568.     show_top_of_stack(to);
  569.     clear_status();
  570.     addstr("Moved the ");
  571.     show_card(c);
  572.   }
  573.   else {
  574.     clear_status();
  575.     addstr("The ");
  576.     show_card(c);
  577.     addstr(" cannot be moved");
  578.   }
  579.   addstr(" from stack ");
  580.   addch(from+'1');
  581.   addstr(" to stack ");
  582.   addch(to+'1');
  583.   addch('.');
  584.   return 0;
  585. }
  586.  
  587. int getcmd()            /* Implements aliases for commands. */
  588. {
  589.   int c = getch();
  590.   switch (c) {
  591.   case ' ': return '0';        /* Aliases for use when there is no */
  592.   case 'j': return '1';        /* numeric keypad. */
  593.   case 'k': return '2';
  594.   case 'l': return '3';
  595.   case ';': return '4';
  596.   case 'u': return '5';
  597.   case 'i': return '6';
  598.   case 'o': return '7';
  599.   case 'p': return '8';
  600.   default: return c;
  601.   }
  602. }
  603.  
  604. int get_other_move(from)
  605.      int from;
  606. {
  607.   int to;
  608.   clear_prompt();
  609.   addstr("Move ");
  610.   show_card(top_card(from));
  611.   addstr(" from stack ");
  612.   addch(from+'1');
  613.   addstr(" to ");
  614.   refresh();
  615.   to = getcmd();
  616.   if (to == EOF || to == 'q') return 1;
  617.   if (to == 'r') return restore_game();
  618.   if (to == 's') return save_game();
  619.   if (to == '?') return show_help();
  620.   to -= '1';
  621.   clear_status();
  622.   if (to < -1 || to >= 8) {
  623.     addstr("Bad input.  Type ? for help.");
  624.     return 0;
  625.   }
  626.   if (to == -1) return move_to_foundation(from);
  627.   else return move_to_stack(from,to);
  628. }
  629.  
  630. int get_move()
  631. {
  632.   int from;
  633.   clear_prompt();
  634.   addstr("Move from stack ");
  635.   refresh();
  636.   from = getcmd();
  637.   if (from == EOF || from == 'q') return 1;
  638.   if (from == 'r') return restore_game();
  639.   if (from == 's') return save_game();
  640.   if (from == '?') return show_help();
  641.   from -= '1';
  642.   if (from < 0 || from >= 8) {
  643.     clear_status();
  644.     addstr("Bad input.  Type ? for help.");
  645.     return 0;
  646.   }
  647.   if (is_nil(from)) {
  648.     clear_status();
  649.     addstr("There is no card in stack ");
  650.     addch(from+'1');
  651.     addch('.');
  652.     return 0;
  653.   }
  654.   return get_other_move(from);
  655. }
  656.  
  657. int play_one_game ()
  658. {
  659.   deal();
  660.   (void) show_game();
  661.   while (1)
  662.     if (is_done()) return 0;
  663.     else if (get_move()) return 1;
  664. }      
  665.  
  666. void play()
  667. {
  668.   int ch, status;
  669.   init_show();
  670.   while (1) {
  671.     status = play_one_game();
  672.     clear_status();
  673.     addstr(status == 0 ? "You won!" : "You lose.");
  674.     clear_prompt();
  675.     addstr("Press space to play again. ");
  676.     refresh();
  677.     ch = getch();
  678.     if (ch != ' ') return;
  679.   }
  680. }
  681.  
  682. int usage()
  683. {
  684.   char **h;
  685.   for (h = help; h < help + sizeof(help)/sizeof(char *); h++)
  686.     fputs(*h, stderr);
  687.   fprintf(stderr, "\nUsage:\t%s [number_of_ranks].\n", program_name);
  688.   fprintf(stderr, "The number of ranks must be between %d and %d.\n",
  689.       minranks, nranks);
  690.   return 1;
  691. }
  692.   
  693. int main(argc, argv)
  694.      int argc;
  695.      char *argv[];
  696. {
  697.   program_name = argv[0];
  698.   if (argc > 2) usage();
  699.   if (argc == 2) {        /* Small game requested. */
  700.     cards = atoi(argv[1]);
  701.     if (cards < minranks || cards > nranks) return usage();
  702.     cards *= nsuits;
  703.   }
  704.   else cards = ncards;
  705.   if (ERR == initscr()) {
  706.     fprintf(stderr, "%s:\tCannot initialize screen.\n", program_name);
  707.     return 1;
  708.   }
  709.   cbreak();
  710.   noecho();
  711.   play();
  712.   endwin();
  713.   putchar('\n');
  714.   return 0;
  715. }
  716.