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