Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1 | /**************************************************************************** |
| 2 | * Copyright (c) 1998-2009,2013 Free Software Foundation, Inc. * |
| 3 | * * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a * |
| 5 | * copy of this software and associated documentation files (the * |
| 6 | * "Software"), to deal in the Software without restriction, including * |
| 7 | * without limitation the rights to use, copy, modify, merge, publish, * |
| 8 | * distribute, distribute with modifications, sublicense, and/or sell * |
| 9 | * copies of the Software, and to permit persons to whom the Software is * |
| 10 | * furnished to do so, subject to the following conditions: * |
| 11 | * * |
| 12 | * The above copyright notice and this permission notice shall be included * |
| 13 | * in all copies or substantial portions of the Software. * |
| 14 | * * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * |
| 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * |
| 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * |
| 18 | * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * |
| 19 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * |
| 20 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * |
| 21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * |
| 22 | * * |
| 23 | * Except as contained in this notice, the name(s) of the above copyright * |
| 24 | * holders shall not be used in advertising or otherwise to promote the * |
| 25 | * sale, use or other dealings in this Software without prior written * |
| 26 | * authorization. * |
| 27 | ****************************************************************************/ |
| 28 | /***************************************************************************** |
| 29 | * * |
| 30 | * B l u e M o o n * |
| 31 | * ================= * |
| 32 | * V2.2 * |
| 33 | * A patience game by T.A.Lister * |
| 34 | * Integral screen support by Eric S. Raymond * |
| 35 | * * |
| 36 | *****************************************************************************/ |
| 37 | |
| 38 | /* |
| 39 | * Compile this with the command `cc -O blue.c -lcurses -o blue'. For best |
| 40 | * results, use the ncurses(3) library. On non-Intel machines, SVr4 curses is |
| 41 | * just as good. |
| 42 | * |
| 43 | * $Id: blue.c,v 1.35 2013/04/27 19:46:53 tom Exp $ |
| 44 | */ |
| 45 | |
| 46 | #include <test.priv.h> |
| 47 | |
| 48 | #include <time.h> |
| 49 | |
| 50 | #define NOCARD (-1) |
| 51 | |
| 52 | #define ACE 0 |
| 53 | #define KING 12 |
| 54 | #define SUIT_LENGTH 13 |
| 55 | |
| 56 | #define HEARTS 0 |
| 57 | #define SPADES 1 |
| 58 | #define DIAMONDS 2 |
| 59 | #define CLUBS 3 |
| 60 | #define NSUITS 4 |
| 61 | |
| 62 | #define GRID_WIDTH 14 /* 13+1 */ |
| 63 | #define GRID_LENGTH 56 /* 4*(13+1) */ |
| 64 | #define PACK_SIZE 52 |
| 65 | |
| 66 | #define BASEROW 1 |
| 67 | #define PROMPTROW 11 |
| 68 | |
| 69 | #define RED_ON_WHITE 1 |
| 70 | #define BLACK_ON_WHITE 2 |
| 71 | #define BLUE_ON_WHITE 3 |
| 72 | |
| 73 | static void die(int onsig) GCC_NORETURN; |
| 74 | |
| 75 | static int deck_size = PACK_SIZE; /* initial deck */ |
| 76 | static int deck[PACK_SIZE]; |
| 77 | |
| 78 | static int grid[GRID_LENGTH]; /* card layout grid */ |
| 79 | static int freeptr[4]; /* free card space pointers */ |
| 80 | |
| 81 | static int deal_number = 0; |
| 82 | |
| 83 | static chtype ranks[SUIT_LENGTH][2] = |
| 84 | { |
| 85 | {' ', 'A'}, |
| 86 | {' ', '2'}, |
| 87 | {' ', '3'}, |
| 88 | {' ', '4'}, |
| 89 | {' ', '5'}, |
| 90 | {' ', '6'}, |
| 91 | {' ', '7'}, |
| 92 | {' ', '8'}, |
| 93 | {' ', '9'}, |
| 94 | {'1', '0'}, |
| 95 | {' ', 'J'}, |
| 96 | {' ', 'Q'}, |
| 97 | {' ', 'K'} |
| 98 | }; |
| 99 | |
| 100 | /* Please note, that this is a bad example. |
| 101 | Color values should not be or'ed in. This |
| 102 | only works, because the characters used here |
| 103 | are plain and have no color attribute themselves. */ |
| 104 | #ifdef COLOR_PAIR |
| 105 | #define OR_COLORS(value,pair) ((value) | COLOR_PAIR(pair)) |
| 106 | #else |
| 107 | #define OR_COLORS(value,pair) (value) |
| 108 | #endif |
| 109 | |
| 110 | #define PC_COLORS(value,pair) (OR_COLORS(value,pair) | A_ALTCHARSET) |
| 111 | |
| 112 | static chtype letters[4] = |
| 113 | { |
| 114 | OR_COLORS('h', RED_ON_WHITE), /* hearts */ |
| 115 | OR_COLORS('s', BLACK_ON_WHITE), /* spades */ |
| 116 | OR_COLORS('d', RED_ON_WHITE), /* diamonds */ |
| 117 | OR_COLORS('c', BLACK_ON_WHITE), /* clubs */ |
| 118 | }; |
| 119 | |
| 120 | #if defined(__i386__) && defined(A_ALTCHARSET) && HAVE_TIGETSTR |
| 121 | static chtype glyphs[] = |
| 122 | { |
| 123 | PC_COLORS('\003', RED_ON_WHITE), /* hearts */ |
| 124 | PC_COLORS('\006', BLACK_ON_WHITE), /* spades */ |
| 125 | PC_COLORS('\004', RED_ON_WHITE), /* diamonds */ |
| 126 | PC_COLORS('\005', BLACK_ON_WHITE), /* clubs */ |
| 127 | }; |
| 128 | #define USE_CP437 1 |
| 129 | #else |
| 130 | #define USE_CP437 0 |
| 131 | #endif /* __i386__ */ |
| 132 | |
| 133 | static chtype *suits = letters; /* this may change to glyphs below */ |
| 134 | |
| 135 | static void |
| 136 | die(int onsig) |
| 137 | { |
| 138 | (void) signal(onsig, SIG_IGN); |
| 139 | endwin(); |
| 140 | ExitProgram(EXIT_SUCCESS); |
| 141 | } |
| 142 | |
| 143 | static void |
| 144 | init_vars(void) |
| 145 | { |
| 146 | int i; |
| 147 | |
| 148 | deck_size = PACK_SIZE; |
| 149 | for (i = 0; i < PACK_SIZE; i++) |
| 150 | deck[i] = i; |
| 151 | for (i = 0; i < 4; i++) |
| 152 | freeptr[i] = i * GRID_WIDTH; |
| 153 | } |
| 154 | |
| 155 | static void |
| 156 | shuffle(int size) |
| 157 | { |
| 158 | int i, j, numswaps, swapnum, temp; |
| 159 | |
| 160 | numswaps = size * 10; /* an arbitrary figure */ |
| 161 | |
| 162 | for (swapnum = 0; swapnum < numswaps; swapnum++) { |
| 163 | i = rand() % size; |
| 164 | j = rand() % size; |
| 165 | temp = deck[i]; |
| 166 | deck[i] = deck[j]; |
| 167 | deck[j] = temp; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | static void |
| 172 | deal_cards(void) |
| 173 | { |
| 174 | int ptr, card = 0, value, csuit, crank, suit, aces[4]; |
| 175 | |
| 176 | memset(aces, 0, sizeof(aces)); |
| 177 | for (suit = HEARTS; suit <= CLUBS; suit++) { |
| 178 | ptr = freeptr[suit]; |
| 179 | grid[ptr++] = NOCARD; /* 1st card space is blank */ |
| 180 | while ((ptr % GRID_WIDTH) != 0) { |
| 181 | value = deck[card++]; |
| 182 | crank = value % SUIT_LENGTH; |
| 183 | csuit = value / SUIT_LENGTH; |
| 184 | if (crank == ACE) |
| 185 | aces[csuit] = ptr; |
| 186 | grid[ptr++] = value; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | if (deal_number == 1) /* shift the aces down to the 1st column */ |
| 191 | for (suit = HEARTS; suit <= CLUBS; suit++) { |
| 192 | grid[suit * GRID_WIDTH] = suit * SUIT_LENGTH; |
| 193 | grid[aces[suit]] = NOCARD; |
| 194 | freeptr[suit] = aces[suit]; |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | static void |
| 199 | printcard(int value) |
| 200 | { |
| 201 | (void) addch(' '); |
| 202 | if (value == NOCARD) |
| 203 | (void) addstr(" "); |
| 204 | else { |
| 205 | addch(ranks[value % SUIT_LENGTH][0] | (chtype) COLOR_PAIR(BLUE_ON_WHITE)); |
| 206 | addch(ranks[value % SUIT_LENGTH][1] | (chtype) COLOR_PAIR(BLUE_ON_WHITE)); |
| 207 | addch(suits[value / SUIT_LENGTH]); |
| 208 | } |
| 209 | (void) addch(' '); |
| 210 | } |
| 211 | |
| 212 | static void |
| 213 | display_cards(int deal) |
| 214 | { |
| 215 | int row, card; |
| 216 | |
| 217 | clear(); |
| 218 | (void) printw( |
| 219 | "Blue Moon 2.1 - by Tim Lister & Eric Raymond - Deal %d.\n", |
| 220 | deal); |
| 221 | for (row = HEARTS; row <= CLUBS; row++) { |
| 222 | move(BASEROW + row + row + 2, 1); |
| 223 | for (card = 0; card < GRID_WIDTH; card++) |
| 224 | printcard(grid[row * GRID_WIDTH + card]); |
| 225 | } |
| 226 | |
| 227 | move(PROMPTROW + 2, 0); |
| 228 | refresh(); |
| 229 | #define P(x) (void)printw("%s\n", x) |
| 230 | P(" This 52-card solitaire starts with the entire deck shuffled and dealt"); |
| 231 | P("out in four rows. The aces are then moved to the left end of the layout,"); |
| 232 | P("making 4 initial free spaces. You may move to a space only the card that"); |
| 233 | P("matches the left neighbor in suit, and is one greater in rank. Kings are"); |
| 234 | P("high, so no cards may be placed to their right (they create dead spaces)."); |
| 235 | P(" When no moves can be made, cards still out of sequence are reshuffled"); |
| 236 | P("and dealt face up after the ends of the partial sequences, leaving a card"); |
| 237 | P("space after each sequence, so that each row looks like a partial sequence"); |
| 238 | P("followed by a space, followed by enough cards to make a row of 14. "); |
| 239 | P(" A moment's reflection will show that this game cannot take more than 13"); |
| 240 | P("deals. A good score is 1-3 deals, 4-7 is average, 8 or more is poor. "); |
| 241 | #undef P |
| 242 | refresh(); |
| 243 | } |
| 244 | |
| 245 | static int |
| 246 | find(int card) |
| 247 | { |
| 248 | int i; |
| 249 | |
| 250 | if ((card < 0) || (card >= PACK_SIZE)) |
| 251 | return (NOCARD); |
| 252 | for (i = 0; i < GRID_LENGTH; i++) |
| 253 | if (grid[i] == card) |
| 254 | return i; |
| 255 | return (NOCARD); |
| 256 | } |
| 257 | |
| 258 | static void |
| 259 | movecard(int src, int dst) |
| 260 | { |
| 261 | grid[dst] = grid[src]; |
| 262 | grid[src] = NOCARD; |
| 263 | |
| 264 | move(BASEROW + (dst / GRID_WIDTH) * 2 + 2, (dst % GRID_WIDTH) * 5 + 1); |
| 265 | printcard(grid[dst]); |
| 266 | |
| 267 | move(BASEROW + (src / GRID_WIDTH) * 2 + 2, (src % GRID_WIDTH) * 5 + 1); |
| 268 | printcard(grid[src]); |
| 269 | |
| 270 | refresh(); |
| 271 | } |
| 272 | |
| 273 | static void |
| 274 | play_game(void) |
| 275 | { |
| 276 | int dead = 0, i, j; |
| 277 | char c; |
| 278 | int selection[4], card; |
| 279 | |
| 280 | while (dead < 4) { |
| 281 | dead = 0; |
| 282 | for (i = 0; i < 4; i++) { |
| 283 | card = grid[freeptr[i] - 1]; |
| 284 | |
| 285 | if (((card % SUIT_LENGTH) == KING) |
| 286 | || |
| 287 | (card == NOCARD)) |
| 288 | selection[i] = NOCARD; |
| 289 | else |
| 290 | selection[i] = find(card + 1); |
| 291 | |
| 292 | if (selection[i] == NOCARD) |
| 293 | dead++; |
| 294 | }; |
| 295 | |
| 296 | if (dead < 4) { |
| 297 | char live[NSUITS + 1], *lp = live; |
| 298 | |
| 299 | for (i = 0; i < 4; i++) { |
| 300 | if (selection[i] != NOCARD) { |
| 301 | move(BASEROW + (selection[i] / GRID_WIDTH) * 2 + 3, |
| 302 | (selection[i] % GRID_WIDTH) * 5); |
| 303 | (void) printw(" %c ", (*lp++ = (char) ('a' + i))); |
| 304 | } |
| 305 | }; |
| 306 | *lp = '\0'; |
| 307 | |
| 308 | if (strlen(live) == 1) { |
| 309 | move(PROMPTROW, 0); |
| 310 | (void) printw( |
| 311 | "Making forced moves... "); |
| 312 | refresh(); |
| 313 | (void) sleep(1); |
| 314 | c = live[0]; |
| 315 | } else { |
| 316 | char buf[BUFSIZ]; |
| 317 | |
| 318 | (void) sprintf(buf, |
| 319 | "Type [%s] to move, r to redraw, q or INTR to quit: ", |
| 320 | live); |
| 321 | |
| 322 | do { |
| 323 | move(PROMPTROW, 0); |
| 324 | (void) addstr(buf); |
| 325 | move(PROMPTROW, (int) strlen(buf)); |
| 326 | clrtoeol(); |
| 327 | (void) addch(' '); |
| 328 | } while |
| 329 | (((c = (char) getch()) < 'a' || c > 'd') |
| 330 | && (c != 'r') |
| 331 | && (c != 'q')); |
| 332 | } |
| 333 | |
| 334 | for (j = 0; j < 4; j++) |
| 335 | if (selection[j] != NOCARD) { |
| 336 | move(BASEROW + (selection[j] / GRID_WIDTH) * 2 + 3, |
| 337 | (selection[j] % GRID_WIDTH) * 5); |
| 338 | (void) printw(" "); |
| 339 | } |
| 340 | |
| 341 | if (c == 'r') |
| 342 | display_cards(deal_number); |
| 343 | else if (c == 'q') |
| 344 | die(SIGINT); |
| 345 | else { |
| 346 | i = c - 'a'; |
| 347 | if (selection[i] == NOCARD) |
| 348 | beep(); |
| 349 | else { |
| 350 | movecard(selection[i], freeptr[i]); |
| 351 | freeptr[i] = selection[i]; |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | move(PROMPTROW, 0); |
| 358 | (void) standout(); |
| 359 | (void) printw("Finished deal %d - type any character to continue...", deal_number); |
| 360 | (void) standend(); |
| 361 | (void) getch(); |
| 362 | } |
| 363 | |
| 364 | static int |
| 365 | collect_discards(void) |
| 366 | { |
| 367 | int row, col, cardno = 0, finish, gridno; |
| 368 | |
| 369 | for (row = HEARTS; row <= CLUBS; row++) { |
| 370 | finish = 0; |
| 371 | for (col = 1; col < GRID_WIDTH; col++) { |
| 372 | gridno = row * GRID_WIDTH + col; |
| 373 | |
| 374 | if ((grid[gridno] != (grid[gridno - 1] + 1)) && (finish == 0)) { |
| 375 | finish = 1; |
| 376 | freeptr[row] = gridno; |
| 377 | }; |
| 378 | |
| 379 | if ((finish != 0) && (grid[gridno] != NOCARD)) |
| 380 | deck[cardno++] = grid[gridno]; |
| 381 | } |
| 382 | } |
| 383 | return cardno; |
| 384 | } |
| 385 | |
| 386 | static void |
| 387 | game_finished(int deal) |
| 388 | { |
| 389 | clear(); |
| 390 | (void) printw("You finished the game in %d deals. This is ", deal); |
| 391 | (void) standout(); |
| 392 | if (deal < 2) |
| 393 | (void) addstr("excellent"); |
| 394 | else if (deal < 4) |
| 395 | (void) addstr("good"); |
| 396 | else if (deal < 8) |
| 397 | (void) addstr("average"); |
| 398 | else |
| 399 | (void) addstr("poor"); |
| 400 | (void) standend(); |
| 401 | (void) addstr(". "); |
| 402 | refresh(); |
| 403 | } |
| 404 | |
| 405 | int |
| 406 | main(int argc, char *argv[]) |
| 407 | { |
| 408 | CATCHALL(die); |
| 409 | |
| 410 | setlocale(LC_ALL, ""); |
| 411 | |
| 412 | initscr(); |
| 413 | |
| 414 | /* |
| 415 | * We use COLOR_GREEN because COLOR_BLACK is wired to the wrong thing. |
| 416 | */ |
| 417 | start_color(); |
| 418 | init_pair(RED_ON_WHITE, COLOR_RED, COLOR_WHITE); |
| 419 | init_pair(BLUE_ON_WHITE, COLOR_BLUE, COLOR_WHITE); |
| 420 | init_pair(BLACK_ON_WHITE, COLOR_BLACK, COLOR_WHITE); |
| 421 | |
| 422 | #ifndef COLOR_PAIR |
| 423 | letters[0] = OR_COLORS('h', RED_ON_WHITE); /* hearts */ |
| 424 | letters[1] = OR_COLORS('s', BLACK_ON_WHITE); /* spades */ |
| 425 | letters[2] = OR_COLORS('d', RED_ON_WHITE); /* diamonds */ |
| 426 | letters[3] = OR_COLORS('c', BLACK_ON_WHITE); /* clubs */ |
| 427 | #if USE_CP437 |
| 428 | glyphs[0] = PC_COLORS('\003', RED_ON_WHITE); /* hearts */ |
| 429 | glyphs[1] = PC_COLORS('\006', BLACK_ON_WHITE); /* spades */ |
| 430 | glyphs[2] = PC_COLORS('\004', RED_ON_WHITE); /* diamonds */ |
| 431 | glyphs[3] = PC_COLORS('\005', BLACK_ON_WHITE); /* clubs */ |
| 432 | #endif |
| 433 | #endif |
| 434 | |
| 435 | #if USE_CP437 |
| 436 | if (tigetstr("smpch")) |
| 437 | suits = glyphs; |
| 438 | #endif /* USE_CP437 */ |
| 439 | |
| 440 | cbreak(); |
| 441 | |
| 442 | if (argc == 2) |
| 443 | srand((unsigned) atoi(argv[1])); |
| 444 | else |
| 445 | srand((unsigned) time((time_t *) 0)); |
| 446 | |
| 447 | init_vars(); |
| 448 | |
| 449 | do { |
| 450 | deal_number++; |
| 451 | shuffle(deck_size); |
| 452 | deal_cards(); |
| 453 | display_cards(deal_number); |
| 454 | play_game(); |
| 455 | } |
| 456 | while |
| 457 | ((deck_size = collect_discards()) != 0); |
| 458 | |
| 459 | game_finished(deal_number); |
| 460 | |
| 461 | die(SIGINT); |
| 462 | /*NOTREACHED */ |
| 463 | } |
| 464 | |
| 465 | /* blue.c ends here */ |