[comp.sources.games] v12i080: saa - Streets and Alleys solitaire, Part01/01

billr@saab.CNA.TEK.COM (Bill Randle) (05/17/91)

Submitted-by: ramsdell@linus.mitre.org
Posting-number: Volume 12, Issue 80
Archive-name: saa/Part01
Environment: curses


	[I was able to compile and run this just fine on my Sun 3/60
	 SunOS 3.5 (definately not Posix compliant).   -br] 

[[		saa - Streets and Alleys Solitaire

saa is an implementation of the solitaire game Streets and Alleys for
Posix compliant machines that also provide the curses terminal screen
handling and optimization package.  It is written in ANSI C, but in a
style that should allow its compilation by many non-ANSI C compilers.

Enjoy!
John]]

#! /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 <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README MANIFEST Makefile saa.c
# Wrapped by billr@saab on Fri May 17 08:40:32 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1022 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X		saa - Streets and Alleys Solitaire
X
XThis is README version 1.1 of 91/05/17 for Streets and Alleys.
X
Xsaa is an implementation of the solitaire game Streets and Alleys for
XPosix compliant machines that also provide the curses terminal screen
Xhandling and optimization package.
X
XTo create an executable named `saa', type `make'.  If the compilation
Xfails because stdlib.h is missing, type `make "CFLAGS=-O -DNON_ANSI_C"'.
X
XTo learn how play the game, type `saa help', or start saa and type
X`?'.  You can play streets and alleys with a reduced sized deck by
Xgiving saa the number of ranks with which you wish to play.
X
XEnjoy!
XJohn
X
XCopyright 1991 by John D. Ramsdell.
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted,
Xprovided that the above copyright notice appear in all copies.  John
XRamsdell makes no representations about the suitability of this
Xsoftware for any purpose.  It is provided "as is" without express or
Ximplied warranty.
END_OF_FILE
if test 1022 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(238 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	
X README                     1	
X saa.c                      1	
END_OF_FILE
if test 238 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(323 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# This is Makefile version 1.1 of 91/05/17 for Streets and Alleys.
X
XPROG	= saa
XCFLAGS	= -O
X# Define NON_ANSI_C if /usr/include/stdlib.h is missing.
X#CFLAGS	= -O -DNON_ANSI_C
XLDLIBS	= -lcurses -ltermcap
X
Xall:	$(PROG)
X
X$(PROG):	$(PROG).c
X	$(CC) $(CFLAGS) -o $(PROG) $(PROG).c $(LDLIBS)
X
Xclean:
X	rm $(PROG)
X
X.PHONY:	all clean
END_OF_FILE
if test 323 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'saa.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'saa.c'\"
else
echo shar: Extracting \"'saa.c'\" \(12201 characters\)
sed "s/^X//" >'saa.c' <<'END_OF_FILE'
X/* Streets and Alleys */
X/* This is saa.c version 1.1 of 91/05/17. */
X
X/* Streets and Alleys is a game of solitaire.  This implementation is
Xin ANSI C and uses the curses package.  You can play games with less
Xthan fifty two cards by starting this program with a command line
Xargument giving the number of ranks to be used. */
X
Xstatic char copyright[] = "Copyright 1991 by John D. Ramsdell.";
X/* Permission to use, copy, modify, and distribute this software and
Xits documentation for any purpose and without fee is hereby granted,
Xprovided that the above copyright notice appear in all copies.  John
XRamsdell makes no representations about the suitability of this
Xsoftware for any purpose.  It is provided "as is" without express or
Ximplied warranty.  */
X
Xstatic char what[] = "@(#)saa.c	1.1";
X
X/* If your machine does not have an ANSI C compiler, but it does have
Xthe curses package, there is a good chance you can get this program
Xrunning by defining NON_ANSI_C. */
X
X#if defined NON_ANSI_C
X#include <sys/types.h>
X#else
X#include <stdlib.h>
X#endif
X#include <time.h>
X#include <curses.h>
X
X/* If your compiler knows about inlining, don't define inline,
Xhowever, this program is so quick it really does not matter. */
X#if !defined __GNU__ 
X#define inline 
X#endif
X
X#define nranks 13
X#define nsuits 4
X#define ncards (nranks * nsuits)
X
X#define minranks 5
X/* Actual number of cards used in this game. */
Xint cards;
Xchar *program_name;
X
X/* Characters used to print suits. */
X#define club 'C'
X#define diamond 'D'
X#define heart 'H'
X#define spade 'S'
X
X/* A card is a natural number less then fifty six. */
X/* 0, 1, 2, and 3 are used for null cards. */
X
Xtypedef int card_t;
X
Xstatic inline int card2rank(card)
X     card_t card;
X{
X  return card / nsuits;
X}
X  
Xstatic inline int card2suit(card)
X     card_t card;
X{
X  return card % nsuits;
X}
X
Xcard_t deck[ncards];
X
Xvoid shuffle()
X{
X  int i;
X  time_t the_time;
X  struct tm *now;
X  unsigned int seed;
X  if (time(&the_time) == (time_t) -1) {
X    endwin();
X    fprintf(stderr,
X	    "%s:\tCannot initialize random number generator using the timer.\n",
X	    program_name);
X    exit (1);
X  }
X  now = localtime(&the_time);
X  seed = now->tm_sec + 60 * now->tm_min + 60 * 60 * now->tm_hour;
X  srand(seed);
X  for (i = 0; i < cards; i++) deck[i] = i + nsuits;
X  for (i = cards - 1; i > 0; i--) {
X    int j = rand() % (i + 1);
X    int k = deck[i];
X    deck[i] = deck[j];
X    deck[j] = k;
X  }
X}
X
X/* A pile of cards is represented by a linked list. */
Xtypedef struct card_cell {
X  card_t card;
X  struct card_cell *rest;
X}  card_cell_t;
X
X/* The card heap can be preallocated 
X   because the number of cards is bounded. */
Xcard_cell_t card_cell[ncards];
X
Xtypedef card_cell_t *card_list_t;
X
X#define NIL ((card_list_t) NULL)
X
Xcard_list_t free_list;
X
Xvoid free_list_init()
X{
X  int i;
X  free_list = card_cell;
X  for (i = 0; i < ncards-1; i++)
X    card_cell[i].rest = card_cell + i + 1;
X  card_cell[ncards-1].rest = NIL;
X}
X
Xcard_list_t cons(card, list)
X     card_t card;
X     card_list_t list;
X{
X  card_list_t result = free_list;
X  if (result == NIL) {
X    endwin();
X    fprintf(stderr, "%s:\tCannot get space.\n", program_name);
X    exit (1);
X  }
X  else free_list = result->rest;
X  result->card = card;
X  result->rest = list;
X  return result;
X}
X
Xvoid dispose(list)
X     card_list_t list;
X{
X  list->rest = free_list;
X  free_list = list;
X}
X
Xint length(list)
X     card_list_t list;
X{
X  int len = 0;
X  for(; list != NIL; list = list->rest, len++);
X  return len;
X}
X
X/* The state of the game is given by the board. */ 
X#define nstacks (2 * nsuits)
X
Xtypedef struct {
X  card_list_t stack[nstacks];
X  card_t foundation[nsuits];
X} board_t;
X  
Xboard_t board;
X
Xstatic inline void push_card(c, p)
X     card_t c;
X     int p;
X{
X  board.stack[p] = cons(c, board.stack[p]);
X}
X
Xstatic inline int is_nil(p)
X     int p;
X{
X  return board.stack[p] == NIL;
X}
X
Xstatic inline card_t top_card(p)
X     int p;
X{
X  return board.stack[p]->card;
X}
X
Xstatic inline void pop_card(p)
X     int p;
X{
X  card_list_t list = board.stack[p];
X  board.stack[p] = list->rest;
X  dispose(list);
X}
X
Xstatic inline card_t foundation_ref(r)
X     int r;
X{
X  return board.foundation[r];
X}
X
Xstatic inline card_t foundation_set(r, c)
X     int r;
X     card_t c;
X{
X  board.foundation[r] = c;
X}
X
Xvoid deal()
X{
X  int i; int j;
X  free_list_init();
X  shuffle();
X  for (j = 0; j < nstacks; j++) board.stack[j] = NIL;
X  for (j = 0; j < nsuits; j++) foundation_set(j, j);
X  for (i = 0, j = 0; i < cards; i++, j = (j + 1) % nstacks)
X    push_card(deck[i], j);
X}
X
X/* Procedures used in the display begin with "show_". */
X  
Xvoid show_suit(suit)
X  int suit;
X{
X  switch (suit) {
X  case 0: addch(club); break;
X  case 1: addch(diamond); break;
X  case 2: addch(heart); break;
X  case 3: addch(spade); break;
X  default: addch('?'); break;
X  }
X}
X
Xvoid show_rank(rank)
X     int rank;
X{
X  switch (rank) {
X  case 0: addch('-'); break;
X  case 1: addch('A'); break;
X  case 2:
X  case 3:
X  case 4:
X  case 5:
X  case 6:
X  case 7:
X  case 8: 
X  case 9: addch(rank+'0'); break;
X  case 10: addch('T'); break;
X  case 11: addch('J'); break;
X  case 12: addch('Q'); break;
X  case 13: addch('K'); break;
X  default: addch('?');
X  }
X}
X
Xvoid show_card(card)
X     card_t card;
X{
X  show_suit(card2suit(card));
X  show_rank(card2rank(card));
X}
X
X/* Board display routines. */
X#define prompt_height 1
X#define status_height 1
X#define command_height 2
X#define board_height 19
X#define title_height 1
X
X#define stack_indent 12
X#define card_size 6
X
Xint prompt_row, status_row, command_row, board_row, title_row;
X
Xvoid init_show()
X{
X  prompt_row = LINES - prompt_height;
X  status_row = prompt_row - status_height;
X  command_row = status_row - command_height;
X  board_row = command_row - board_height;
X  title_row = board_row - title_height;
X}
X  
Xvoid clear_status()
X{
X  move(status_row, stack_indent);
X  clrtoeol();
X}
X
Xvoid clear_prompt()
X{
X  move(prompt_row, stack_indent);
X  clrtoeol();
X}
X
Xvoid goto_stack_top(p, h)
X     int p, h;
X{
X  move(command_row - h,
X       stack_indent + card_size * (p + 1));
X}
X
Xvoid goto_foundation(s)
X     int s;
X{
X  goto_stack_top(-1, 2 * (s + 1));
X}
X
Xvoid erase_top_of_stack(p)
X     int p;
X{
X  int len = length(board.stack[p]);
X  goto_stack_top(p, len);
X  addstr("  ");
X}
X
Xvoid show_top_of_stack(p)
X     int p;
X{
X  int len = length(board.stack[p]);
X  goto_stack_top(p, len);
X  show_card(top_card(p));
X}
X
Xvoid show_foundation(s)
X     int s;
X{
X  goto_foundation(s);
X  show_card(foundation_ref(s));
X}
X
Xvoid show_board()
X{
X  int i; int len; card_list_t list;
X  for (i = 0; i < nsuits; i++) show_foundation(i);
X  for (i = 0; i < nstacks; i++) {
X    list = board.stack[i];
X    len = length(list);
X    for (; list != NIL; list = list->rest, len--) {
X      goto_stack_top(i, len);
X      show_card(list->card);
X    }
X  }
X}
X
Xint show_game()
X{
X  int i;
X  clear();
X  move(title_row, stack_indent);
X  addstr("Streets and Alleys");
X  show_board();
X  move(command_row, 0);
X  addstr("Commands:");
X  for (i = -1; i < nstacks; i++) {
X    goto_stack_top(i, 0);
X    addch(i+'1');
X    addch(',');
X  }
X  goto_stack_top(8, 0);
X  addstr("q, r, or ?.");
X  move(status_row, 0);
X  addstr("Status:");
X  clear_status();
X  addstr("Fresh display.  Type ? for help.");
X  move(prompt_row, 0);
X  addstr("Prompt:");
X  return 0;
X}
X
Xchar *help[] = {
X  "\tStreets and Alleys version 1.1\n\n",
X  "There are eight stacks of cards and a foundation for each suit.  A\n",
X  "card may be moved from the top of a stack to its foundation or to\n",
X  "the top of another stack.  The object of the game is to order the\n",
X  "cards in each stack so that each card is covered only by cards of\n",
X  "lesser rank. The ace has the smallest rank and the king has the\n",
X  "greatest rank.\n",
X  "\n",
X  "A card may be moved to its foundation when the card's predecessor of\n",
X  "the same suit is there.  A card may be moved to a stack when the top\n",
X  "card of the stack has rank one greater than the card being moved.  A\n",
X  "card can always be moved to an empty stack.\n",
X  "\n",
X  "Commands:\n",
X  "\n",
X  "  0\tSelect a foundation.\n",
X  "  1-8\tSelect a stack.\n",
X  "  q\tQuit the game.\n",
X  "  r\tRefresh the display.\n",
X  "  ?\tPrint this help.\n",
X};
X  
Xint show_help()
X{
X  char **h;
X  clear();
X  for (h = help; h < help + sizeof(help)/sizeof(char *); h++)
X    addstr(*h);
X  move(prompt_row, 0);
X  addstr("Type any character to continue. ");
X  refresh();
X  (void) getch();
X  return show_game();
X}
X
Xint is_stack_done(p)
X     int p;
X{
X  card_list_t list;
X  if (is_nil(p)) return 1;
X  for (list = board.stack[p]; list->rest != NIL; list = list->rest) 
X    if (card2rank(list->card) >= card2rank(list->rest->card)) return 0;
X  return 1;
X}
X
Xint is_done()			/* The game is done when all cards */
X{				/* have been moved to the foundation, */
X  int i;			/* or every stack is ordered by rank. */
X  for (i = 0; i < nstacks; i++)
X    if (!is_stack_done(i)) return 0;
X  return 1;
X}
X
X/* Procedures that read input and move cards. */
X
Xint move_to_foundation(from)
X     int from;
X{
X  card_t c = top_card(from);
X  int to = card2suit(c);
X  if (c == nsuits + foundation_ref(to)) {
X    erase_top_of_stack(from);
X    pop_card(from);
X    foundation_set(to, c);
X    show_foundation(to);
X    clear_status();
X    addstr("The ");
X    show_card(c);
X    addstr(" was");
X  }
X  else {
X    clear_status();
X    addstr("The ");
X    show_card(c);
X    addstr(" cannot be");
X  }
X  addstr(" moved to the foundation.");
X  return 0;
X}
X
Xint move_to_stack(from, to)
X     int from, to;
X{
X  card_t c = top_card(from);
X  if (is_nil(to) || card2rank(top_card(to)) == 1 + card2rank(c)) {
X    erase_top_of_stack(from);
X    pop_card(from);
X    push_card(c, to);
X    show_top_of_stack(to);
X    clear_status();
X    addstr("Moved the ");
X    show_card(c);
X  }
X  else {
X    clear_status();
X    addstr("The ");
X    show_card(c);
X    addstr(" cannot be moved");
X  }
X  addstr(" from stack ");
X  addch(from+'1');
X  addstr(" to stack ");
X  addch(to+'1');
X  addch('.');
X  return 0;
X}
X
Xint get_other_move(from)
X     int from;
X{
X  int to;
X  clear_prompt();
X  addstr("Move ");
X  show_card(top_card(from));
X  addstr(" from stack ");
X  addch(from+'1');
X  addstr(" to ");
X  refresh();
X  to = getch();
X  if (to == EOF || to == 'q') return 1;
X  if (to == 'r') return show_game();
X  if (to == '?') return show_help();
X  to -= '1';
X  clear_status();
X  if (to < -1 || to >= 8) {
X    addstr("Bad input.  Type ? for help.");
X    return 0;
X  }
X  if (to == -1) return move_to_foundation(from);
X  else return move_to_stack(from,to);
X}
X
Xint get_move()
X{
X  int from;
X  clear_prompt();
X  addstr("Move from stack ");
X  refresh();
X  from = getch();
X  if (from == EOF || from == 'q') return 1;
X  if (from == 'r') return show_game();
X  if (from == '?') return show_help();
X  from -= '1';
X  if (from < 0 || from >= 8) {
X    clear_status();
X    addstr("Bad input.  Type ? for help.");
X    return 0;
X  }
X  if (is_nil(from)) {
X    clear_status();
X    addstr("There is no card in stack ");
X    addch(from+'1');
X    addch('.');
X    return 0;
X  }
X  return get_other_move(from);
X}
X
Xint play_one_game ()
X{
X  deal();
X  (void) show_game();
X  while (1)
X    if (is_done()) return 0;
X    else if (get_move()) return 1;
X}      
X
Xvoid play()
X{
X  int ch, status;
X  init_show();
X  while (1) {
X    status = play_one_game();
X    clear_status();
X    addstr(status == 0 ? "You won!" : "You lose.");
X    clear_prompt();
X    addstr("Press space to play again. ");
X    refresh();
X    ch = getch();
X    if (ch != ' ') return;
X  }
X}
X
Xint usage()
X{
X  char **h;
X  for (h = help; h < help + sizeof(help)/sizeof(char *); h++)
X    fputs(*h, stderr);
X  fprintf(stderr, "\nUsage:\t%s [number_of_ranks].\n", program_name);
X  fprintf(stderr, "The number of ranks must be between %d and %d.\n",
X	  minranks, nranks);
X  return 1;
X}
X  
Xint main(argc, argv)
X     int argc;
X     char *argv[];
X{
X  program_name = argv[0];
X  if (argc > 2) usage();
X  if (argc == 2) {		/* Small game requested. */
X    cards = atoi(argv[1]);
X    if (cards < minranks || cards > nranks) return usage();
X    cards *= nsuits;
X  }
X  else cards = ncards;
X  if (ERR == initscr()) {
X    fprintf(stderr, "%s:\tCannot initialize screen.\n", program_name);
X    return 1;
X  }
X  cbreak();
X  noecho();
X  play();
X  endwin();
X  putchar('\n');
X  return 0;
X}
END_OF_FILE
if test 12201 -ne `wc -c <'saa.c'`; then
    echo shar: \"'saa.c'\" unpacked with wrong size!
fi
# end of 'saa.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0