1 /////////////////////////////////////////////////////////////////////////////
2 // Name: game.cpp
3 // Purpose: Forty Thieves patience game
4 // Author: Chris Breeze
5 // Modified by:
6 // Created: 21/07/97
7 // Copyright: (c) 1993-1998 Chris Breeze
8 // Licence: wxWindows licence
9 //---------------------------------------------------------------------------
10 // Last modified: 22nd July 1998 - ported to wxWidgets 2.0
11 /////////////////////////////////////////////////////////////////////////////
12
13 // For compilers that support precompilation, includes "wx/wx.h".
14 #include "wx/wxprec.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/wx.h"
18 #endif
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <string.h>
24 #include "forty.h"
25 #include "game.h"
26
Game(int wins,int games,int score)27 Game::Game(int wins, int games, int score) :
28 m_inPlay(false),
29 m_moveIndex(0),
30 m_redoIndex(0),
31 m_bmap(0),
32 m_bmapCard(0)
33 {
34 int i;
35
36 m_pack = new Pack(2, 2 + 4 * (CardHeight + 2));
37 srand(time(0));
38
39 for (i = 0; i < 5; i++) m_pack->Shuffle();
40
41 m_discard = new Discard(2, 2 + 5 * (CardHeight + 2));
42
43 for (i = 0; i < 8; i++)
44 {
45 m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
46 2 + (i % 4) * (CardHeight + 2));
47 }
48
49 for (i = 0; i < 10; i++)
50 {
51 m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
52 }
53 Deal();
54 m_srcPile = 0;
55 m_liftedCard = 0;
56
57 // copy the input parameters for future reference
58 m_numWins = wins;
59 m_numGames = games;
60 m_totalScore = score;
61 m_currentScore = 0;
62 }
63
64
Layout()65 void Game::Layout()
66 {
67 int i;
68
69 m_pack->SetPos(2, 2 + 4 * (CardHeight + 2));
70
71 m_discard->SetPos(2, 2 + 5 * (CardHeight + 2));
72
73 for (i = 0; i < 8; i++)
74 {
75 m_foundations[i]->SetPos(2 + (i / 4) * (CardWidth + 2),
76 2 + (i % 4) * (CardHeight + 2));
77 }
78
79 for (i = 0; i < 10; i++)
80 {
81 m_bases[i]->SetPos(8 + (i + 2) * (CardWidth + 2), 2);
82 }
83 delete m_bmap;
84 delete m_bmapCard;
85 m_bmap = 0;
86 m_bmapCard = 0;
87 }
88
89 // Make sure we delete all objects created by the game object
~Game()90 Game::~Game()
91 {
92 int i;
93
94 delete m_pack;
95 delete m_discard;
96 for (i = 0; i < 8; i++)
97 {
98 delete m_foundations[i];
99 }
100 for (i = 0; i < 10; i++)
101 {
102 delete m_bases[i];
103 }
104 delete m_bmap;
105 delete m_bmapCard;
106 }
107
108 /*
109 Set the score for a new player.
110 NB: call Deal() first if the new player is to start
111 a new game
112 */
NewPlayer(int wins,int games,int score)113 void Game::NewPlayer(int wins, int games, int score)
114 {
115 m_numWins = wins;
116 m_numGames = games;
117 m_totalScore = score;
118 m_currentScore = 0;
119 }
120
121 // Undo the last move
Undo(wxDC & dc)122 void Game::Undo(wxDC& dc)
123 {
124 if (m_moveIndex > 0)
125 {
126 m_moveIndex--;
127 Card* card = m_moves[m_moveIndex].dest->RemoveTopCard(dc);
128 m_moves[m_moveIndex].src->AddCard(dc, card);
129 DisplayScore(dc);
130 }
131 }
132
133 // Redo the last move
Redo(wxDC & dc)134 void Game::Redo(wxDC& dc)
135 {
136 if (m_moveIndex < m_redoIndex)
137 {
138 Card* card = m_moves[m_moveIndex].src->RemoveTopCard(dc);
139 if (m_moves[m_moveIndex].src == m_pack)
140 {
141 m_pack->Redraw(dc);
142 card->TurnCard(faceup);
143 }
144 m_moves[m_moveIndex].dest->AddCard(dc, card);
145 DisplayScore(dc);
146 m_moveIndex++;
147 }
148 }
149
DoMove(wxDC & dc,Pile * src,Pile * dest)150 void Game::DoMove(wxDC& dc, Pile* src, Pile* dest)
151 {
152 if (m_moveIndex < MaxMoves)
153 {
154 if (src == dest)
155 {
156 wxMessageBox(wxT("Game::DoMove() src == dest"), wxT("Debug message"),
157 wxOK | wxICON_EXCLAMATION);
158 }
159 m_moves[m_moveIndex].src = src;
160 m_moves[m_moveIndex].dest = dest;
161 m_moveIndex++;
162
163 // when we do a move any moves in redo buffer are discarded
164 m_redoIndex = m_moveIndex;
165 }
166 else
167 {
168 wxMessageBox(wxT("Game::DoMove() Undo buffer full"), wxT("Debug message"),
169 wxOK | wxICON_EXCLAMATION);
170 }
171
172 if (!m_inPlay)
173 {
174 m_inPlay = true;
175 m_numGames++;
176 }
177 DisplayScore(dc);
178
179 if (HaveYouWon())
180 {
181 wxWindow *frame = wxTheApp->GetTopWindow();
182 wxWindow *canvas = (wxWindow *) NULL;
183
184 if (frame)
185 {
186 wxWindowList::compatibility_iterator node = frame->GetChildren().GetFirst();
187 if (node) canvas = (wxWindow*)node->GetData();
188 }
189
190 // This game is over
191 m_inPlay = false;
192
193 // Redraw the score box to update games won
194 DisplayScore(dc);
195
196 if (wxMessageBox(wxT("Do you wish to play again?"),
197 wxT("Well Done, You have won!"), wxYES_NO | wxICON_QUESTION) == wxYES)
198 {
199 Deal();
200 canvas->Refresh();
201 }
202 else
203 {
204 // user cancelled the dialog - exit the app
205 ((wxFrame*)canvas->GetParent())->Close(true);
206 }
207 }
208 }
209
210
DisplayScore(wxDC & dc)211 void Game::DisplayScore(wxDC& dc)
212 {
213 wxColour bgColour = FortyApp::BackgroundColour();
214 wxPen* pen = wxThePenList->FindOrCreatePen(bgColour);
215 dc.SetTextBackground(bgColour);
216 dc.SetTextForeground(FortyApp::TextColour());
217 dc.SetBrush(FortyApp::BackgroundBrush());
218 dc.SetPen(* pen);
219
220 // count the number of cards in foundations
221 m_currentScore = 0;
222 for (int i = 0; i < 8; i++)
223 {
224 m_currentScore += m_foundations[i]->GetNumCards();
225 }
226
227 int x, y;
228 m_pack->GetTopCardPos(x, y);
229 x += 12 * CardWidth - 105;
230
231 wxCoord w, h;
232 {
233 wxCoord width, height;
234 dc.GetTextExtent(wxT("Average score:m_x"), &width, &height);
235 w = width;
236 h = height;
237 }
238 dc.DrawRectangle(x + w, y, 20, 4 * h);
239
240 wxString str;
241 str.Printf(wxT("%d"), m_currentScore);
242 dc.DrawText(wxT("Score:"), x, y);
243 dc.DrawText(str, x + w, y);
244 y += h;
245
246 str.Printf(wxT("%d"), m_numGames);
247 dc.DrawText(wxT("Games played:"), x, y);
248 dc.DrawText(str, x + w, y);
249 y += h;
250
251 str.Printf(wxT("%d"), m_numWins);
252 dc.DrawText(wxT("Games won:"), x, y);
253 dc.DrawText(str, x + w, y);
254 y += h;
255
256 int average = 0;
257 if (m_numGames > 0)
258 {
259 average = (2 * (m_currentScore + m_totalScore) + m_numGames ) / (2 * m_numGames);
260 }
261 str.Printf(wxT("%d"), average);
262 dc.DrawText(wxT("Average score:"), x, y);
263 dc.DrawText(str, x + w, y);
264 }
265
266
267 // Shuffle the m_pack and deal the cards
Deal()268 void Game::Deal()
269 {
270 int i, j;
271 Card* card;
272
273 // Reset all the piles, the undo buffer and shuffle the m_pack
274 m_moveIndex = 0;
275 m_pack->ResetPile();
276 for (i = 0; i < 5; i++)
277 {
278 m_pack->Shuffle();
279 }
280 m_discard->ResetPile();
281 for (i = 0; i < 10; i++)
282 {
283 m_bases[i]->ResetPile();
284 }
285 for (i = 0; i < 8; i++)
286 {
287 m_foundations[i]->ResetPile();
288 }
289
290 // Deal the initial 40 cards onto the bases
291 for (i = 0; i < 10; i++)
292 {
293 for (j = 1; j <= 4; j++)
294 {
295 card = m_pack->RemoveTopCard();
296 card->TurnCard(faceup);
297 m_bases[i]->AddCard(card);
298 }
299 }
300
301 if (m_inPlay)
302 {
303 // player has started the game and then redealt
304 // and so we must add the score for this game to the total score
305 m_totalScore += m_currentScore;
306 }
307 m_currentScore = 0;
308 m_inPlay = false;
309 }
310
311
312 // Redraw the m_pack, discard pile, the bases and the foundations
Redraw(wxDC & dc)313 void Game::Redraw(wxDC& dc)
314 {
315 int i;
316 m_pack->Redraw(dc);
317 m_discard->Redraw(dc);
318 for (i = 0; i < 8; i++)
319 {
320 m_foundations[i]->Redraw(dc);
321 }
322 for (i = 0; i < 10; i++)
323 {
324 m_bases[i]->Redraw(dc);
325 }
326 DisplayScore(dc);
327
328 if (m_bmap == 0)
329 {
330 m_bmap = new wxBitmap(CardWidth, CardHeight);
331 m_bmapCard = new wxBitmap(CardWidth, CardHeight);
332
333 // Initialise the card bitmap to the background colour
334 wxMemoryDC memoryDC;
335 memoryDC.SelectObject(*m_bmapCard);
336 memoryDC.SetPen( *wxTRANSPARENT_PEN );
337 memoryDC.SetBrush(FortyApp::BackgroundBrush());
338 memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
339 memoryDC.SelectObject(*m_bmap);
340 memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
341 memoryDC.SelectObject(wxNullBitmap);
342 }
343 }
344
345
346 // Test to see if the point (x, y) is over the top card of one of the piles
347 // Returns pointer to the pile, or 0 if (x, y) is not over a pile
348 // or the pile is empty
WhichPile(int x,int y)349 Pile* Game::WhichPile(int x, int y)
350 {
351 if (m_pack->GetCard(x, y) &&
352 m_pack->GetCard(x, y) == m_pack->GetTopCard())
353 {
354 return m_pack;
355 }
356
357 if (m_discard->GetCard(x, y) &&
358 m_discard->GetCard(x, y) == m_discard->GetTopCard())
359 {
360 return m_discard;
361 }
362
363 int i;
364 for (i = 0; i < 8; i++)
365 {
366 if (m_foundations[i]->GetCard(x, y) &&
367 m_foundations[i]->GetCard(x, y) == m_foundations[i]->GetTopCard())
368 {
369 return m_foundations[i];
370 }
371 }
372
373 for (i = 0; i < 10; i++)
374 {
375 if (m_bases[i]->GetCard(x, y) &&
376 m_bases[i]->GetCard(x, y) == m_bases[i]->GetTopCard())
377 {
378 return m_bases[i];
379 }
380 }
381 return 0;
382 }
383
384
385 // Left button is pressed - if cursor is over the m_pack then deal a card
386 // otherwise if it is over a card pick it up ready to be dragged - see MouseMove()
LButtonDown(wxDC & dc,int x,int y)387 bool Game::LButtonDown(wxDC& dc, int x, int y)
388 {
389 m_srcPile = WhichPile(x, y);
390 if (m_srcPile == m_pack)
391 {
392 Card* card = m_pack->RemoveTopCard();
393 if (card)
394 {
395 m_pack->Redraw(dc);
396 card->TurnCard(faceup);
397 m_discard->AddCard(dc, card);
398 DoMove(dc, m_pack, m_discard);
399 }
400 m_srcPile = 0;
401 }
402 else if (m_srcPile)
403 {
404 m_srcPile->GetTopCardPos(m_xPos, m_yPos);
405 m_xOffset = m_xPos - x;
406 m_yOffset = m_yPos - y;
407
408 // Copy the area under the card
409 // Initialise the card bitmap to the background colour
410 {
411 wxMemoryDC memoryDC;
412 memoryDC.SelectObject(*m_bmap);
413 m_liftedCard = m_srcPile->RemoveTopCard(memoryDC, m_xPos, m_yPos);
414 }
415
416 // Draw the card in card bitmap ready for blitting onto
417 // the screen
418 {
419 wxMemoryDC memoryDC;
420 memoryDC.SelectObject(*m_bmapCard);
421 m_liftedCard->Draw(memoryDC, 0, 0);
422 }
423 }
424 return m_srcPile != 0;
425 }
426
427 // Called when the left button is double clicked
428 // If a card is under the pointer and it can move elsewhere then move it.
429 // Move onto a foundation as first choice, a populated base as second and
430 // an empty base as third choice.
431 // NB Cards in the m_pack cannot be moved in this way - they aren't in play
432 // yet
LButtonDblClk(wxDC & dc,int x,int y)433 void Game::LButtonDblClk(wxDC& dc, int x, int y)
434 {
435 Pile* pile = WhichPile(x, y);
436 if (!pile) return;
437
438 // Double click on m_pack is the same as left button down
439 if (pile == m_pack)
440 {
441 LButtonDown(dc, x, y);
442 }
443 else
444 {
445 Card* card = pile->GetTopCard();
446
447 if (card)
448 {
449 int i;
450
451 // if the card is an ace then try to place it next
452 // to an ace of the same suit
453 if (card->GetPipValue() == 1)
454 {
455 for(i = 0; i < 4; i++)
456 {
457 Card* m_topCard = m_foundations[i]->GetTopCard();
458 if ( m_topCard )
459 {
460 if (m_topCard->GetSuit() == card->GetSuit() &&
461 m_foundations[i + 4] != pile &&
462 m_foundations[i + 4]->GetTopCard() == 0)
463 {
464 pile->RemoveTopCard(dc);
465 m_foundations[i + 4]->AddCard(dc, card);
466 DoMove(dc, pile, m_foundations[i + 4]);
467 return;
468 }
469 }
470 }
471 }
472
473 // try to place the card on a foundation
474 for(i = 0; i < 8; i++)
475 {
476 if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
477 {
478 pile->RemoveTopCard(dc);
479 m_foundations[i]->AddCard(dc, card);
480 DoMove(dc, pile, m_foundations[i]);
481 return;
482 }
483 }
484 // try to place the card on a populated base
485 for(i = 0; i < 10; i++)
486 {
487 if (m_bases[i]->AcceptCard(card) &&
488 m_bases[i] != pile &&
489 m_bases[i]->GetTopCard())
490 {
491 pile->RemoveTopCard(dc);
492 m_bases[i]->AddCard(dc, card);
493 DoMove(dc, pile, m_bases[i]);
494 return;
495 }
496 }
497 // try to place the card on any base
498 for(i = 0; i < 10; i++)
499 {
500 if (m_bases[i]->AcceptCard(card) && m_bases[i] != pile)
501 {
502 pile->RemoveTopCard(dc);
503 m_bases[i]->AddCard(dc, card);
504 DoMove(dc, pile, m_bases[i]);
505 return;
506 }
507 }
508 }
509 }
510 }
511
512
513 // Test to see whether the game has been won:
514 // i.e. m_pack, discard and bases are empty
HaveYouWon()515 bool Game::HaveYouWon()
516 {
517 if (m_pack->GetTopCard()) return false;
518 if (m_discard->GetTopCard()) return false;
519 for(int i = 0; i < 10; i++)
520 {
521 if (m_bases[i]->GetTopCard()) return false;
522 }
523 m_numWins++;
524 m_totalScore += m_currentScore;
525 m_currentScore = 0;
526 return true;
527 }
528
529
530 // See whether the card under the cursor can be moved somewhere else
531 // Returns 'true' if it can be moved, 'false' otherwise
CanYouGo(int x,int y)532 bool Game::CanYouGo(int x, int y)
533 {
534 Pile* pile = WhichPile(x, y);
535 if (pile && pile != m_pack)
536 {
537 Card* card = pile->GetTopCard();
538
539 if (card)
540 {
541 int i;
542 for(i = 0; i < 8; i++)
543 {
544 if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
545 {
546 return true;
547 }
548 }
549 for(i = 0; i < 10; i++)
550 {
551 if (m_bases[i]->GetTopCard() &&
552 m_bases[i]->AcceptCard(card) &&
553 m_bases[i] != pile)
554 {
555 return true;
556 }
557 }
558 }
559 }
560 return false;
561 }
562
563
564 // Called when the left button is released after dragging a card
565 // Scan the piles to see if this card overlaps a pile and can be added
566 // to the pile. If the card overlaps more than one pile on which it can be placed
567 // then put it on the nearest pile.
LButtonUp(wxDC & dc,int x,int y)568 void Game::LButtonUp(wxDC& dc, int x, int y)
569 {
570 if (m_srcPile)
571 {
572 // work out the position of the dragged card
573 x += m_xOffset;
574 y += m_yOffset;
575
576 Pile* nearestPile = 0;
577 int distance = (CardHeight + CardWidth) * (CardHeight + CardWidth);
578
579 // find the nearest pile which will accept the card
580 int i;
581 for (i = 0; i < 8; i++)
582 {
583 if (DropCard(x, y, m_foundations[i], m_liftedCard))
584 {
585 if (m_foundations[i]->CalcDistance(x, y) < distance)
586 {
587 nearestPile = m_foundations[i];
588 distance = nearestPile->CalcDistance(x, y);
589 }
590 }
591 }
592 for (i = 0; i < 10; i++)
593 {
594 if (DropCard(x, y, m_bases[i], m_liftedCard))
595 {
596 if (m_bases[i]->CalcDistance(x, y) < distance)
597 {
598 nearestPile = m_bases[i];
599 distance = nearestPile->CalcDistance(x, y);
600 }
601 }
602 }
603
604 // Restore the area under the card
605 wxMemoryDC memoryDC;
606 memoryDC.SelectObject(*m_bmap);
607 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
608 &memoryDC, 0, 0, wxCOPY);
609
610 // Draw the card in its new position
611 if (nearestPile)
612 {
613 // Add to new pile
614 nearestPile->AddCard(dc, m_liftedCard);
615 if (nearestPile != m_srcPile)
616 {
617 DoMove(dc, m_srcPile, nearestPile);
618 }
619 }
620 else
621 {
622 // Return card to src pile
623 m_srcPile->AddCard(dc, m_liftedCard);
624 }
625 m_srcPile = 0;
626 m_liftedCard = 0;
627 }
628 }
629
630
631
632
DropCard(int x,int y,Pile * pile,Card * card)633 bool Game::DropCard(int x, int y, Pile* pile, Card* card)
634 {
635 bool retval = false;
636 if (pile->Overlap(x, y))
637 {
638 if (pile->AcceptCard(card))
639 {
640 retval = true;
641 }
642 }
643 return retval;
644 }
645
646
MouseMove(wxDC & dc,int mx,int my)647 void Game::MouseMove(wxDC& dc, int mx, int my)
648 {
649 if (m_liftedCard)
650 {
651 wxMemoryDC memoryDC;
652 memoryDC.SelectObject(*m_bmap);
653
654 int dx = mx + m_xOffset - m_xPos;
655 int dy = my + m_yOffset - m_yPos;
656
657 if (abs(dx) >= CardWidth || abs(dy) >= CardHeight)
658 {
659 // Restore the area under the card
660 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
661 &memoryDC, 0, 0, wxCOPY);
662
663 // Copy the area under the card in the new position
664 memoryDC.Blit(0, 0, CardWidth, CardHeight,
665 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
666 }
667 else if (dx >= 0)
668 {
669 // dx >= 0
670 dc.Blit(m_xPos, m_yPos, dx, CardHeight, &memoryDC, 0, 0, wxCOPY);
671 if (dy >= 0)
672 {
673 // dy >= 0
674 dc.Blit(m_xPos + dx, m_yPos, CardWidth - dx, dy, &memoryDC, dx, 0, wxCOPY);
675 memoryDC.Blit(0, 0, CardWidth - dx, CardHeight - dy,
676 &memoryDC, dx, dy, wxCOPY);
677 memoryDC.Blit(0, CardHeight - dy, CardWidth - dx, dy,
678 &dc, m_xPos + dx, m_yPos + CardHeight, wxCOPY);
679 }
680 else
681 {
682 // dy < 0
683 dc.Blit(m_xPos + dx, m_yPos + dy + CardHeight, CardWidth - dx, -dy,
684 &memoryDC, dx, CardHeight + dy, wxCOPY);
685 memoryDC.Blit(0, -dy, CardWidth - dx, CardHeight + dy,
686 &memoryDC, dx, 0, wxCOPY);
687 memoryDC.Blit(0, 0, CardWidth - dx, -dy,
688 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
689 }
690 memoryDC.Blit(CardWidth - dx, 0, dx, CardHeight,
691 &dc, m_xPos + CardWidth, m_yPos + dy, wxCOPY);
692 }
693 else
694 {
695 // dx < 0
696 dc.Blit(m_xPos + CardWidth + dx, m_yPos, -dx, CardHeight,
697 &memoryDC, CardWidth + dx, 0, wxCOPY);
698 if (dy >= 0)
699 {
700 dc.Blit(m_xPos, m_yPos, CardWidth + dx, dy, &memoryDC, 0, 0, wxCOPY);
701 memoryDC.Blit(-dx, 0, CardWidth + dx, CardHeight - dy,
702 &memoryDC, 0, dy, wxCOPY);
703 memoryDC.Blit(-dx, CardHeight - dy, CardWidth + dx, dy,
704 &dc, m_xPos, m_yPos + CardHeight, wxCOPY);
705 }
706 else
707 {
708 // dy < 0
709 dc.Blit(m_xPos, m_yPos + CardHeight + dy, CardWidth + dx, -dy,
710 &memoryDC, 0, CardHeight + dy, wxCOPY);
711 memoryDC.Blit(-dx, -dy, CardWidth + dx, CardHeight + dy,
712 &memoryDC, 0, 0, wxCOPY);
713 memoryDC.Blit(-dx, 0, CardWidth + dx, -dy,
714 &dc, m_xPos, m_yPos + dy, wxCOPY);
715 }
716 memoryDC.Blit(0, 0, -dx, CardHeight,
717 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
718 }
719 m_xPos += dx;
720 m_yPos += dy;
721
722 // draw the card in its new position
723 memoryDC.SelectObject(*m_bmapCard);
724 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
725 &memoryDC, 0, 0, wxCOPY);
726 }
727 }
728
729
730
731 //----------------------------------------------//
732 // The Pack class: holds the two decks of cards //
733 //----------------------------------------------//
Pack(int x,int y)734 Pack::Pack(int x, int y) : Pile(x, y, 0, 0)
735 {
736 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
737 {
738 m_cards[m_topCard] = new Card(1 + m_topCard / 2, facedown);
739 }
740 m_topCard = NumCards - 1;
741 }
742
743
Shuffle()744 void Pack::Shuffle()
745 {
746 Card* temp[NumCards];
747 int i;
748
749 // Don't try to shuffle an empty m_pack!
750 if (m_topCard < 0) return;
751
752 // Copy the cards into a temporary array. Start by clearing
753 // the array and then copy the card into a random position.
754 // If the position is occupied then find the next lower position.
755 for (i = 0; i <= m_topCard; i++)
756 {
757 temp[i] = 0;
758 }
759 for (i = 0; i <= m_topCard; i++)
760 {
761 int pos = rand() % (m_topCard + 1);
762 while (temp[pos])
763 {
764 pos--;
765 if (pos < 0) pos = m_topCard;
766 }
767 m_cards[i]->TurnCard(facedown);
768 temp[pos] = m_cards[i];
769 m_cards[i] = 0;
770 }
771
772 // Copy each card back into the m_pack in a random
773 // position. If position is occupied then find nearest
774 // unoccupied position after the random position.
775 for (i = 0; i <= m_topCard; i++)
776 {
777 int pos = rand() % (m_topCard + 1);
778 while (m_cards[pos])
779 {
780 pos++;
781 if (pos > m_topCard) pos = 0;
782 }
783 m_cards[pos] = temp[i];
784 }
785 }
786
Redraw(wxDC & dc)787 void Pack::Redraw(wxDC& dc)
788 {
789 Pile::Redraw(dc);
790
791 wxString str;
792 str.Printf(wxT("%d "), m_topCard + 1);
793
794 dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID );
795 dc.SetTextBackground(FortyApp::BackgroundColour());
796 dc.SetTextForeground(FortyApp::TextColour());
797 dc.DrawText(str, m_x + CardWidth + 5, m_y + CardHeight / 2);
798
799 }
800
AddCard(Card * card)801 void Pack::AddCard(Card* card)
802 {
803 if (card == m_cards[m_topCard + 1])
804 {
805 m_topCard++;
806 }
807 else
808 {
809 wxMessageBox(wxT("Pack::AddCard() Undo error"), wxT("Forty Thieves: Warning"),
810 wxOK | wxICON_EXCLAMATION);
811 }
812 card->TurnCard(facedown);
813 }
814
815
~Pack()816 Pack::~Pack()
817 {
818 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
819 {
820 delete m_cards[m_topCard];
821 }
822 }
823
824
825 //------------------------------------------------------//
826 // The Base class: holds the initial pile of four cards //
827 //------------------------------------------------------//
Base(int x,int y)828 Base::Base(int x, int y) : Pile(x, y, 0, 12)
829 {
830 m_topCard = -1;
831 }
832
833
AcceptCard(Card * card)834 bool Base::AcceptCard(Card* card)
835 {
836 bool retval = false;
837
838 if (m_topCard >= 0)
839 {
840 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
841 m_cards[m_topCard]->GetPipValue() - 1 == card->GetPipValue())
842 {
843 retval = true;
844 }
845 }
846 else
847 {
848 // pile is empty - ACCEPT
849 retval = true;
850 }
851 return retval;
852 }
853
854
855 //----------------------------------------------------------------//
856 // The Foundation class: holds the cards built up from the ace... //
857 //----------------------------------------------------------------//
Foundation(int x,int y)858 Foundation::Foundation(int x, int y) : Pile(x, y, 0, 0)
859 {
860 m_topCard = -1;
861 }
862
AcceptCard(Card * card)863 bool Foundation::AcceptCard(Card* card)
864 {
865 bool retval = false;
866
867 if (m_topCard >= 0)
868 {
869 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
870 m_cards[m_topCard]->GetPipValue() + 1 == card->GetPipValue())
871 {
872 retval = true;
873 }
874 }
875 else if (card->GetPipValue() == 1)
876 {
877 // It's an ace and the pile is empty - ACCEPT
878 retval = true;
879 }
880 return retval;
881 }
882
883
884 //----------------------------------------------------//
885 // The Discard class: holds cards dealt from the m_pack //
886 //----------------------------------------------------//
Discard(int x,int y)887 Discard::Discard(int x, int y) : Pile(x, y, 19, 0)
888 {
889 m_topCard = -1;
890 }
891
Redraw(wxDC & dc)892 void Discard::Redraw(wxDC& dc)
893 {
894 if (m_topCard >= 0)
895 {
896 if (m_dx == 0 && m_dy == 0)
897 {
898 m_cards[m_topCard]->Draw(dc, m_x, m_y);
899 }
900 else
901 {
902 int x = m_x;
903 int y = m_y;
904 for (int i = 0; i <= m_topCard; i++)
905 {
906 m_cards[i]->Draw(dc, x, y);
907 x += m_dx;
908 y += m_dy;
909 if (i == 31)
910 {
911 x = m_x;
912 y = m_y + CardHeight / 3;
913 }
914 }
915 }
916 }
917 else
918 {
919 Card::DrawNullCard(dc, m_x, m_y);
920 }
921 }
922
923
GetTopCardPos(int & x,int & y)924 void Discard::GetTopCardPos(int& x, int& y)
925 {
926 if (m_topCard < 0)
927 {
928 x = m_x;
929 y = m_y;
930 }
931 else if (m_topCard > 31)
932 {
933 x = m_x + m_dx * (m_topCard - 32);
934 y = m_y + CardHeight / 3;
935 }
936 else
937 {
938 x = m_x + m_dx * m_topCard;
939 y = m_y;
940 }
941 }
942
943
RemoveTopCard(wxDC & dc,int m_xOffset,int m_yOffset)944 Card* Discard::RemoveTopCard(wxDC& dc, int m_xOffset, int m_yOffset)
945 {
946 Card* card;
947
948 if (m_topCard <= 31)
949 {
950 card = Pile::RemoveTopCard(dc, m_xOffset, m_yOffset);
951 }
952 else
953 {
954 int topX, topY, x, y;
955 GetTopCardPos(topX, topY);
956 card = Pile::RemoveTopCard();
957 card->Erase(dc, topX - m_xOffset, topY - m_yOffset);
958 GetTopCardPos(x, y);
959 dc.SetClippingRegion(topX - m_xOffset, topY - m_yOffset,
960 CardWidth, CardHeight);
961
962 for (int i = m_topCard - 31; i <= m_topCard - 31 + CardWidth / m_dx; i++)
963 {
964 m_cards[i]->Draw(dc, m_x - m_xOffset + i * m_dx, m_y - m_yOffset);
965 }
966 if (m_topCard > 31)
967 {
968 m_cards[m_topCard]->Draw(dc, topX - m_xOffset - m_dx, topY - m_yOffset);
969 }
970 dc.DestroyClippingRegion();
971 }
972
973 return card;
974 }
975