[comp.sources.games] v02i031: Blue Moon, a solitaire/patience game

games-request@basser.UUCP (08/13/87)

Submitted by: Tim Lister <tal@basser.oz.AU>
Comp.sources.games: Volume 2, Issue 31
Archive-name: bluemoon

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Contents:  README blue.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
  This is an implementation of the patience game Blue Moon.

  It uses curses, but is otherwise written as simply as possible, and
should work under any Un*x variant.

  The details of the game are included in the game, and the instructions
are self evident.

  A graphics version is under preparation for the Sun, and may be released
soon.

  Save the file as:
			blue.c

  Compile with:
			cc -O blue.c -lcurses -ltermlib
@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - blue.c
sed 's/^@//' > "blue.c" <<'@//E*O*F blue.c//'
/*****************************************************************************
 *                                                                           *
 *                         B l u e   M o o n                                 *
 *                         =================                                 *
 *                                                                           *
 *                  A patience game by T.A.Lister                            *
 *                                                                           *
 *****************************************************************************/

/* #include <stdio.h>   made redundant by "curses". tal */

#include <signal.h>
#include <curses.h>

long time();

#define NOCARD		-1

#define HEARTS		0
#define SPADES		1
#define DIAMONDS	2
#define CLUBS		3

#define ACE		0
#define TWO		1
#define THREE		2
#define FOUR		3
#define FIVE		4
#define SIX		5
#define SEVEN		6
#define EIGHT		7
#define NINE		8
#define TEN		9
#define JACK		10
#define QUEEN		11
#define KING		12

#define SUIT_LENGTH	13

#define GRID_WIDTH	14	/*    13+1  */
#define GRID_LENGTH	56	/* 4*(13+1) */

#define PACK_SIZE	52

int deck_size=PACK_SIZE;	/* initial deck */
int deck[PACK_SIZE];

int grid[GRID_LENGTH];	/* card layout grid */
int freeptr[4];		/* free card space pointers */

int deal_number=0;

die()
{
	signal(SIGINT, SIG_IGN);
	mvcur(0, COLS-1, LINES-1, 0);
	endwin();
	exit(0);
}

init_vars()
{
	int i;

	deck_size=PACK_SIZE;
	for(i=0;i<PACK_SIZE;i++)	deck[i]=i;
	for(i=0;i<4;i++)		freeptr[i]=i * GRID_WIDTH;
}

instructions()
{
	char c;

	clear();
	printw("Welcome to Blue Moon - do you want instructions? (y/n) : ");
	refresh();
	if(((c=getchar())=='n')||(c=='N'))	return;
	clear();
	printw("  Blue Moon is a solitaire or patience game played with a full 52 card deck.\n");
	printw("  After shuffling the deck thoroughly, deal the entire deck out face up,\n");
	printw("into four rows of 13 cards.  Find the Aces and move them to the left end of the\n");
	printw("layout, so that each row begins with an Ace, reading Hearts, Spades, Diamonds\n");
	printw("and Clubs from top to bottom.\n");
	printw("  Now play may proceed.  You may move cards into the empty spaces, subject to\n");
	printw("the proviso that you may only move into any space the card that is exactly one\n");
	printw("higher in rank, and of the same suit, as the card to the left of the space.\n");
	printw("  As Aces are low, Kings are high, and may not have any card moved into a space\n");
	printw("to their right.  Thus spaces to the right of Kings are considered to be dead.\n");
	printw("  Your aim is to end up with the four rows reading Ace to King in sequence.\n");
	printw("  Since it is virtually impossible to get this out in one deal, this is not a\n");
	printw("requirement of the game.  Instead, at the end of the play, leaving those cards\n");
	printw("that are in sequence from an Ace alone, gather up the rest of the cards and\n");
	printw("reshuffle them.  Deal the shuffled cards face up after the ends of the partial\n");
	printw("sequences, leaving a card space after each sequence, so that each row reads\n");
	printw("'A partial sequence', 'A space', 'enough cards to make a row of 14',\n");
	printw("then proceed to play as before.  Repeat as often as necessary.  A moment's\n");
	printw("reflection will show you that this game cannot take more than 13 deals.\n");
	printw("  A good score is 1 to 3 deals, 4 to 7 is average, 8 or more is poor.\n");
	printw("\n.....oooooOOOOO press <SPACE> to continue OOOOOooooo.....");
	refresh();
	(void) getchar();
}

shuffle(size)
int size;
{
	int i,j,numswaps,swapnum,temp;

	numswaps=size*10;	/* an arbitrary figure */

	for (swapnum=0;swapnum<numswaps;swapnum++)
	{
		i=rand() % size;
		j=rand() % size;
		temp=deck[i];
		deck[i]=deck[j];
		deck[j]=temp;
	}
}

deal_cards()
{
	int ptr, card=0, value, csuit, crank, suit, aces[4];

	for (suit=HEARTS;suit<=CLUBS;suit++)
	{
		ptr=freeptr[suit];
		grid[ptr++]=NOCARD;	/* 1st card space is blank */
		while ((ptr % GRID_WIDTH) != 0)
		{
			value=deck[card++];
			crank=value % SUIT_LENGTH;
			csuit=value / SUIT_LENGTH;
			if (crank==ACE)
				aces[csuit]=ptr;
			grid[ptr++]=value;
		}
	}

	if (deal_number==1)	/* shift the aces down to the 1st column */
		for (suit=HEARTS;suit<=CLUBS;suit++)
		{
			grid[suit * GRID_WIDTH] = suit * SUIT_LENGTH;
			grid[aces[suit]]=NOCARD;
			freeptr[suit]=aces[suit];
		}
}

char suitch(suit)
int suit;
{
	switch (suit)
	{
		case HEARTS:	return 'h';
		case SPADES:	return 's';
		case DIAMONDS:	return 'd';
		case CLUBS:	return 'c';
		default:	return '?';
	}
}

printcard(value)
int value;
{
	int rank, suit;

	rank=value % SUIT_LENGTH;
	suit=value / SUIT_LENGTH;
	switch (rank)
	{
		case NOCARD:	printw(" [ ] ");
				break;

		case ACE:	printw("  A%c ",suitch(suit));
				break;

		default: /* TWO to TEN */
				printw(" %2d%c ",rank+1,suitch(suit));
				break;

		case JACK:	printw("  J%c ",suitch(suit));
				break;

		case QUEEN:	printw("  Q%c ",suitch(suit));
				break;

		case KING:	printw("  K%c ",suitch(suit));
				break;
	}
}

display_cards(deal)
int deal;
{
	int row, card;

	clear();
	printw("Blue Moon - by Tim Lister - Deal Number %d.\n\n",deal);
	for(row=HEARTS;row<=CLUBS;row++)
	{
		move(row+row+2, 0);
		for(card=0;card<GRID_WIDTH;card++)
			printcard(grid[row * GRID_WIDTH + card]);
	}
	refresh();
}

find(card)
int card;
{
	int i;

	if ((card<0)||(card>=PACK_SIZE))	return NOCARD;
	for(i=0;i<GRID_LENGTH;i++)
		if (grid[i]==card)		return i;
	return NOCARD;
}

movecard(src, dst)
int src, dst;
{
	grid[dst]=grid[src];
	grid[src]=NOCARD;

	move( (dst / GRID_WIDTH)*2+2, (dst % GRID_WIDTH)*5);
	printcard(grid[dst]);

	move( (src / GRID_WIDTH)*2+2, (src % GRID_WIDTH)*5);
	printcard(grid[src]);

	refresh();
}

play_game()
{
	int dead=0, i, j;
	char c;
	int select[4], card;

	while (dead<4)
	{
		dead=0;
		for (i=0;i<4;i++)
		{
			card=grid[freeptr[i]-1];

			if (	((card % SUIT_LENGTH)==KING)
				||
				(card==NOCARD)	)
				select[i]=NOCARD;
			else
				select[i]=find(card+1);

			if (select[i]==NOCARD)
				dead++;
		};

		if (dead<4)
		{
			for (i=0;i<4;i++)
			{
				move(12+i,0);
				printw("%c:  ",'a'+i);
				if (select[i]==NOCARD)
					printw("DEAD ");
				else
				{
					printcard(grid[select[i]]);
					move( (select[i] / GRID_WIDTH)*2+3,
					      (select[i] % GRID_WIDTH)*5);
					printw("  ^  ");
				}
			};

			move(18,0);
			printw("Select undead card to move (a..d), R to redraw, RUBOUT to quit : ");
			refresh();
			for (j=0;j<4;j++)
				if (select[j]!=NOCARD)
				{
					move( (select[j] / GRID_WIDTH)*2+3,
					      (select[j] % GRID_WIDTH)*5);
					printw("     ");
				}

			while ((((c=getchar())<'a')||(c>'d'))&&(c!='r')&&(c!='R'));
			if ((c=='r')||(c=='R'))
				wrefresh(curscr);
			else
			{
				i=c-'a';
				if (select[i]!=NOCARD)
				{
					movecard(select[i], freeptr[i]);
					freeptr[i]=select[i];
				}
			}
		}
	}
	for (i=0;i<4;i++)
	{
		move(12+i,0);
		printw("%c:  ",'a'+i);
		printw("DEAD ");
	}
	move(20,0);
	printw(".....oooooOOOOO Finished Deal %d - Press <SPACE> to Continue OOOOOooooo.....", deal_number);
	refresh();
	(void) getchar();
}

collect_discards()
{
	int row, col, cardno=0, finish, gridno;

	for (row=HEARTS;row<=CLUBS;row++)
	{
		finish=0;
		for (col=1;col<GRID_WIDTH;col++)
		{
			gridno=row * GRID_WIDTH + col;

			if ((grid[gridno]!=(grid[gridno-1]+1))&&(finish==0))
			{
				finish=1;
				freeptr[row]=gridno;
			};

			if ((finish!=0)&&(grid[gridno]!=NOCARD))
				deck[cardno++]=grid[gridno];
		}
	}
	return cardno;
}

game_finished(deal)
int deal;
{
	clear();
	printw("\n\n\nYou finished the game in %d deals.\nThis is ",deal);
	if (deal<4)
		printw("good.\n");
	else if (deal<8)
		printw("average.\n");
	else
		printw("poor.\n");
	refresh();
	die();
}

main()
{
	initscr();
	signal(SIGINT, die);
	crmode();
	noecho();
	scrollok(stdscr, FALSE);

	instructions();

	srand((int)time((long *)0));

	init_vars();

	do{
		deal_number++;
		shuffle(deck_size);
		deal_cards();
		display_cards(deal_number);
		play_game();
	}while	((deck_size=collect_discards()) != 0);

	game_finished(deal_number);
}
@//E*O*F blue.c//
chmod u=rw,g=r,o=r blue.c
 
exit 0