13 min read

Categories

Tags

Designing BlackJack Game

Table of Contents

  1. Designing BlackJack Game
    1. Problem Statement
    2. Identify Classes and Relationships
    3. BlackJack - Header
    4. BlackJack - Source



Problem Statement

Design a blackjack game using object-oriented principles.

To come up with an object-oriented design for Black Jack game lets first go over the requirements of the system

  • There is one dealer and multiple players.
  • Each participant attempts to beat the dealer by getting a count as close to 21 as possible, without going over 21.
  • The standard 52-card pack is used, but in most casinos, several decks of cards are shuffled together.
  • An ace is worth 1 or 11.
  • Before the deal begins, each player places a bet.
  • When all the players have placed their bets, the dealer gives two card face up to each player in rotation clockwise, and then two card face up to himself.
  • If a player’s first two cards are an ace and a “ten-card” (a picture card or 10), giving him a count of 21 in two cards, this is a natural or “blackjack.”
  • After 1st round players can either fold the card, keep betting and get another card or bet and stand.
  • After 1st round dealer will deal one card to all players who are in the game.
  • Player whose total sum of cards is more than 21 automatically folds and lose the bet.



Identify Classes and Relationships

center-aligned-image



BlackJack - Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#ifndef blackjack_hpp
#define blackjack_hpp

#include <stdio.h>
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <stdlib.h>

using namespace std;

enum SuitColor {DEFAULT=0, CLOVE, DIAMOND, HEART, SPADE};
enum PlayerType {DEALER=0, PLAYER};

/*************************
 * Class card
 *************************/
class card
{
private:
    SuitColor cardColor;
    int cardNumber;
    bool available;

public:
    card () {}
    card (SuitColor color, int number) {
        this->cardColor = color;
        this->cardNumber = number;
        this->available = true;
    }

    bool isAvailable() {
        return available;
    }

    int getCardNumber() {
        return cardNumber;
    }

    SuitColor getCardColor() {
        return cardColor;
    }
};

/*************************
 * Class cardDeck
 *************************/
class cardDeck
{
private:
    vector<card*> deck;

public:
    inline cardDeck();

    vector<card*> getDeck() {
        return deck;
    }
    card* draw();
    void shuffleDeck();
    void printDeck();
};

/*************************
 * Class Person
 *************************/
class person
{
protected:
    string name;
    int playerNumber;
    PlayerType type;
    int totalAmount;
    int amountLeft;
    int betAmount;
    bool bet;
    bool fold;
    bool stands;

public:
    person() {}
    person(string name, int totalAmount) {
        this->name = name;
        this->totalAmount = totalAmount;
        this->betAmount = 0;
    }
    virtual string getName() {
        return name;
    }

    virtual int getPlayerNumber() {
        return playerNumber;
    }

    virtual PlayerType getPlayerType() {
        return type;
    }

    virtual int getTotalAmount() {
        return totalAmount;
    }

    virtual  int getAmountLeft() {
        return amountLeft;
    }

    virtual int getBetAmount() {
        return betAmount;
    }

    virtual bool isFolding() {
        return false;
    }

    virtual void setFold(bool fold) {
        this->fold = false;
    }

    virtual bool didFold() {
        return fold;
    }

    virtual bool isStanding() {
        return false;
    }

    virtual void setStanding(bool stands) {
        this->stands = false;
    }

    virtual bool getStanding() {
        return this->stands;
    }

    virtual bool isBetting() {
        return true;
    }

    virtual bool getBetting() {
        return bet;
    }

    virtual bool canBet(int betAmount) {
        if (betAmount < amountLeft) {
            return true;
        }

        return false;
    }

    virtual void setBet(int betAmount) {
        amountLeft -= betAmount;
        this->betAmount += betAmount;
    }


    virtual void refillFunds(int amount) {
        totalAmount += amount;
        amountLeft += amount;
    }

    virtual int compensateLooseAmount() {
        return 0;
    }
    virtual void printObjectState() {}
};

/*************************
 * Class Dealer
 *************************/
class dealer:public person
{
private:
    cardDeck* deckObj;

public:
    dealer() {}
    dealer(string name, int totalAmount) {
        this->name = name;
        this->totalAmount = totalAmount;
        this->amountLeft = totalAmount;
        this->betAmount = 0;
        this->type = DEALER;

        // create deck of card. Deck belongs to dealer
        deckObj = new cardDeck();
    }

    card* deal();

    int compensateLooseAmount(int amount) {
        this->amountLeft -= amount;
        return amount;
    }

    void printDealerReport();
};

/*************************
 * Class player
 *************************/
class player:public person
{
public:
    player(string name, int totalAmount, int playerNumber) {
        this->name = name;
        this->totalAmount = totalAmount;
        this->amountLeft = totalAmount;
        this->betAmount = 0;
        this->type = PLAYER;

        fold = false;
        stands = false;
        bet = false;
        this->playerNumber = playerNumber;
    }
    bool flipCoin();
    bool isFolding();
    void setFold(bool fold);
    bool isStanding();
    void setStanding(bool stands);
    bool isBetting();
    bool canBet(int betAmount);
    int getAmountLeft();
    int compensateLooseAmount();
    void printPlayerReport();
};


/*************************
 * Class gameTable
 *************************/
class gameTable
{
private:
    map<person*, vector<card*>> hand;
    map<person*, int> scoreDetails;
    vector<person*> players;

public:
    gameTable() {}
    void addPlayerInHand(person *player);
    void setScore(person* player, int score);
    int getScore(person* player);
    void cardToPlayer(person* player, card* Card);
    vector<person*> getPlayers();
    void calculateScore();
    vector<person*> getPlayersHitBlackJack();
    void foldPlayersWhoLost(dealer *dealerObj);
    void removePlayerInHand(person *player);
    vector<person*> currentPlayersInGame();
    vector<card*> getPlayerCards(person* player);
    void printScoreReport();
};

void blackjackDriver();

#endif /* blackjack_hpp */

BlackJack - Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
#include "blackjack.hpp"

/*************************
 * Class cardDeck methods
 *************************/
inline cardDeck::cardDeck()
{
    SuitColor color = DEFAULT;
    int colorCounter = 0;

    int cardNumber = 0;
    for (int i = 1; i <= 52; i++) {
        if (i % 13 == 1) {
            color = (SuitColor)(++colorCounter);
        }

        cardNumber = i % 13;
        cardNumber = (cardNumber == 0) ? 13 : cardNumber;

        card* cardObj = new card(color, cardNumber);
        deck.push_back(cardObj);
    }
    shuffleDeck();
}

card* cardDeck::draw()
{
    // Get card from deck
    if (!deck.empty()) {
        card* temp = deck.back();
        deck.pop_back();
        return temp;
    }

    return NULL;
}

void cardDeck::shuffleDeck()
{
    int indx = 0;
    card *temp;

    for (int i = 0; i <= 51; i++) {
        indx = rand() % 52;

        cout << "Swap " << i << ", " << indx << endl;
        temp = deck[i];
        deck[i] = deck[indx];
        deck[indx] = temp;
    }
}

void cardDeck::printDeck()
{
    for (int i = 0; i < deck.size(); i++) {
        switch (deck[i]->getCardColor()) {
            case CLOVE:
                cout << (i%13)+1 << " -> " << "CLOVE" << endl;
                break;

            case DIAMOND:
                cout << (i%13)+1 << " -> " << "DIAMOND" << endl;
                break;

            case HEART:
                cout << (i%13)+1 << " -> " << "HEART" << endl;
                break;

            case SPADE:
                cout << (i%13)+1 << " -> " << "SPADE" << endl;
                break;
            default:
                break;
        }
    }
}

/*************************
 * Class gameTable methods
 *************************/
void gameTable::addPlayerInHand(person *player) {
    hand.insert(std::pair<person*, vector<card*>>(player, NULL));
    scoreDetails.insert(std::pair<person*, int>(player, 0));

    this->players.push_back(player);
}

void gameTable::setScore(person* player, int score) {
    scoreDetails[player] = score;
}

int gameTable::getScore(person* player) {
    return scoreDetails[player];
}

void gameTable::cardToPlayer(person* player, card* Card) {
    map<person*, vector<card*>>::iterator handItr;

    handItr = hand.find(player);
    vector<card*> allCardsInHand = handItr->second;
    allCardsInHand.push_back(Card);
    handItr->second = allCardsInHand;
}

vector<person*> gameTable::getPlayers() {
    return players;
}

void gameTable::calculateScore() {
    // Go through each player and calulate score
    map<person*, vector<card*>>::iterator handItr;
    map<person*, int>::iterator scoreDetailsItr;

    for (handItr = hand.begin(); handItr != hand.end(); ++handItr) {
        vector<card*> allCardsSoFar = handItr->second;

        int totalscore = 0;
        for (int i = 0; i < allCardsSoFar.size(); i++) {
            switch(allCardsSoFar[i]->getCardNumber()) {
                    // Cards 10, J, Q, K
                case 10:
                case 11:
                case 12:
                case 13:
                    totalscore += 10;
                    break;
                case 1:
                    // Card 'A'
                    if (totalscore + 11 >= 21) {
                        totalscore += 1;
                    } else {
                        totalscore += 11;
                    }

                    break;
                default:
                    // All cards between 2-9
                    totalscore += allCardsSoFar[i]->getCardNumber();
                    break;
            }
        }
        scoreDetailsItr = scoreDetails.find(handItr->first);
        scoreDetailsItr->second = totalscore;
    }
}

vector<person*> gameTable::getPlayersHitBlackJack() {
    map<person*, int>::iterator scoreDetailsItr;
    vector<person*> temp;

    for (scoreDetailsItr = scoreDetails.begin();
         scoreDetailsItr != scoreDetails.end(); ++scoreDetailsItr) {
        if (scoreDetailsItr->second == 21 && !scoreDetailsItr->first->didFold() &&
            scoreDetailsItr->first->getPlayerType() == PLAYER) {
            temp.push_back(scoreDetailsItr->first);
        }
    }

    return temp;
}

void gameTable::removePlayerInHand(person *player) {
    hand.erase(player);
    scoreDetails.erase(player);

    vector<person*>::iterator playersItr;
    for (playersItr = players.begin(); playersItr != players.end(); ++playersItr) {
        if (*playersItr == player) {
            players.erase(playersItr);
        }
    }
}

vector<person*> gameTable::currentPlayersInGame()
{
    vector<person*> temp;
    for (int i = 0; i < players.size(); i++) {
        if (!players[i]->didFold()) {
            temp.push_back(players[i]);
        }
    }

    return temp;
}

vector<card*> gameTable::getPlayerCards(person* player)
{
    map<person*, vector<card*>>::iterator handItr;
    handItr = hand.find(player);

    return handItr->second;
}

void gameTable::foldPlayersWhoLost(dealer *dealerObj)
{
    map<person*, int>::iterator scoreDetailsItr;

    for (scoreDetailsItr = scoreDetails.begin();
         scoreDetailsItr != scoreDetails.end(); ++scoreDetailsItr) {
        if (scoreDetailsItr->first->getPlayerType() == PLAYER &&
            !scoreDetailsItr->first->didFold() &&
            scoreDetailsItr->second > 21) {

            // Get the bet amount of player
            dealerObj->refillFunds(scoreDetailsItr->first->getBetAmount());
            scoreDetailsItr->first->setFold(true);
        }
    }

    return;
}

void gameTable::printScoreReport()
{
    // Print score of each player
    cout << endl << "[Players Scores]" << endl;
    for (int i = 0; i < this->players.size(); i++) {
        cout << "   ==> [" << players[i]->getName() << "] " <<
        getScore(players[i]) << endl;
    }
}

/*************************
 * Class player methods
 *************************/
bool player::flipCoin() {
    return rand() % 2;
}

bool player::isFolding() {
    if (this->fold == true) {
        return true;
    }
    return flipCoin();
}

void player::setFold(bool fold) {
    this->fold = fold;
}

bool player::isStanding() {
    bool temp = flipCoin();
    setStanding(temp);
    return temp;
}

void player::setStanding(bool stands) {
    this->stands = stands;
}

bool player::isBetting() {
    return flipCoin();
}

bool player::canBet(int betAmount) {
    if (betAmount < this->amountLeft && this->fold == false) {
        return true;
    }

    return false;
}

int player::getAmountLeft() {
    return amountLeft;
}

int player::compensateLooseAmount() {
    int temp = betAmount;
    betAmount = 0;
    return temp;
}

void player::printPlayerReport()
{
    cout << "[Player]" << endl;
    cout << "   ==> Player Name: " << getName() << endl;
    cout << "   ==> Player Number: " << getPlayerNumber() << endl;
    cout << "   ==> Player Type: " << getPlayerType() << endl;
    cout << "   ==> Total Amount: " << getTotalAmount() << endl;
    cout << "   ==> Amount Left: " << getAmountLeft() << endl;
    cout << "   ==> Bet Amount: " << getBetAmount() << endl;
    cout << "   ==> Betting: " << getBetting() << endl;
    cout << "   ==> Fold: " << didFold() << endl;
    cout << "   ==> Stand: " << getStanding()  << endl << endl;
}

/*************************
 * Class dealer methods
 *************************/
card* dealer::deal()
{
    return deckObj->draw();
}

void dealer::printDealerReport()
{
    cout << "[Dealer]" << endl;
    cout << "   ==> Dealer Name: " << getName() << endl;
    cout << "   ==> Dealer Number: " << getPlayerNumber() << endl;
    cout << "   ==> Player Type: " << getPlayerType() << endl;
    cout << "   ==> Total Amount: " << getTotalAmount() << endl;
    cout << "   ==> Amount Left: " << getAmountLeft() << endl;
    cout << "   ==> Bet Amount: " << getBetAmount() << endl;
    cout << "   ==> Betting: " << getBetting() << endl;
    cout << "   ==> Fold: " << didFold() << endl;
    cout << "   ==> Stand: " << getStanding()  << endl << endl;
}

/*************************
 * blackjackDriver
 *************************/
void blackjackDriver()
{
    // Create dealer
    dealer dealerObj("Dealer", 10000);

    // Create players
    player player0("Player0", 200, 0);
    player player1("Player1", 200, 1);
    player player2("Player2", 200, 2);
    player player3("Player3", 200, 3);

    // Create Game Table
    gameTable gameObj;
    gameObj.addPlayerInHand(&player0);
    gameObj.addPlayerInHand(&player1);
    gameObj.addPlayerInHand(&player2);
    gameObj.addPlayerInHand(&player3);
    gameObj.addPlayerInHand(&dealerObj);

    // Print Player reports
    vector<person*> players = gameObj.getPlayers();
    dealerObj.printDealerReport();
    player0.printPlayerReport();
    player1.printPlayerReport();
    player2.printPlayerReport();
    player3.printPlayerReport();

    int round = 0;
    while(1) {
        round++;
        cout << endl << "========Round " << round << "========" << endl;

        // All players bet
        for (int i = 0; i < players.size(); i++) {
            if (players[i]->canBet(50)) {
                if (players[i]->getPlayerType() == PLAYER) {
                    players[i]->setBet(50);

                    cout << "[" << players[i]->getName() << "] betting: " <<
                                    players[i]->getBetAmount() << endl;
                }

                if (!players[i]->isStanding()) {
                    gameObj.cardToPlayer(players[i], dealerObj.deal());
                    if (round == 1) {
                        gameObj.cardToPlayer(players[i], dealerObj.deal());
                    }

                    cout << "[" << players[i]->getName() <<
                            "] is not standing" << endl;
                }

                vector<card*>cards =  gameObj.getPlayerCards(players[i]);
                for (vector<card*>::iterator cardsItr = cards.begin();
                     cardsItr != cards.end(); ++cardsItr) {
                    cout << "   ==> " << (*cardsItr)->getCardColor() <<
                            ", " << (*cardsItr)->getCardNumber() << endl;
                }
            }
        }

        // Find scores after each iteration
        gameObj.calculateScore();

        // Find players exceeded 21 and lost
        gameObj.foldPlayersWhoLost(&dealerObj);

        // Print score of each player
        gameObj.printScoreReport();

        // Find players hit blackJack and compensate them
        vector<person*> playersHitBlackJack = gameObj.getPlayersHitBlackJack();
        for (int i = 0; i < playersHitBlackJack.size(); i++) {
            int betAmount = playersHitBlackJack[i]->getBetAmount();

            // dealer compensate the amount to player
            playersHitBlackJack[i]->refillFunds(
                                dealerObj.compensateLooseAmount(2 * betAmount));
            playersHitBlackJack[i]->setFold(true);
        }

        // Find if dealer hit BJ or Over 21 or under 21?
        if (gameObj.getScore(&dealerObj) == 21) {
            // Game over and dealer get money from other players still in game
            for (int i = 0; i < playersHitBlackJack.size(); i++) {
                if (playersHitBlackJack[i]->getPlayerType() == PLAYER &&
                    !playersHitBlackJack[i]->didFold()) {
                    dealerObj.refillFunds(
                            playersHitBlackJack[i]->compensateLooseAmount());
                }
            }

            break;
        } else if (gameObj.getScore(&dealerObj) > 21) {
            // Dealer loose and other people get double the bet
            vector<person*> currentPlayers = gameObj.currentPlayersInGame();

            for (int i = 0; i < currentPlayers.size(); i++) {
                if (currentPlayers[i]->getPlayerType() == PLAYER &&
                    gameObj.getScore(currentPlayers[i]) < 21) {
                    int betAmount = currentPlayers[i]->getBetAmount();

                    currentPlayers[i]->refillFunds(
                                dealerObj.compensateLooseAmount(2 * betAmount));
                }
            }

            break;
        }
    }

    cout << endl << endl;
    cout << "****[Final Report]****" << endl;
    dealerObj.printDealerReport();
    player0.printPlayerReport();
    player1.printPlayerReport();
    player2.printPlayerReport();
    player3.printPlayerReport();

    cout << "========BlackJack Ended========";
    return;
}