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