/***************************************************************************** * * * B l u e M o o n * * ================= * * A patience game by T.A.Lister * * Integral screen support by Eric S. Raymond * * * *****************************************************************************/ /* * Compile this with the command `cc -O bluemoon.c -lcurses -o bluemoon'. * For best results, use the open-source ncurses(3) library. On non-Intel * machines, SVr4 curses is just as good. * * Upon reading this code, Tim Lister wrote: > I have come to the conclusion that it looks like a C port of a game > I wrote many, many moons ago in GFA BASIC (one of the best versions of > BASIC written) for the Atari ST. I am not sure whether I gave a > copy of it to anyone and I am at a loss to explain how it > managed to find its way onto the Internet. Ah well, may you live > in interesting times as the Chinese curse goes... * * Debugging note: The command `x' causes the game to terminate and write * a line consisting of the token `blue' followed by the random-number * seed used for the game, followed by a list of the moves. */ #include #include #include #include #include #include #include #define NOCARD (-1) #define ACE 0 #define KING 12 #define SUIT_LENGTH 13 #define HEARTS 0 #define SPADES 1 #define DIAMONDS 2 #define CLUBS 3 #define NSUITS 4 #define GRID_WIDTH (SUIT_LENGTH + 1) #define GRID_LENGTH (NSUITS * GRID_WIDTH) #define PACK_SIZE 52 #define BASEROW 1 #define PROMPTROW 11 static int seed, nmove; static char journal[PACK_SIZE]; static int deck_size = PACK_SIZE; /* initial deck */ static int deck[PACK_SIZE]; static int grid[GRID_LENGTH]; /* card layout grid */ static int freeptr[4]; /* free card space pointers */ static int deal_number=0; static char *ranks[SUIT_LENGTH] = {" A"," 2"," 3"," 4"," 5"," 6"," 7"," 8"," 9","10"," J"," Q"," K"}; static chtype letters[] = { 'h' | COLOR_PAIR(COLOR_RED), /* hearts */ 's' | COLOR_PAIR(COLOR_GREEN), /* spades */ 'd' | COLOR_PAIR(COLOR_RED), /* diamonds */ 'c' | COLOR_PAIR(COLOR_GREEN), /* clubs */ }; #if defined(__i386__) && defined(A_PCCHARSET) static chtype glyphs[] = { '\003' | A_PCCHARSET | COLOR_PAIR(COLOR_RED), /* hearts */ '\006' | A_PCCHARSET | COLOR_PAIR(COLOR_GREEN), /* spades */ '\004' | A_PCCHARSET | COLOR_PAIR(COLOR_RED), /* diamonds */ '\005' | A_PCCHARSET | COLOR_PAIR(COLOR_GREEN), /* clubs */ }; #endif /* __i386__ && A_PCCHARSET */ static chtype *suits = letters; /* this may change to glyphs below */ static void die(onsig) int onsig; { signal(onsig, SIG_IGN); endwin(); if (seed) printf("bluemoon %d %s\n", seed, journal); exit(0); } static void 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; } static void shuffle(size) int size; { int i,j,numswaps,swapnum,temp; numswaps=size*10; /* an arbitrary figure */ for (swapnum=0;swapnum=PACK_SIZE)) return(NOCARD); for(i = 0; i < GRID_LENGTH; i++) if (grid[i] == card) return i; return(NOCARD); } static void movecard(src, dst) int src, dst; { grid[dst]=grid[src]; grid[src]=NOCARD; move( BASEROW + (dst / GRID_WIDTH)*2+2, (dst % GRID_WIDTH)*5 + 1); printcard(grid[dst]); move( BASEROW + (src / GRID_WIDTH)*2+2, (src % GRID_WIDTH)*5 + 1); printcard(grid[src]); refresh(); } static void play_game() { int dead=0, i, j; char c; int select[NSUITS], card; nmove = 0; memset(journal, '\0', sizeof(journal)); 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) { char live[NSUITS+1], *lp = live; for (i=0;i<4;i++) { if (select[i] != NOCARD) { move(BASEROW + (select[i] / GRID_WIDTH)*2+3, (select[i] % GRID_WIDTH)*5); (void)printw(" %c ", *lp++ = 'a' + i); } }; *lp = '\0'; if (strlen(live) == 1) { move(PROMPTROW,0); (void)printw( "Making forced moves... "); refresh(); (void) sleep(1); c = live[0]; } else { char buf[BUFSIZ]; (void)sprintf(buf, "Type [%s] to move, r to redraw, q or INTR to quit: ", live); do { move(PROMPTROW,0); (void) addstr(buf); move(PROMPTROW, strlen(buf)); clrtoeol(); (void) addch(' '); } while (!(c = getch()) || !strchr("abcdqrx", c)); } for (j = 0; j < 4; j++) if (select[j]!=NOCARD) { move(BASEROW + (select[j] / GRID_WIDTH)*2+3, (select[j] % GRID_WIDTH)*5); (void)printw(" "); } if (c == 'r') { clearok(stdscr, TRUE); display_cards(deal_number); } else if (c == 'x') die(0); else if (c == 'q') { seed = 0; die(0); } else { i = c-'a'; if (select[i] == NOCARD) beep(); else { movecard(select[i], freeptr[i]); freeptr[i]=select[i]; } } journal[nmove++] = c; } } move(PROMPTROW, 0); standout(); (void)printw("Finished deal %d - type any character to continue...", deal_number); standend(); (void) getch(); } static int collect_discards() { int row, col, cardno=0, finish, gridno; for (row=HEARTS;row<=CLUBS;row++) { finish=0; for (col=1;col