Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

455 lines
14 KiB

  1. /****************************************************************************
  2. Game.c
  3. June 91, JimH initial code
  4. Oct 91, JimH port to Win32
  5. Routines for playing the game are here and in game2.c
  6. ****************************************************************************/
  7. #include "freecell.h"
  8. #include "freecons.h"
  9. #include <assert.h>
  10. #include <memory.h>
  11. /****************************************************************************
  12. SetFromLoc
  13. User clicks to select card to transfer FROM.
  14. ****************************************************************************/
  15. VOID SetFromLoc(HWND hWnd, UINT x, UINT y)
  16. {
  17. HDC hDC;
  18. UINT col, pos;
  19. wFromCol = EMPTY; // assume we didn't find a card
  20. wFromPos = EMPTY;
  21. cUndo = 0;
  22. EnableMenuItem(GetMenu(hWnd), IDM_UNDO, MF_GRAYED);
  23. if (Point2Card(x, y, &col, &pos))
  24. {
  25. if (col == TOPROW)
  26. {
  27. if (card[TOPROW][pos] == EMPTY || pos > 3)
  28. return;
  29. }
  30. else
  31. {
  32. pos = FindLastPos(col);
  33. if (pos == EMPTY) // empty column
  34. return;
  35. }
  36. }
  37. else
  38. return;
  39. wFromCol = col; // ok, we have a card
  40. wFromPos = pos;
  41. wMouseMode = TO;
  42. hDC = GetDC(hWnd);
  43. DrawCard(hDC, col, pos, card[col][pos], HILITE);
  44. if (col == TOPROW && pos < 4)
  45. DrawKing(hDC, LEFT, TRUE);
  46. ReleaseDC(hWnd, hDC);
  47. }
  48. /****************************************************************************
  49. ProcessMoveRequest
  50. After user has selected a FROM card with SetFromLoc() above, he then
  51. chooses a TO location with the mouse which this function processes.
  52. Note that the layout of the cards (the card array) is copied into
  53. an array called shadow. This is so queued move requests can be determined
  54. before we commit to moving the cards.
  55. ****************************************************************************/
  56. VOID ProcessMoveRequest(HWND hWnd, UINT x, UINT y)
  57. {
  58. UINT tcol, tpos; // location to move selected card TO
  59. UINT freecells; // number of free cells unoccupied
  60. UINT trans; // number of cards requested transfer requires
  61. UINT maxtrans; // MaxTransfer() number
  62. INT i; // index
  63. TCHAR buffer[160]; // extra buffer needed for MessageBox
  64. assert(wFromCol != EMPTY);
  65. /* Make copy of card[][] in shadow[][] */
  66. memcpy(&(shadow[0][0]), &(card[0][0]), sizeof(card));
  67. Point2Card(x, y, &tcol, &tpos); // determine requested TO loc.
  68. if (tcol >= MAXCOL) // if illegal move selected...
  69. {
  70. tpos = wFromPos; // cancel it.
  71. tcol = wFromCol;
  72. }
  73. if (tcol == TOPROW) // if moving to top row
  74. {
  75. if (tpos > 7) // illegal move...
  76. {
  77. tpos = wFromPos; // so cancel it.
  78. tcol = wFromCol;
  79. }
  80. }
  81. else // if moving to a column...
  82. {
  83. tpos = FindLastPos(tcol); // find end of column
  84. if (tpos == EMPTY) // if column is empty...
  85. tpos = 0; // move to top of column.
  86. }
  87. /* if moving between non-empty columns */
  88. if (wFromCol != TOPROW && tcol != TOPROW && card[tcol][0] != EMPTY)
  89. {
  90. freecells = 0; // count free freecells
  91. for (i = 0; i < 4; i++)
  92. if (card[TOPROW][i] == EMPTY)
  93. freecells++;
  94. trans = NumberToTransfer(wFromCol, tcol); // how many required?
  95. DEBUGMSG(TEXT("trans is %u "), trans);
  96. DEBUGMSG(TEXT("and MaxTransfer() is %u\r\n"), MaxTransfer());
  97. if (trans > 0) // legal move?
  98. {
  99. maxtrans = MaxTransfer();
  100. if (trans <= maxtrans) // enough free cells?
  101. {
  102. MultiMove(wFromCol, tcol);
  103. }
  104. else if (bMessages)
  105. {
  106. LoadString(hInst, IDS_APPNAME, smallbuf, SMALL);
  107. LoadString(hInst, IDS_TOOFEWFREE, buffer, sizeof(buffer)/sizeof(TCHAR));
  108. wsprintf(bigbuf, buffer, trans, maxtrans);
  109. MessageBeep(MB_ICONINFORMATION);
  110. MessageBox(hWnd, bigbuf, smallbuf, MB_ICONINFORMATION);
  111. /* illegal move, so deselect that card */
  112. QueueTransfer(wFromCol, wFromPos, wFromCol, wFromPos);
  113. }
  114. else
  115. return;
  116. }
  117. else
  118. {
  119. if (bMessages)
  120. {
  121. LoadString(hInst, IDS_APPNAME, smallbuf, SMALL);
  122. LoadString(hInst, IDS_ILLEGAL, bigbuf, BIG);
  123. MessageBeep(MB_ICONINFORMATION);
  124. MessageBox(hWnd, bigbuf, smallbuf, MB_ICONINFORMATION);
  125. // deselect
  126. QueueTransfer(wFromCol, wFromPos, wFromCol, wFromPos);
  127. }
  128. else
  129. return;
  130. }
  131. }
  132. else // else move involves TOPROW or move to empty column
  133. {
  134. bMoveCol = 0;
  135. if (IsValidMove(hWnd, tcol, tpos))
  136. {
  137. if (bMoveCol) // user selected move column?
  138. MoveCol(wFromCol, tcol);
  139. else
  140. QueueTransfer(wFromCol, wFromPos, tcol, tpos);
  141. }
  142. else
  143. {
  144. if (bMessages && (bMoveCol != -1)) // user did not select Cancel
  145. {
  146. LoadString(hInst, IDS_APPNAME, smallbuf, SMALL);
  147. LoadString(hInst, IDS_ILLEGAL, bigbuf, BIG);
  148. MessageBeep(MB_ICONINFORMATION);
  149. MessageBox(hWnd, bigbuf, smallbuf, MB_ICONINFORMATION);
  150. // deselect
  151. QueueTransfer(wFromCol, wFromPos, wFromCol, wFromPos);
  152. }
  153. else if (bMoveCol == -1) // user selected cancel
  154. {
  155. // deselect
  156. QueueTransfer(wFromCol, wFromPos, wFromCol, wFromPos);
  157. }
  158. else
  159. return;
  160. }
  161. }
  162. Cleanup(); // queue transfers of unneeded cards
  163. MoveCards(hWnd); // start transferring queued cards
  164. wMouseMode = FROM; // next mouse click chooses FROM card
  165. }
  166. /****************************************************************************
  167. ProcessDoubleClick
  168. Double clicking on card in a column moves it to the first free cell found.
  169. After finding the free cell, this routine is identical to ProcessMoveRequest.
  170. ****************************************************************************/
  171. BOOL ProcessDoubleClick(HWND hWnd)
  172. {
  173. UINT freecell = EMPTY; // assume none found
  174. INT i; // counter
  175. for (i = 3; i >= 0; i--) // look for free cell;
  176. if (card[TOPROW][i] == EMPTY)
  177. freecell = (UINT) i;
  178. if (freecell == EMPTY) // if none found
  179. return FALSE;
  180. memcpy(&(shadow[0][0]), &(card[0][0]), sizeof(card));
  181. wFromPos = FindLastPos(wFromCol);
  182. QueueTransfer(wFromCol, wFromPos, TOPROW, freecell);
  183. Cleanup();
  184. MoveCards(hWnd);
  185. wMouseMode = FROM;
  186. return TRUE;
  187. }
  188. /****************************************************************************
  189. IsValidMove
  190. This function determines if moving from wFromCol,wFromPos to tcol,tpos
  191. is valid. It can assume wFromCol and tcol are not both non-empty columns.
  192. In other words, except for moves to empty columns, it is concerned with
  193. moving only one card.
  194. If the move is to an empty column, the user can select if he wants to move
  195. one card or as much of the column as possible.
  196. ****************************************************************************/
  197. BOOL IsValidMove(HWND hWnd, UINT tcol, UINT tpos)
  198. {
  199. CARD fcard, tcard; // card values (0 to 51)
  200. UINT trans; // num cards required for transfer
  201. UINT freecells; // num of unoccupied free cells
  202. UINT pos;
  203. DEBUGMSG(TEXT("IVM: tpos is %d\r\n"), tpos);
  204. assert (tpos < MAXPOS);
  205. bMoveCol = FALSE; // assume FALSE
  206. /* allow cancel move from top row. */
  207. if (wFromCol == TOPROW && tcol == TOPROW && wFromPos == tpos)
  208. return TRUE;
  209. fcard = card[wFromCol][wFromPos];
  210. tcard = card[tcol][tpos];
  211. /* transfer to empty column */
  212. if ((wFromCol != TOPROW) && (tcol != TOPROW) && (card[tcol][0] == EMPTY))
  213. {
  214. trans = NumberToTransfer(wFromCol, tcol); // how many required?
  215. freecells = 0;
  216. for (pos = 0; pos < 4; pos++) // count free cells
  217. if (card[TOPROW][pos] == EMPTY)
  218. freecells++;
  219. if (freecells == 0 && trans > 1) // no freecells anyway
  220. trans = 1;
  221. if (trans == 0) // if no valid move
  222. return FALSE;
  223. else if (trans == 1) // if only 1 card can move
  224. return TRUE;
  225. /* If multiple cards can move, user must disambiguate request. */
  226. bMoveCol = (BOOL) DialogBox(hInst, TEXT("MoveCol"), hWnd, MoveColDlg);
  227. if (bMoveCol == -1) // CANCEL chosen
  228. return FALSE;
  229. return TRUE;
  230. }
  231. if (tcol == TOPROW)
  232. {
  233. if (tpos < 4) // free cells
  234. {
  235. if (tcard == EMPTY)
  236. return TRUE;
  237. else
  238. return FALSE;
  239. }
  240. else // home cells
  241. {
  242. if (VALUE(fcard) == ACE && tcard == EMPTY) // ace on new pile
  243. return TRUE;
  244. else if (SUIT(fcard) == SUIT(tcard)) // same suit
  245. {
  246. if (VALUE(fcard) == (VALUE(tcard) + 1)) // next card
  247. return TRUE;
  248. else
  249. return FALSE;
  250. }
  251. return FALSE;
  252. }
  253. }
  254. else // tcol is not TOPROW
  255. {
  256. if (card[tcol][0] == EMPTY) // top of empty column always ok
  257. return TRUE;
  258. return FitsUnder(fcard, tcard);
  259. }
  260. }
  261. /****************************************************************************
  262. Cleanup
  263. This function checks if exposed cards (cards in home cells or bottoms
  264. of columns) are no longer needed (Useless.)
  265. When it finds cards it doesn't need, it queues them for transfer. It
  266. keeps looking until an entire pass finds no new useless cards.
  267. ****************************************************************************/
  268. VOID Cleanup()
  269. {
  270. UINT col, pos;
  271. UINT i; // counter
  272. CARD c;
  273. BOOL bMore = TRUE; // assume we need another pass
  274. while (bMore)
  275. {
  276. bMore = FALSE;
  277. for (pos = 0; pos < 4; pos++) // do TOPROW first
  278. {
  279. c = card[TOPROW][pos];
  280. if (Useless(c)) // if card is discardable
  281. {
  282. bMore = TRUE; // need another pass
  283. /* If this is the first card of this suit, we need to
  284. determine which home cell it can use. */
  285. if (homesuit[SUIT(c)] == EMPTY)
  286. {
  287. i = 4;
  288. while (card[TOPROW][i] != EMPTY)
  289. i++;
  290. homesuit[SUIT(c)] = i;
  291. }
  292. QueueTransfer(TOPROW, pos, TOPROW, homesuit[SUIT(c)]);
  293. }
  294. }
  295. for (col = 1; col <= 8; col++) // do columns next
  296. {
  297. pos = FindLastPos(col);
  298. if (pos != EMPTY)
  299. {
  300. c = card[col][pos];
  301. if (Useless(c))
  302. {
  303. bMore = TRUE;
  304. if (homesuit[SUIT(c)] == EMPTY)
  305. {
  306. i = 4;
  307. while (card[TOPROW][i] != EMPTY)
  308. i++;
  309. homesuit[SUIT(c)] = i;
  310. }
  311. QueueTransfer(col, pos, TOPROW, homesuit[SUIT(c)]);
  312. }
  313. }
  314. }
  315. }
  316. }
  317. /****************************************************************************
  318. Useless
  319. returns TRUE if a card can be safely discarded (no existing cards could
  320. possibly play on it.)
  321. Note that the lines identified with // *** are required in the 32 bit version
  322. since EMPTY == 0xFFFF is not longer equal to -1 as was the case in 16 bit
  323. ****************************************************************************/
  324. BOOL Useless(CARD c)
  325. {
  326. CARD limit; // top home card of this suit
  327. if (c == EMPTY)
  328. return FALSE; // no card to discard
  329. if (bCheating == CHEAT_WIN)
  330. return TRUE;
  331. if (VALUE(c) == ACE)
  332. return TRUE; // ACEs can always be cleaned up
  333. else if (VALUE(c) == DEUCE) // DEUCEs need only check if ACE is up
  334. {
  335. if (home[SUIT(c)] == ACE)
  336. return TRUE;
  337. else
  338. return FALSE;
  339. }
  340. else // else check both cards that can play on it
  341. {
  342. limit = VALUE(c) - 1;
  343. if (home[SUIT(c)] != limit) // is this the next card?
  344. return FALSE;
  345. if (COLOUR(c) == RED)
  346. {
  347. if (home[CLUB] == EMPTY || home[SPADE] == EMPTY) // ***
  348. return FALSE;
  349. else
  350. return (home[CLUB] >= limit && home[SPADE] >= limit);
  351. }
  352. else
  353. {
  354. if (home[DIAMOND] == EMPTY || home[HEART] == EMPTY) // ***
  355. return FALSE;
  356. else
  357. return (home[DIAMOND] >= limit && home[HEART] >= limit);
  358. }
  359. }
  360. }