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