blue.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /****************************************************************************
  2. * Copyright (c) 1998-2006,2008 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. * Compile this with the command `cc -O blue.c -lcurses -o blue'. For best
  39. * results, use the ncurses(3) library. On non-Intel machines, SVr4 curses is
  40. * just as good.
  41. *
  42. * $Id: blue.c,v 1.30 2008/08/03 18:20:27 tom Exp $
  43. */
  44. #include <test.priv.h>
  45. #include <time.h>
  46. #define NOCARD (-1)
  47. #define ACE 0
  48. #define KING 12
  49. #define SUIT_LENGTH 13
  50. #define HEARTS 0
  51. #define SPADES 1
  52. #define DIAMONDS 2
  53. #define CLUBS 3
  54. #define NSUITS 4
  55. #define GRID_WIDTH 14 /* 13+1 */
  56. #define GRID_LENGTH 56 /* 4*(13+1) */
  57. #define PACK_SIZE 52
  58. #define BASEROW 1
  59. #define PROMPTROW 11
  60. #define RED_ON_WHITE 1
  61. #define BLACK_ON_WHITE 2
  62. #define BLUE_ON_WHITE 3
  63. static RETSIGTYPE die(int onsig) GCC_NORETURN;
  64. static int deck_size = PACK_SIZE; /* initial deck */
  65. static int deck[PACK_SIZE];
  66. static int grid[GRID_LENGTH]; /* card layout grid */
  67. static int freeptr[4]; /* free card space pointers */
  68. static int deal_number = 0;
  69. static chtype ranks[SUIT_LENGTH][2] =
  70. {
  71. {' ', 'A'},
  72. {' ', '2'},
  73. {' ', '3'},
  74. {' ', '4'},
  75. {' ', '5'},
  76. {' ', '6'},
  77. {' ', '7'},
  78. {' ', '8'},
  79. {' ', '9'},
  80. {'1', '0'},
  81. {' ', 'J'},
  82. {' ', 'Q'},
  83. {' ', 'K'}
  84. };
  85. /* Please note, that this is a bad example.
  86. Color values should not be or'ed in. This
  87. only works, because the characters used here
  88. are plain and have no color attribute themselves. */
  89. #ifdef COLOR_PAIR
  90. #define OR_COLORS(value,pair) ((value) | COLOR_PAIR(pair))
  91. #else
  92. #define OR_COLORS(value,pair) (value)
  93. #endif
  94. #define PC_COLORS(value,pair) (OR_COLORS(value,pair) | A_ALTCHARSET)
  95. static chtype letters[4] =
  96. {
  97. OR_COLORS('h', RED_ON_WHITE), /* hearts */
  98. OR_COLORS('s', BLACK_ON_WHITE), /* spades */
  99. OR_COLORS('d', RED_ON_WHITE), /* diamonds */
  100. OR_COLORS('c', BLACK_ON_WHITE), /* clubs */
  101. };
  102. #if defined(__i386__) && defined(A_ALTCHARSET) && HAVE_TIGETSTR
  103. static chtype glyphs[] =
  104. {
  105. PC_COLORS('\003', RED_ON_WHITE), /* hearts */
  106. PC_COLORS('\006', BLACK_ON_WHITE), /* spades */
  107. PC_COLORS('\004', RED_ON_WHITE), /* diamonds */
  108. PC_COLORS('\005', BLACK_ON_WHITE), /* clubs */
  109. };
  110. #define USE_CP437 1
  111. #else
  112. #define USE_CP437 0
  113. #endif /* __i386__ */
  114. static chtype *suits = letters; /* this may change to glyphs below */
  115. static RETSIGTYPE
  116. die(int onsig)
  117. {
  118. (void) signal(onsig, SIG_IGN);
  119. endwin();
  120. ExitProgram(EXIT_SUCCESS);
  121. }
  122. static void
  123. init_vars(void)
  124. {
  125. int i;
  126. deck_size = PACK_SIZE;
  127. for (i = 0; i < PACK_SIZE; i++)
  128. deck[i] = i;
  129. for (i = 0; i < 4; i++)
  130. freeptr[i] = i * GRID_WIDTH;
  131. }
  132. static void
  133. shuffle(int size)
  134. {
  135. int i, j, numswaps, swapnum, temp;
  136. numswaps = size * 10; /* an arbitrary figure */
  137. for (swapnum = 0; swapnum < numswaps; swapnum++) {
  138. i = rand() % size;
  139. j = rand() % size;
  140. temp = deck[i];
  141. deck[i] = deck[j];
  142. deck[j] = temp;
  143. }
  144. }
  145. static void
  146. deal_cards(void)
  147. {
  148. int ptr, card = 0, value, csuit, crank, suit, aces[4];
  149. memset(aces, 0, sizeof(aces));
  150. for (suit = HEARTS; suit <= CLUBS; suit++) {
  151. ptr = freeptr[suit];
  152. grid[ptr++] = NOCARD; /* 1st card space is blank */
  153. while ((ptr % GRID_WIDTH) != 0) {
  154. value = deck[card++];
  155. crank = value % SUIT_LENGTH;
  156. csuit = value / SUIT_LENGTH;
  157. if (crank == ACE)
  158. aces[csuit] = ptr;
  159. grid[ptr++] = value;
  160. }
  161. }
  162. if (deal_number == 1) /* shift the aces down to the 1st column */
  163. for (suit = HEARTS; suit <= CLUBS; suit++) {
  164. grid[suit * GRID_WIDTH] = suit * SUIT_LENGTH;
  165. grid[aces[suit]] = NOCARD;
  166. freeptr[suit] = aces[suit];
  167. }
  168. }
  169. static void
  170. printcard(int value)
  171. {
  172. (void) addch(' ');
  173. if (value == NOCARD)
  174. (void) addstr(" ");
  175. else {
  176. addch(ranks[value % SUIT_LENGTH][0] | COLOR_PAIR(BLUE_ON_WHITE));
  177. addch(ranks[value % SUIT_LENGTH][1] | COLOR_PAIR(BLUE_ON_WHITE));
  178. addch(suits[value / SUIT_LENGTH]);
  179. }
  180. (void) addch(' ');
  181. }
  182. static void
  183. display_cards(int deal)
  184. {
  185. int row, card;
  186. clear();
  187. (void) printw(
  188. "Blue Moon 2.1 - by Tim Lister & Eric Raymond - Deal %d.\n",
  189. deal);
  190. for (row = HEARTS; row <= CLUBS; row++) {
  191. move(BASEROW + row + row + 2, 1);
  192. for (card = 0; card < GRID_WIDTH; card++)
  193. printcard(grid[row * GRID_WIDTH + card]);
  194. }
  195. move(PROMPTROW + 2, 0);
  196. refresh();
  197. #define P(x) (void)printw("%s\n", x)
  198. P(" This 52-card solitaire starts with the entire deck shuffled and dealt");
  199. P("out in four rows. The aces are then moved to the left end of the layout,");
  200. P("making 4 initial free spaces. You may move to a space only the card that");
  201. P("matches the left neighbor in suit, and is one greater in rank. Kings are");
  202. P("high, so no cards may be placed to their right (they create dead spaces).");
  203. P(" When no moves can be made, cards still out of sequence are reshuffled");
  204. P("and dealt face up after the ends of the partial sequences, leaving a card");
  205. P("space after each sequence, so that each row looks like a partial sequence");
  206. P("followed by a space, followed by enough cards to make a row of 14. ");
  207. P(" A moment's reflection will show that this game cannot take more than 13");
  208. P("deals. A good score is 1-3 deals, 4-7 is average, 8 or more is poor. ");
  209. #undef P
  210. refresh();
  211. }
  212. static int
  213. find(int card)
  214. {
  215. int i;
  216. if ((card < 0) || (card >= PACK_SIZE))
  217. return (NOCARD);
  218. for (i = 0; i < GRID_LENGTH; i++)
  219. if (grid[i] == card)
  220. return i;
  221. return (NOCARD);
  222. }
  223. static void
  224. movecard(int src, int dst)
  225. {
  226. grid[dst] = grid[src];
  227. grid[src] = NOCARD;
  228. move(BASEROW + (dst / GRID_WIDTH) * 2 + 2, (dst % GRID_WIDTH) * 5 + 1);
  229. printcard(grid[dst]);
  230. move(BASEROW + (src / GRID_WIDTH) * 2 + 2, (src % GRID_WIDTH) * 5 + 1);
  231. printcard(grid[src]);
  232. refresh();
  233. }
  234. static void
  235. play_game(void)
  236. {
  237. int dead = 0, i, j;
  238. char c;
  239. int selection[4], card;
  240. while (dead < 4) {
  241. dead = 0;
  242. for (i = 0; i < 4; i++) {
  243. card = grid[freeptr[i] - 1];
  244. if (((card % SUIT_LENGTH) == KING)
  245. ||
  246. (card == NOCARD))
  247. selection[i] = NOCARD;
  248. else
  249. selection[i] = find(card + 1);
  250. if (selection[i] == NOCARD)
  251. dead++;
  252. };
  253. if (dead < 4) {
  254. char live[NSUITS + 1], *lp = live;
  255. for (i = 0; i < 4; i++) {
  256. if (selection[i] != NOCARD) {
  257. move(BASEROW + (selection[i] / GRID_WIDTH) * 2 + 3,
  258. (selection[i] % GRID_WIDTH) * 5);
  259. (void) printw(" %c ", *lp++ = 'a' + i);
  260. }
  261. };
  262. *lp = '\0';
  263. if (strlen(live) == 1) {
  264. move(PROMPTROW, 0);
  265. (void) printw(
  266. "Making forced moves... ");
  267. refresh();
  268. (void) sleep(1);
  269. c = live[0];
  270. } else {
  271. char buf[BUFSIZ];
  272. (void) sprintf(buf,
  273. "Type [%s] to move, r to redraw, q or INTR to quit: ",
  274. live);
  275. do {
  276. move(PROMPTROW, 0);
  277. (void) addstr(buf);
  278. move(PROMPTROW, (int) strlen(buf));
  279. clrtoeol();
  280. (void) addch(' ');
  281. } while
  282. (((c = getch()) < 'a' || c > 'd') && (c != 'r') && (c != 'q'));
  283. }
  284. for (j = 0; j < 4; j++)
  285. if (selection[j] != NOCARD) {
  286. move(BASEROW + (selection[j] / GRID_WIDTH) * 2 + 3,
  287. (selection[j] % GRID_WIDTH) * 5);
  288. (void) printw(" ");
  289. }
  290. if (c == 'r')
  291. display_cards(deal_number);
  292. else if (c == 'q')
  293. die(SIGINT);
  294. else {
  295. i = c - 'a';
  296. if (selection[i] == NOCARD)
  297. beep();
  298. else {
  299. movecard(selection[i], freeptr[i]);
  300. freeptr[i] = selection[i];
  301. }
  302. }
  303. }
  304. }
  305. move(PROMPTROW, 0);
  306. standout();
  307. (void) printw("Finished deal %d - type any character to continue...", deal_number);
  308. standend();
  309. (void) getch();
  310. }
  311. static int
  312. collect_discards(void)
  313. {
  314. int row, col, cardno = 0, finish, gridno;
  315. for (row = HEARTS; row <= CLUBS; row++) {
  316. finish = 0;
  317. for (col = 1; col < GRID_WIDTH; col++) {
  318. gridno = row * GRID_WIDTH + col;
  319. if ((grid[gridno] != (grid[gridno - 1] + 1)) && (finish == 0)) {
  320. finish = 1;
  321. freeptr[row] = gridno;
  322. };
  323. if ((finish != 0) && (grid[gridno] != NOCARD))
  324. deck[cardno++] = grid[gridno];
  325. }
  326. }
  327. return cardno;
  328. }
  329. static void
  330. game_finished(int deal)
  331. {
  332. clear();
  333. (void) printw("You finished the game in %d deals. This is ", deal);
  334. standout();
  335. if (deal < 2)
  336. (void) addstr("excellent");
  337. else if (deal < 4)
  338. (void) addstr("good");
  339. else if (deal < 8)
  340. (void) addstr("average");
  341. else
  342. (void) addstr("poor");
  343. standend();
  344. (void) addstr(". ");
  345. refresh();
  346. }
  347. int
  348. main(int argc, char *argv[])
  349. {
  350. CATCHALL(die);
  351. setlocale(LC_ALL, "");
  352. initscr();
  353. /*
  354. * We use COLOR_GREEN because COLOR_BLACK is wired to the wrong thing.
  355. */
  356. start_color();
  357. init_pair(RED_ON_WHITE, COLOR_RED, COLOR_WHITE);
  358. init_pair(BLUE_ON_WHITE, COLOR_BLUE, COLOR_WHITE);
  359. init_pair(BLACK_ON_WHITE, COLOR_BLACK, COLOR_WHITE);
  360. #ifndef COLOR_PAIR
  361. letters[0] = OR_COLORS('h', RED_ON_WHITE); /* hearts */
  362. letters[1] = OR_COLORS('s', BLACK_ON_WHITE); /* spades */
  363. letters[2] = OR_COLORS('d', RED_ON_WHITE); /* diamonds */
  364. letters[3] = OR_COLORS('c', BLACK_ON_WHITE); /* clubs */
  365. #if USE_CP437
  366. glyphs[0] = PC_COLORS('\003', RED_ON_WHITE); /* hearts */
  367. glyphs[1] = PC_COLORS('\006', BLACK_ON_WHITE); /* spades */
  368. glyphs[2] = PC_COLORS('\004', RED_ON_WHITE); /* diamonds */
  369. glyphs[3] = PC_COLORS('\005', BLACK_ON_WHITE); /* clubs */
  370. #endif
  371. #endif
  372. #if USE_CP437
  373. if (tigetstr("smpch"))
  374. suits = glyphs;
  375. #endif /* USE_CP437 */
  376. cbreak();
  377. if (argc == 2)
  378. srand((unsigned) atoi(argv[1]));
  379. else
  380. srand((unsigned) time((time_t *) 0));
  381. init_vars();
  382. do {
  383. deal_number++;
  384. shuffle(deck_size);
  385. deal_cards();
  386. display_cards(deal_number);
  387. play_game();
  388. }
  389. while
  390. ((deck_size = collect_discards()) != 0);
  391. game_finished(deal_number);
  392. die(SIGINT);
  393. /*NOTREACHED */
  394. }
  395. /* blue.c ends here */