Title: Ch 16. Case Study A Card Game
1Ch 16. Case Study A Card Game
- Timothy Budd
- Oregon State University
2Introduction
- Simple card game Solitaire
- Microsoft Foundation Classes (MFC) is used for
the graphical interface
3The Class Card
- Use the preprocessor to ensure that including a
header file more than once will not cause error. - Names defined within a class declaration must
always be qualified when they are used outside
the class. - include "card.h"CardColors Cardcolor ()
if (suit() Heart suit()
Diamond) return Red return Black
4- ifndef CARDH // ensure only included once
- define CARDH
- // Playing Card Abstraction
- class Card public enum Suits Heart, Spade,
Diamond, Club enum Colors Red, Black //
constructors Card (Suits sv, int rv) s(sv),
r(rv) fup false // return attributes int
rank () return r Suits suit() return s
bool faceup () return fup Colors color
() // change attributes void flip () fup !
fup private const Suits s const int
r bool fup -
- endif
5Data and View Classes
- A view class provides the tools used to view data
values held by another class. - To isolate the library-specific aspects of the
card view, the actual display methods are
declared as pure virtual methods.
6- ifndef CardViewH define CardViewH include
"card.h"// CardView -- Display a graphical
representation of a card - class CardView public static const int
Width 40 static const int Height
70 virtual void display (Card aCard, int,
int) 0 virtual void halfDisplay (Card
aCard, int, int) 0 endif
7The Game
- Klondike
- The cards that are not part of the tableau are
initially all in the deck. - Cards in the deck are face down, and are drawn
one by one from the deck and placed, face up, on
the discard pile. - Can be moved onto either a tableau pile or a
foundation. - Cards are drawn from the deck until the pile is
empty then the game is over if no further moves
can be made.
8Figure 16.2 Layout for the Solitaire Game
Shit piles
Discard pile
Deck
Table piles
9Card Piles Inheritance in Action
- Use inheritance to factor out common behavior
from similar behavior in similar classes. - Top method
- Card CardPiletop() throw (CardPilePopError)
if (cards.empty()) throw new
PopError() return cards.front()
10- class CardPile //CardPile -- representation
of a Pile of Cards public CardPile (int
xl, int yl) x(xl), y(yl) class PopError
// exception on pop from empty stack - // drawing cards from pile bool empty ()
return cards.empty() Card top () throw
(PopError) Card pop () throw (PopError)
Card result top() cards.pop_front() return
result - // virtual methods that can be
overridden virtual bool includes (int,
int) virtual void addCard (Card ) virtual
void display (CardView ) virtual bool canTake
(Card ) virtual void select ()
protected list cards const int x //
location of display const int y ... //
definitions of subclasses
11Card Piles
- To catch the potential stack underflow, the main
program will at some point be surrounded by a try
clause -
- Card CardPiletop() throw (CardPilePopError)
if (cards.empty()) throw new
PopError() return cards.front()
12Card Piles
- includes Determines if the coordinates given as
arguments are contained within the boundaries of
the pile. - canTake Tells whether a pile can take a specific
card. - addCard Adds a card to the card list. It is
redefined in the discard pile class to ensure
that the card is face up.
13Card Piles
- display Displays the card deck. The default
method merely displays the topmost card of the
pile, but is overridden in the tableau class to
display a column of cards. Only the topmostand
bottommost face-up cards are displayed. - select Performs an action in response to a mouse
click. It is invoked when the user selects a pile
by clicking the mouse in the portion of the
playing field covered by the pile.
14Methods and Interface
15The Default Card Pile
- bool CardPileincludes (int tx, int ty) //
default behavior is point within bounds of
card? return x CardViewWidth y CardViewHeightvoid CardPileaddCard
(Card aCard) // default behavior push card
on front of stack cards.push_front(aCard)
16The Default Card Pile
- void CardPiledisplay (CardView cv) //
default behavior print topmost card if
(empty()) cv.display(0, x, y) else cv.displa
y(top(), x, y)bool CardPilecanTake (Card
) // default behavior just say no return
falsevoid CardPileselect () // default
behavior do nothing
17The Suit Piles
- Class SuitPile represents the pile of cards at
the top of the playing surface, the pile being
built up in suit from ace to king. - The interface for this class
- class SuitPile public CardPile
public SuitPile (int xl, int yl) CardPile(xl,
yl) bool canTake (Card )
18The Suit Piles
- bool SuitPilecanTake (Card aCard) // can
take ace if empty if (empty()) return
aCard-rank() 1 - // otherwise must be next card in suit Card
topCard top() return (aCard-suit()
topCard-suit()) (aCard-rank() 1
topCard-rank())
19The Deck Pile
- The DeckPile maintains the deck from which new
cards are drawn. - class DeckPile public CardPile
public DeckPile (int, int, Card ) void
select ()
20The Deck Pile
- DeckPileDeckPile (int xl, int yl, Card orig
) CardPile(xl, yl) for (int i 0 i 52 i) Card theCard origi if
(theCard-faceup()) theCard-flip() addCard(
theCard)
21The Deck Pile
- include "Game.h" // include description of
game manager extern Game gameManagervoid
DeckPileselect () // move topmost card to
discard stack if (empty()) return gameManager
.discardPile()-addCard (top()) pop()
22Figure 16.4 Definition of Application Class
- include "CardPile.h"class Game // Game --
Game Manager public // constructor,
initialization and Destructor void init
() Game () // access to the piles CardPile
deckPile () return piles0 CardPile
discardPile () return piles1 CardPile
suitPile (int i) return piles2i CardPile
tableau (int i) return piles6i //
handling actions void repaint(CardView ) void
mouseDown (int, int) private CardPile
piles13
23The Discard Pile
- class DiscardPile public CardPile
public DiscardPile (int xl, int yl)
CardPile(xl, yl) void addCard (Card
) void select ()
24The Discard Pile
- void DiscardPileselect () if
(empty()) return Card topCard top() for
(int i 0 i (i)-canTake(topCard)) gameManager.suitPil
e(i)-addCard(topCard) pop() return
for (int i 0 i (gameManager.tableau(i)-canTake(topCard))
gameManager.tableau(i)-addCard(topCard) pop(
) return
25The Discard Pile
- The C does not use the pseudo-code variable
super, instead, uses qualified name. - void DiscardPileaddCard (Card aCard) if
(! aCard-faceup()) aCard-flip() CardPilead
dCard(aCard)
26The Table Piles
- The most complex of the subclasses of CardPile is
that used to hold a tableau, or table pile. - The interface for this class redefines nearly all
of the virtual methods defined in ClassPile - class TablePile public CardPile
public TablePile (int xl, int yl, int
p) bool canTake (Card aCard) bool includes
(int tx, int ty) void display (CardView
cv) void select ()
27The Table Piles
- TablePileTablePile(int xv, int yv, int p)
CardPile(xv, yv) // copy right number of cards
into deck for (int i 0 i aCard gameManager.deckPile()-top() if
(aCard-faceup()) aCard-flip() addCard(aCar
d) gameManager.deckPile()-pop() // flip
topmost card top()-flip()
28The Table Piles
- bool TablePilecanTake (Card aCard) if
(empty()) return aCard-rank() 12 Card
topCard top() return (aCard-color() !
topCard-color()) (aCard-rank()
topCard-rank() - 1) - bool TablePileincludes (int tx, int
ty) return x CardViewWidth y
29- void TablePileselect () // if empty, do
nothing if (empty()) return // if face down,
then flip Card topCard top() if (!
topCard-faceup()) topCard-flip() return
// else see if any pile can take card for
(int i 0 i (i)-canTake(topCard)) gameManager.suitPile(i
)-addCard(pop()) return for (int i
0 i canTa
ke(topCard)) gameManager.tableau(i)-addCard(
pop()) return
30The Table Piles
- void TablePiledisplay (CardView cv) if
(empty()) CardPiledisplay(cv) else int
lx x int ly y listiterator
cptr cards.end() listiterator
front cards.begin() for (--cptr cptr !
front --cptr) cv.halfDisplay(cptr, lx,
ly) ly CardViewHeight/2 cv.display
(front, lx, ly)
31Playing the Polymorphic Game
- Initialize card piles and shuffle deck
- void Gameinit () // first, create a deck and
randomize itint j 0for (int i 1 i i) originalDeckj new
Card(CardDiamond, i) originalDeckj new
Card(CardSpade, i) originalDeckj new
Card(CardHeart, i) originalDeckj new
Card(CardClub, i)randomInteger swapper //
declare the random function objectrandom_shuffle(
originalDeck, originalDeck52, swapper) - // continue in back
32Playing the Polymorphic Game
- Variable piles by all card piles used in drawing
and other operations. - // then initialize each of the deck
pilespiles0 new DeckPile(335, 5,
originalDeck) piles1 new DiscardPile(268,
5)j 2for (int i 0 i pilesj new SuitPile(15 60 i,
5)for (int i 0 i new TablePile(5 55 i, 80, i1) -
33Playing the Polymorphic Game
- class randomInteger public unsigned int
operator () (unsigned int max) unsigned int
rval rand() return rval max - GameGame() // free up all the old card
values for (int i 0 i originalDecki
34Playing the Polymorphic Game
- void Gamerepaint(CardView cv) // simply
repaint each of the card decks for (int i 0 i
display(cv) - void GamemouseDown(int x, int y) for (int i
0 i includes(x, y))
pilesi-select() return
35The Graphical User Interface
- Isolate the graphical interface from the rest of
a program. - class MFCardView public CardView
public // constructor saves drawing
context MFCardView (CPaintDC idc) dc(idc)
// implement the interface void display
(Card aCard, int, int) void halfDisplay (Card
aCard, int, int)private CPaintDC dc //
drawing context void paintCard(Card aCard,
int, int)
36The Graphical User Interface
- void MFCardViewdisplay (Card aCard, int x,
int y) dc.Rectangle (x, y, x Width, y
Height) paintCard(aCard, x, y)void
MFCardViewhalfDisplay (Card aCard, int x, int
y) dc.Rectangle (x, y, x Width, y Height/
2) paintCard (aCard, x, y)
37- void MFCardViewpaintCard (Card aCard, int x,
int y) char buffer80if (aCard 0)
strcpy(buffer,"mt") // mt
empty dc.SetTextColor(RGB(0,255, 0)) //
green dc.TextOut(x5, y5, buffer,
strlen(buffer)) else if (aCard-faceup())
char suitCard switch (aCard-suit())
case CardHeart suitCard 'H'
break switch (aCard-rank())
case 1 sprintf(buffer,"A c", suitCard)
break - default
- sprintf(buffer, "d c", aCard-rank(),
suitCard) break if (aCard-color()
CardRed) dc.SetTextColor(RGB(255, 0, 0)) //
red else dc.SetTextColor(RGB(0, 0, 0)) //
black dc.TextOut(x5, y5, buffer,
strlen(buffer)) else strcpy(buffer,
"back") dc.SetTextColor(RGB(255, 255,
0)) // yellow dc.TextOut(x5, y5,
buffer, strlen(buffer))
38The Graphical User Interface
- class SolitareMainWindow public CFrameWnd
public SolitareMainWindow() Create(NULL,
"Solitare Game") afx_msg void
OnPaint() afx_msg void OnLButtonDown (UINT
flag, CPoint loc) DECLARE_MESSAGE_MAP()
39The Graphical User Interface
- void SolitareMainWindowOnPaint() CPaintDC
dc(this) MFCardView cv(dc) gameManager.repain
t(cv)void SolitareMainWindowOnLButtonDown(U
INT flag, CPoint loc) try gameManager.mouse
Down(loc.x, loc.y) catch (CardPilePopError
e) // do nothing on error InvalidateRect(N
ULL)
40The Graphical User Interface
- class SolitareApplication public CWinApp
public BOOL InitInstance()SolitareAppli
cation theAppBOOL SolitareApplicationInitInst
ance() gameManager.init() m_pMainWnd new
SolitareMainWindow() m_pMainWnd-ShowWindow
(m_nCmdShow) m_pMainWnd-UpdateWindow()