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: game.cpp 44498 2007-02-13 23:20:12Z VZ $
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 
Game(int wins,int games,int score)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 
Layout()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
~Game()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 */
NewPlayer(int wins,int games,int score)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
Undo(wxDC & dc)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
Redo(wxDC & dc)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 
DoMove(wxDC & dc,Pile * src,Pile * dest)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 
DisplayScore(wxDC & dc)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
Deal()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
Redraw(wxDC & dc)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
WhichPile(int x,int y)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()
LButtonDown(wxDC & dc,int x,int y)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
LButtonDblClk(wxDC & dc,int x,int y)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
HaveYouWon()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
CanYouGo(int x,int y)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.
LButtonUp(wxDC & dc,int x,int y)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 
DropCard(int x,int y,Pile * pile,Card * card)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 
MouseMove(wxDC & dc,int mx,int my)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 //----------------------------------------------//
Pack(int x,int y)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 
Shuffle()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 
Redraw(wxDC & dc)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 
AddCard(Card * card)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 
~Pack()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 //------------------------------------------------------//
Base(int x,int y)833 Base::Base(int x, int y) : Pile(x, y, 0, 12)
834 {
835     m_topCard = -1;
836 }
837 
838 
AcceptCard(Card * card)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 //----------------------------------------------------------------//
Foundation(int x,int y)863 Foundation::Foundation(int x, int y) : Pile(x, y, 0, 0)
864 {
865     m_topCard = -1;
866 }
867 
AcceptCard(Card * card)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 //----------------------------------------------------//
Discard(int x,int y)892 Discard::Discard(int x, int y) : Pile(x, y, 19, 0)
893 {
894     m_topCard = -1;
895 }
896 
Redraw(wxDC & dc)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 
GetTopCardPos(int & x,int & y)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 
RemoveTopCard(wxDC & dc,int m_xOffset,int m_yOffset)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 }
980