]>
git.saurik.com Git - wxWidgets.git/blob - demos/forty/game.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Forty Thieves patience game
4 // Author: Chris Breeze
8 // Copyright: (c) 1993-1998 Chris Breeze
9 // Licence: wxWindows licence
10 //---------------------------------------------------------------------------
11 // Last modified: 22nd July 1998 - ported to wxWindows 2.0
12 /////////////////////////////////////////////////////////////////////////////
15 #pragma implementation
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
37 Game::Game(int wins
, int games
, int score
) :
46 m_pack
= new Pack(2, 2 + 4 * (CardHeight
+ 2));
49 for (i
= 0; i
< 5; i
++) m_pack
->Shuffle();
51 m_discard
= new Discard(2, 2 + 5 * (CardHeight
+ 2));
53 for (i
= 0; i
< 8; i
++)
55 m_foundations
[i
] = new Foundation(2 + (i
/ 4) * (CardWidth
+ 2),
56 2 + (i
% 4) * (CardHeight
+ 2));
59 for (i
= 0; i
< 10; i
++)
61 m_bases
[i
] = new Base(8 + (i
+ 2) * (CardWidth
+ 2), 2);
67 // copy the input parameters for future reference
75 // Make sure we delete all objects created by the game object
82 for (i
= 0; i
< 8; i
++)
84 delete m_foundations
[i
];
86 for (i
= 0; i
< 10; i
++)
95 Set the score for a new player.
96 NB: call Deal() first if the new player is to start
99 void Game::NewPlayer(int wins
, int games
, int score
)
103 m_totalScore
= score
;
107 // Undo the last move
108 void Game::Undo(wxDC
& dc
)
113 Card
* card
= m_moves
[m_moveIndex
].dest
->RemoveTopCard(dc
);
114 m_moves
[m_moveIndex
].src
->AddCard(dc
, card
);
119 // Redo the last move
120 void Game::Redo(wxDC
& dc
)
122 if (m_moveIndex
< m_redoIndex
)
124 Card
* card
= m_moves
[m_moveIndex
].src
->RemoveTopCard(dc
);
125 if (m_moves
[m_moveIndex
].src
== m_pack
)
128 card
->TurnCard(faceup
);
130 m_moves
[m_moveIndex
].dest
->AddCard(dc
, card
);
136 void Game::DoMove(wxDC
& dc
, Pile
* src
, Pile
* dest
)
138 if (m_moveIndex
< MaxMoves
)
142 wxMessageBox("Game::DoMove() src == dest", "Debug message",
143 wxOK
| wxICON_EXCLAMATION
);
145 m_moves
[m_moveIndex
].src
= src
;
146 m_moves
[m_moveIndex
].dest
= dest
;
149 // when we do a move any moves in redo buffer are discarded
150 m_redoIndex
= m_moveIndex
;
154 wxMessageBox("Game::DoMove() Undo buffer full", "Debug message",
155 wxOK
| wxICON_EXCLAMATION
);
167 void Game::DisplayScore(wxDC
& dc
)
169 wxColour bgColour
= FortyApp::BackgroundColour();
170 wxPen
* pen
= wxThePenList
->FindOrCreatePen(bgColour
, 1, wxSOLID
);
171 dc
.SetTextBackground(bgColour
);
172 dc
.SetTextForeground(FortyApp::TextColour());
173 dc
.SetBrush(FortyApp::BackgroundBrush());
176 // count the number of cards in foundations
178 for (int i
= 0; i
< 8; i
++)
180 m_currentScore
+= m_foundations
[i
]->GetNumCards();
184 m_pack
->GetTopCardPos(x
, y
);
185 x
+= 12 * CardWidth
- 105;
190 dc
.GetTextExtent("Average score:m_x", &width
, &height
);
194 dc
.DrawRectangle(x
+ w
, y
, 20, 4 * h
);
197 sprintf(str
, "%d", m_currentScore
);
198 dc
.DrawText("Score:", x
, y
);
199 dc
.DrawText(str
, x
+ w
, y
);
202 sprintf(str
, "%d", m_numGames
);
203 dc
.DrawText("Games played:", x
, y
);
204 dc
.DrawText(str
, x
+ w
, y
);
207 sprintf(str
, "%d", m_numWins
);
208 dc
.DrawText("Games won:", x
, y
);
209 dc
.DrawText(str
, x
+ w
, y
);
215 average
= (2 * (m_currentScore
+ m_totalScore
) + m_numGames
) / (2 * m_numGames
);
217 sprintf(str
, "%d", average
);
218 dc
.DrawText("Average score:", x
, y
);
219 dc
.DrawText(str
, x
+ w
, y
);
223 // Shuffle the m_pack and deal the cards
229 // Reset all the piles, the undo buffer and shuffle the m_pack
232 for (i
= 0; i
< 5; i
++)
236 m_discard
->ResetPile();
237 for (i
= 0; i
< 10; i
++)
239 m_bases
[i
]->ResetPile();
241 for (i
= 0; i
< 8; i
++)
243 m_foundations
[i
]->ResetPile();
246 // Deal the initial 40 cards onto the bases
247 for (i
= 0; i
< 10; i
++)
249 for (j
= 1; j
<= 4; j
++)
251 card
= m_pack
->RemoveTopCard();
252 card
->TurnCard(faceup
);
253 m_bases
[i
]->AddCard(card
);
259 // player has started the game and then redealt
260 // and so we must add the score for this game to the total score
261 m_totalScore
+= m_currentScore
;
268 // Redraw the m_pack, discard pile, the bases and the foundations
269 void Game::Redraw(wxDC
& dc
)
273 m_discard
->Redraw(dc
);
274 for (i
= 0; i
< 8; i
++)
276 m_foundations
[i
]->Redraw(dc
);
278 for (i
= 0; i
< 10; i
++)
280 m_bases
[i
]->Redraw(dc
);
286 m_bmap
= new wxBitmap(CardWidth
, CardHeight
);
287 m_bmapCard
= new wxBitmap(CardWidth
, CardHeight
);
289 // Initialise the card bitmap to the background colour
291 memoryDC
.SelectObject(*m_bmapCard
);
292 memoryDC
.SetPen( *wxTRANSPARENT_PEN
);
293 memoryDC
.SetBrush(FortyApp::BackgroundBrush());
294 memoryDC
.DrawRectangle(0, 0, CardWidth
, CardHeight
);
295 memoryDC
.SelectObject(*m_bmap
);
296 memoryDC
.DrawRectangle(0, 0, CardWidth
, CardHeight
);
297 memoryDC
.SelectObject(wxNullBitmap
);
302 // Test to see if the point (x, y) is over the top card of one of the piles
303 // Returns pointer to the pile, or 0 if (x, y) is not over a pile
304 // or the pile is empty
305 Pile
* Game::WhichPile(int x
, int y
)
307 if (m_pack
->GetCard(x
, y
) &&
308 m_pack
->GetCard(x
, y
) == m_pack
->GetTopCard())
313 if (m_discard
->GetCard(x
, y
) &&
314 m_discard
->GetCard(x
, y
) == m_discard
->GetTopCard())
320 for (i
= 0; i
< 8; i
++)
322 if (m_foundations
[i
]->GetCard(x
, y
) &&
323 m_foundations
[i
]->GetCard(x
, y
) == m_foundations
[i
]->GetTopCard())
325 return m_foundations
[i
];
329 for (i
= 0; i
< 10; i
++)
331 if (m_bases
[i
]->GetCard(x
, y
) &&
332 m_bases
[i
]->GetCard(x
, y
) == m_bases
[i
]->GetTopCard())
341 // Left button is pressed - if cursor is over the m_pack then deal a card
342 // otherwise if it is over a card pick it up ready to be dragged - see MouseMove()
343 bool Game::LButtonDown(wxDC
& dc
, int x
, int y
)
345 m_srcPile
= WhichPile(x
, y
);
346 if (m_srcPile
== m_pack
)
348 Card
* card
= m_pack
->RemoveTopCard();
352 card
->TurnCard(faceup
);
353 m_discard
->AddCard(dc
, card
);
354 DoMove(dc
, m_pack
, m_discard
);
360 m_srcPile
->GetTopCardPos(m_xPos
, m_yPos
);
361 m_xOffset
= m_xPos
- x
;
362 m_yOffset
= m_yPos
- y
;
364 // Copy the area under the card
365 // Initialise the card bitmap to the background colour
368 memoryDC
.SelectObject(*m_bmap
);
369 m_liftedCard
= m_srcPile
->RemoveTopCard(memoryDC
, m_xPos
, m_yPos
);
372 // Draw the card in card bitmap ready for blitting onto
376 memoryDC
.SelectObject(*m_bmapCard
);
377 m_liftedCard
->Draw(memoryDC
, 0, 0);
380 return m_srcPile
!= 0;
383 // Called when the left button is double clicked
384 // If a card is under the pointer and it can move elsewhere then move it.
385 // Move onto a foundation as first choice, a populated base as second and
386 // an empty base as third choice.
387 // NB Cards in the m_pack cannot be moved in this way - they aren't in play
389 void Game::LButtonDblClk(wxDC
& dc
, int x
, int y
)
391 Pile
* pile
= WhichPile(x
, y
);
394 // Double click on m_pack is the same as left button down
397 LButtonDown(dc
, x
, y
);
401 Card
* card
= pile
->GetTopCard();
407 // if the card is an ace then try to place it next
408 // to an ace of the same suit
409 if (card
->GetPipValue() == 1)
411 for(i
= 0; i
< 4; i
++)
413 Card
* m_topCard
= m_foundations
[i
]->GetTopCard();
416 if (m_topCard
->GetSuit() == card
->GetSuit() &&
417 m_foundations
[i
+ 4] != pile
&&
418 m_foundations
[i
+ 4]->GetTopCard() == 0)
420 pile
->RemoveTopCard(dc
);
421 m_foundations
[i
+ 4]->AddCard(dc
, card
);
422 DoMove(dc
, pile
, m_foundations
[i
+ 4]);
429 // try to place the card on a foundation
430 for(i
= 0; i
< 8; i
++)
432 if (m_foundations
[i
]->AcceptCard(card
) && m_foundations
[i
] != pile
)
434 pile
->RemoveTopCard(dc
);
435 m_foundations
[i
]->AddCard(dc
, card
);
436 DoMove(dc
, pile
, m_foundations
[i
]);
440 // try to place the card on a populated base
441 for(i
= 0; i
< 10; i
++)
443 if (m_bases
[i
]->AcceptCard(card
) &&
444 m_bases
[i
] != pile
&&
445 m_bases
[i
]->GetTopCard())
447 pile
->RemoveTopCard(dc
);
448 m_bases
[i
]->AddCard(dc
, card
);
449 DoMove(dc
, pile
, m_bases
[i
]);
453 // try to place the card on any base
454 for(i
= 0; i
< 10; i
++)
456 if (m_bases
[i
]->AcceptCard(card
) && m_bases
[i
] != pile
)
458 pile
->RemoveTopCard(dc
);
459 m_bases
[i
]->AddCard(dc
, card
);
460 DoMove(dc
, pile
, m_bases
[i
]);
469 // Test to see whether the game has been won:
470 // i.e. m_pack, discard and bases are empty
471 bool Game::HaveYouWon()
473 if (m_pack
->GetTopCard()) return FALSE
;
474 if (m_discard
->GetTopCard()) return FALSE
;
475 for(int i
= 0; i
< 10; i
++)
477 if (m_bases
[i
]->GetTopCard()) return FALSE
;
480 m_totalScore
+= m_currentScore
;
486 // See whether the card under the cursor can be moved somewhere else
487 // Returns TRUE if it can be moved, FALSE otherwise
488 bool Game::CanYouGo(int x
, int y
)
490 Pile
* pile
= WhichPile(x
, y
);
491 if (pile
&& pile
!= m_pack
)
493 Card
* card
= pile
->GetTopCard();
498 for(i
= 0; i
< 8; i
++)
500 if (m_foundations
[i
]->AcceptCard(card
) && m_foundations
[i
] != pile
)
505 for(i
= 0; i
< 10; i
++)
507 if (m_bases
[i
]->GetTopCard() &&
508 m_bases
[i
]->AcceptCard(card
) &&
520 // Called when the left button is released after dragging a card
521 // Scan the piles to see if this card overlaps a pile and can be added
522 // to the pile. If the card overlaps more than one pile on which it can be placed
523 // then put it on the nearest pile.
524 void Game::LButtonUp(wxDC
& dc
, int x
, int y
)
528 // work out the position of the dragged card
532 Pile
* nearestPile
= 0;
533 int distance
= (CardHeight
+ CardWidth
) * (CardHeight
+ CardWidth
);
535 // find the nearest pile which will accept the card
537 for (i
= 0; i
< 8; i
++)
539 if (DropCard(x
, y
, m_foundations
[i
], m_liftedCard
))
541 if (m_foundations
[i
]->CalcDistance(x
, y
) < distance
)
543 nearestPile
= m_foundations
[i
];
544 distance
= nearestPile
->CalcDistance(x
, y
);
548 for (i
= 0; i
< 10; i
++)
550 if (DropCard(x
, y
, m_bases
[i
], m_liftedCard
))
552 if (m_bases
[i
]->CalcDistance(x
, y
) < distance
)
554 nearestPile
= m_bases
[i
];
555 distance
= nearestPile
->CalcDistance(x
, y
);
560 // Restore the area under the card
562 memoryDC
.SelectObject(*m_bmap
);
563 dc
.Blit(m_xPos
, m_yPos
, CardWidth
, CardHeight
,
564 &memoryDC
, 0, 0, wxCOPY
);
566 // Draw the card in its new position
570 nearestPile
->AddCard(dc
, m_liftedCard
);
571 if (nearestPile
!= m_srcPile
)
573 DoMove(dc
, m_srcPile
, nearestPile
);
578 // Return card to src pile
579 m_srcPile
->AddCard(dc
, m_liftedCard
);
589 bool Game::DropCard(int x
, int y
, Pile
* pile
, Card
* card
)
592 if (pile
->Overlap(x
, y
))
594 if (pile
->AcceptCard(card
))
603 void Game::MouseMove(wxDC
& dc
, int mx
, int my
)
608 memoryDC
.SelectObject(*m_bmap
);
610 int dx
= mx
+ m_xOffset
- m_xPos
;
611 int dy
= my
+ m_yOffset
- m_yPos
;
613 if (abs(dx
) >= CardWidth
|| abs(dy
) >= CardHeight
)
615 // Restore the area under the card
616 dc
.Blit(m_xPos
, m_yPos
, CardWidth
, CardHeight
,
617 &memoryDC
, 0, 0, wxCOPY
);
619 // Copy the area under the card in the new position
620 memoryDC
.Blit(0, 0, CardWidth
, CardHeight
,
621 &dc
, m_xPos
+ dx
, m_yPos
+ dy
, wxCOPY
);
626 dc
.Blit(m_xPos
, m_yPos
, dx
, CardHeight
, &memoryDC
, 0, 0, wxCOPY
);
630 dc
.Blit(m_xPos
+ dx
, m_yPos
, CardWidth
- dx
, dy
, &memoryDC
, dx
, 0, wxCOPY
);
631 memoryDC
.Blit(0, 0, CardWidth
- dx
, CardHeight
- dy
,
632 &memoryDC
, dx
, dy
, wxCOPY
);
633 memoryDC
.Blit(0, CardHeight
- dy
, CardWidth
- dx
, dy
,
634 &dc
, m_xPos
+ dx
, m_yPos
+ CardHeight
, wxCOPY
);
639 dc
.Blit(m_xPos
+ dx
, m_yPos
+ dy
+ CardHeight
, CardWidth
- dx
, -dy
,
640 &memoryDC
, dx
, CardHeight
+ dy
, wxCOPY
);
641 memoryDC
.Blit(0, -dy
, CardWidth
- dx
, CardHeight
+ dy
,
642 &memoryDC
, dx
, 0, wxCOPY
);
643 memoryDC
.Blit(0, 0, CardWidth
- dx
, -dy
,
644 &dc
, m_xPos
+ dx
, m_yPos
+ dy
, wxCOPY
);
646 memoryDC
.Blit(CardWidth
- dx
, 0, dx
, CardHeight
,
647 &dc
, m_xPos
+ CardWidth
, m_yPos
+ dy
, wxCOPY
);
652 dc
.Blit(m_xPos
+ CardWidth
+ dx
, m_yPos
, -dx
, CardHeight
,
653 &memoryDC
, CardWidth
+ dx
, 0, wxCOPY
);
656 dc
.Blit(m_xPos
, m_yPos
, CardWidth
+ dx
, dy
, &memoryDC
, 0, 0, wxCOPY
);
657 memoryDC
.Blit(-dx
, 0, CardWidth
+ dx
, CardHeight
- dy
,
658 &memoryDC
, 0, dy
, wxCOPY
);
659 memoryDC
.Blit(-dx
, CardHeight
- dy
, CardWidth
+ dx
, dy
,
660 &dc
, m_xPos
, m_yPos
+ CardHeight
, wxCOPY
);
665 dc
.Blit(m_xPos
, m_yPos
+ CardHeight
+ dy
, CardWidth
+ dx
, -dy
,
666 &memoryDC
, 0, CardHeight
+ dy
, wxCOPY
);
667 memoryDC
.Blit(-dx
, -dy
, CardWidth
+ dx
, CardHeight
+ dy
,
668 &memoryDC
, 0, 0, wxCOPY
);
669 memoryDC
.Blit(-dx
, 0, CardWidth
+ dx
, -dy
,
670 &dc
, m_xPos
, m_yPos
+ dy
, wxCOPY
);
672 memoryDC
.Blit(0, 0, -dx
, CardHeight
,
673 &dc
, m_xPos
+ dx
, m_yPos
+ dy
, wxCOPY
);
678 // draw the card in its new position
679 memoryDC
.SelectObject(*m_bmapCard
);
680 dc
.Blit(m_xPos
, m_yPos
, CardWidth
, CardHeight
,
681 &memoryDC
, 0, 0, wxCOPY
);
687 //----------------------------------------------//
688 // The Pack class: holds the two decks of cards //
689 //----------------------------------------------//
690 Pack::Pack(int x
, int y
) : Pile(x
, y
, 0, 0)
692 for (m_topCard
= 0; m_topCard
< NumCards
; m_topCard
++)
694 m_cards
[m_topCard
] = new Card(1 + m_topCard
/ 2, facedown
);
696 m_topCard
= NumCards
- 1;
702 Card
* temp
[NumCards
];
705 // Don't try to shuffle an empty m_pack!
706 if (m_topCard
< 0) return;
708 // Copy the cards into a temporary array. Start by clearing
709 // the array and then copy the card into a random position.
710 // If the position is occupied then find the next lower position.
711 for (i
= 0; i
<= m_topCard
; i
++)
715 for (i
= 0; i
<= m_topCard
; i
++)
717 int pos
= rand() % (m_topCard
+ 1);
721 if (pos
< 0) pos
= m_topCard
;
723 m_cards
[i
]->TurnCard(facedown
);
724 temp
[pos
] = m_cards
[i
];
728 // Copy each card back into the m_pack in a random
729 // position. If position is occupied then find nearest
730 // unoccupied position after the random position.
731 for (i
= 0; i
<= m_topCard
; i
++)
733 int pos
= rand() % (m_topCard
+ 1);
737 if (pos
> m_topCard
) pos
= 0;
739 m_cards
[pos
] = temp
[i
];
743 void Pack::Redraw(wxDC
& dc
)
748 sprintf(str
, "%d ", m_topCard
+ 1);
750 dc
.SetBackgroundMode( wxSOLID
);
751 dc
.SetTextBackground(FortyApp::BackgroundColour());
752 dc
.SetTextForeground(FortyApp::TextColour());
753 dc
.DrawText(str
, m_x
+ CardWidth
+ 5, m_y
+ CardHeight
/ 2);
757 void Pack::AddCard(Card
* card
)
759 if (card
== m_cards
[m_topCard
+ 1])
765 wxMessageBox("Pack::AddCard() Undo error", "Forty Thieves: Warning",
766 wxOK
| wxICON_EXCLAMATION
);
768 card
->TurnCard(facedown
);
774 for (m_topCard
= 0; m_topCard
< NumCards
; m_topCard
++)
776 delete m_cards
[m_topCard
];
781 //------------------------------------------------------//
782 // The Base class: holds the initial pile of four cards //
783 //------------------------------------------------------//
784 Base::Base(int x
, int y
) : Pile(x
, y
, 0, 12)
790 bool Base::AcceptCard(Card
* card
)
796 if (m_cards
[m_topCard
]->GetSuit() == card
->GetSuit() &&
797 m_cards
[m_topCard
]->GetPipValue() - 1 == card
->GetPipValue())
804 // pile is empty - ACCEPT
812 // nothing special at the moment
816 //----------------------------------------------------------------//
817 // The Foundation class: holds the cards built up from the ace... //
818 //----------------------------------------------------------------//
819 Foundation::Foundation(int x
, int y
) : Pile(x
, y
, 0, 0)
824 bool Foundation::AcceptCard(Card
* card
)
830 if (m_cards
[m_topCard
]->GetSuit() == card
->GetSuit() &&
831 m_cards
[m_topCard
]->GetPipValue() + 1 == card
->GetPipValue())
836 else if (card
->GetPipValue() == 1)
838 // It's an ace and the pile is empty - ACCEPT
844 Foundation::~Foundation()
846 // nothing special at the moment
850 //----------------------------------------------------//
851 // The Discard class: holds cards dealt from the m_pack //
852 //----------------------------------------------------//
853 Discard::Discard(int x
, int y
) : Pile(x
, y
, 19, 0)
858 void Discard::Redraw(wxDC
& dc
)
862 if (m_dx
== 0 && m_dy
== 0)
864 m_cards
[m_topCard
]->Draw(dc
, m_x
, m_y
);
870 for (int i
= 0; i
<= m_topCard
; i
++)
872 m_cards
[i
]->Draw(dc
, x
, y
);
878 y
= m_y
+ CardHeight
/ 3;
885 Card::DrawNullCard(dc
, m_x
, m_y
);
890 void Discard::GetTopCardPos(int& x
, int& y
)
897 else if (m_topCard
> 31)
899 x
= m_x
+ m_dx
* (m_topCard
- 32);
900 y
= m_y
+ CardHeight
/ 3;
904 x
= m_x
+ m_dx
* m_topCard
;
910 Card
* Discard::RemoveTopCard(wxDC
& dc
, int m_xOffset
, int m_yOffset
)
916 card
= Pile::RemoveTopCard(dc
, m_xOffset
, m_yOffset
);
920 int topX
, topY
, x
, y
;
921 GetTopCardPos(topX
, topY
);
922 card
= Pile::RemoveTopCard();
923 card
->Erase(dc
, topX
- m_xOffset
, topY
- m_yOffset
);
925 dc
.SetClippingRegion(topX
- m_xOffset
, topY
- m_yOffset
,
926 CardWidth
, CardHeight
);
928 for (int i
= m_topCard
- 31; i
<= m_topCard
- 31 + CardWidth
/ m_dx
; i
++)
930 m_cards
[i
]->Draw(dc
, m_x
- m_xOffset
+ i
* m_dx
, m_y
- m_yOffset
);
934 m_cards
[m_topCard
]->Draw(dc
, topX
- m_xOffset
- m_dx
, topY
- m_yOffset
);
936 dc
.DestroyClippingRegion();
945 // nothing special at the moment