Feeds:
Posts
Comments

Archive for March, 2012

So I’ve looked at the initialization and the classes involved in the game, now time to look at the game itself.

The first part of any game of blackjack is the dealing of cards when each player is assigned two cards. These can be laid face up or face down depending on the casino rules, but for my game, all cards would be face up except for the dealer’s second card. The game started this way as a console application so I could see that cards were being dealt randomly, and it continued this way when I moved onto SDL. It is actually played this way in some casinos, so it’s not a complete cop out on my part! It would probably be easier to have all the cards placed face down as this would reduce the number of card images that have to be loaded, but I like the idea of showing the user everything that is going on.

Anyway, on we go with the code. Here’s the function that deals each player’s two starting cards, dealStartingCards():

void dealStartingCards() {
    backofcard = load_image("cards/reverse.png");

    for(int i=0; i<2; i++) {
        for(int j=0; j<PLAYERCOUNT; j++) {
            players[j].giveCard(pickCard());

            if(j==THE_DEALER && i==0)
                apply_surface(DEALER_CARD_X, DEALER_CARD_Y, players[j].getCardImage(i), screen);
            else if (j==THE_DEALER && i==1)
                apply_surface(DEALER_CARD_X+200, DEALER_CARD_Y, backofcard, screen);
            else if(i==0)
                apply_surface(PLAYER_CARD_X + j*200, PLAYER_CARD_Y, players[j].getCardImage(i), screen);
            else
                apply_surface(PLAYER_CARD_X + j*200, PLAYER_CARD_Y-50, players[j].getCardImage(i), screen);

            SDL_Flip(screen);
            SDL_Delay(500);
        }
    }

    playerinstructions = TTF_RenderText_Blended(font,"Press h to hit or s to stand", whiteText);
    apply_surface(int((SCREEN_WIDTH-playerinstructions->w)/2), int((SCREEN_HEIGHT-playerinstructions->h)/3), playerinstructions, screen);
    SDL_Flip(screen);
}

The backofcard SDL_Surface* is loaded straight off the bat, then we enter a nested for loop. The outer loop is because each player receives two cards, while the inner loop goes through each player. The reason the loops are this way round is so that each player is assigned one card in order, and then assigned a second card. Each player is given the card using the pickCard() method, which is incredibly simple

PlayingCard pickCard() {
    static int cardsDealt = 0;

    cardsDealt += 1;
    return pack[cardsDealt-1];
}

The static int is incremented, and the next time the function is called, this value will be remembered and the assignment to 0 will be ignored. This means that as the function is called time and time again, the dealer will work his way through the pack. pack[cardsDealt – 1] is returned because otherwise pack[0] would be ignored. No RNG is needed here as the pack has already been shuffled in generatePack().

The cards are drawn to the screen as soon as they are dealt, and placed into their correct positions depending on their player and card number. The screen is then updated and a delay of 500ms is put in place so that the cards aren’t all dealt in one flash at runtime. If I were to rewrite the code now, I would use SDL_GetTicks() and a while loop to check if 500ms had passed or not, that way the user would be able to interact with the window whilst the cards are being dealt. As it is, the user is unable to close or move the window whilst SDL_Delay() is in action.

Once the cards are dealt, the user is given the option of pressing h or s to hit or stand. Control then moves to the user_plays() function.

void user_plays() {
    Uint8* keystates = SDL_GetKeyState(NULL);

    if(players[USER].isAlive() && !(players[USER].isStanding())) {
        if(keystates[SDLK_h]) {
            players[USER].giveCard(pickCard());

            int numberOfCards = players[USER].getNumberOfCards()-1;
            apply_surface(PLAYER_CARD_X, PLAYER_CARD_Y-numberOfCards*50, players[USER].getCardImage(numberOfCards), screen);
            SDL_Flip(screen);
        }
        else if (keystates[SDLK_s]) {
            players[USER].setStanding(true);

            playerinstructions = TTF_RenderText_Blended(font,"You have chosen to stand", whiteText);
            redrawScreen(playerinstructions);
            SDL_Delay(1000);
        }

        if(!players[USER].isAlive()) {
            playerinstructions = TTF_RenderText_Blended(font,"You have gone bust!", whiteText);
            players[USER].setNameSurface(playerBust, blackText);
            redrawScreen(playerinstructions);
            SDL_Delay(1000);
        }
    }
}

This shows the use of the USER constant, turning player[0] into player[USER], something a lot more readable.

If the user is still alive (i.e. they’ve not cleared 21 and gone bust) and they’re not standing on their cards, their key presses are taken into account. If they press h, give them a card, then draw the card to the screen. If they press s, set the player’s standing status to true and change the text to tell the user this.

Once this has been completed, check if the user has gone bust (this is set in the giveCard() method within player): if so change the text in the middle of the screen and change the user’s name to black and strike it out.

Next time, we’ll take a look at the basic AI that I’ve put in to place for the user and dealer.

Read Full Post »

In my last post I explained how I initialized my blackjack game. In this post I’m going to have a look at the two classes that I created specifically for the game: Player and PlayingCard.

class Player {
    private:
        string m_name;
        SDL_Surface* m_nameSurface;
        vector<PlayingCard> m_vCards;
        vector<SDL_Surface*> m_vCardimages;
        int m_score, m_aceCount;
        bool m_alive, m_standing;

    public:
        Player();

        string getName() const { return m_name; }
        SDL_Surface* getNameSurface() const { return m_nameSurface; }
        PlayingCard getCard(int i) const { return m_vCards[i]; }
        SDL_Surface* getCardImage(int card) const { return m_vCardimages[card]; }
        unsigned int getNumberOfCards() const { return m_vCards.size(); }
        int getScore() const { return m_score; }
        bool isAlive() const { return m_alive; }
        bool isStanding() const { return m_standing; }

        void setName(string playername);
        bool setNameSurface(TTF_Font* font, SDL_Color color);
        void giveCard(PlayingCard card);
        void setStanding(bool standing);
        bool beats(Player& player);
};

Firstly, here’s the Player header file. a Player has a name, the score of their cards, the number of aces in their pack, whether they’re alive, whether they’re standing, a vector of PlayingCards they’ve been dealt and a vector of SDL_Surface pointers to their card images. The function list contains a bunch of get and set methods, a giveCard() method and a beats(Player &p) function to see if they’ve beaten another Player. Lets look at how this all implemented in the source file:

Player::Player() {
    m_name = "Player";
    m_score = 0;
    m_aceCount = 0;
    m_alive = true;
    m_standing = false;
    m_nameSurface = NULL;
}

void Player::setName(string playername) {
   m_name = playername;
}

bool Player::setNameSurface(TTF_Font* font, SDL_Color color) {
    m_nameSurface = TTF_RenderText_Blended(font, m_name.c_str(), color);

    if (m_nameSurface == NULL)
        return false;
    else
        return true;
}

void Player::setStanding(bool standing) {
    m_standing = standing;
}

void Player::giveCard(PlayingCard card) {
    m_vCards.push_back(card);

    SDL_Surface* cardimage = load_image(card.getFilename());

    m_vCardimages.push_back(cardimage); 

    if(card.getName() == "ace")
        m_aceCount += 1;

    m_score += card.getScore();

    if(m_score > 21 && m_aceCount > 0) { 
        m_score -= 10;                     
        m_aceCount -= 1;                   
    }
    else if (m_score > 21) {
        m_alive = false;
    }
}

bool Player::beats(Player& player) {
    if(m_score > player.getScore())
        return true;
    else if (m_score < player.getScore())
        return false;
    else {
        if(m_vCards.size() < player.getNumberOfCards())
            return true;
        else
            return false;
    }
}

This code actually looks quite legible and almost … good? The constructor sets blank values as the Players are constructed at the start of the program and the names aren’t set until later in the game loop. Most of the functions are obvious, but the last two are a bit more detailed, so lets take a look at them.

The giveCard() method pushes the card passed in to the back of the vector, then loads the card’s image and pushes it to the back of the SDL_Surface* vector. The card’s score is then added on to the player’s score and then the clever (?) bit happens. In Blackjack, aces can count for 11 or 1, depending on the user’s circumstance (i.e. if you have an ace and 10, you take your score as 21. If you have an ace and a 2, then draw a 10 you don’t want to go bust on 23, so you take your ace down to one and have your score as 13). As aces have a default score of 11 (see the generatePack() method in the previous post), the if statement at the bottom puts this in to effect: if the user has cleared 21 but has an ace in their pack, take 10 off their score (in effect making the ace worth one). You then have to decrease the aceCount so you can only do this once per ace (in our earlier example you could get a King and be bust on 23 again, but the if statement would take off 10 thinking you had another ace, in effect giving you the King for free). If you go over 21 but don’t have an ace in your pack, you just go bust.

The beats(Player player) method is used at the end of the game to find the winner. If the player has a higher score then the parameter, they have beaten them. If their score is lower then the parameter player, they have lost. If the score is equal, the player with less cards wins.

Now on to the PlayingCard.h file:

class PlayingCard {
    private:
        string m_name, m_suit, m_filename;
        int m_score;

    public:
        PlayingCard() {};

        string getName() const { return m_name; };
        string getSuit() const { return m_suit; };
        string getFilename() const { return m_filename; }
        int getScore() const { return m_score; };

        void setName(string name);
        void setSuit(string suit);
        void setFilename();
        void setScore(int score);

        string toString();
};

A PlayingCard has a name (three, ace, king etc), a suit (clubs, spades etc), a filename and a score. Then there are all the normal get and set methods and a toString() method (does my Java experience show here? I think if I did it now it would be str() as this seems to be C++ convention). The source file is exceptionally easy…

#include "PlayingCard.h"
using namespace std;

void PlayingCard::setName(string name) {
    m_name = name;
}

void PlayingCard::setSuit(string suit) {
    m_suit = suit;
}

void PlayingCard::setFilename() {
    m_filename = "cards/"+m_name+" "+m_suit+".png";
}

void PlayingCard::setScore(int score) {
    m_score = score;
}

string PlayingCard::toString() {
    return (m_name + " of " + m_suit);
}

Nothing amazing there. The filename is used to load the image when it is played (ie “cards/three clubs”) and the toString() method is a bit of a holdover from the console application (i.e. “Jim receives the three of clubs”). I doubt if it’s actually used in the SDL game anywhere (and hence should be deleted, hmmm…)

So that’s all for the classes, next time up we’ll look at some game action (dealing cards and user playing), honest.

Read Full Post »

So once I decided that I was going to get back into my C++ programming after a few years off, I had a hunt round the internet for what people believed good starting programs/games were for beginners. Somewhere (on Stackoverflow if my memory serves me right) I found a post where a guy recommended making a Blackjack game as it would cover most elements that a beginner would need and left open the possibility of extension through expanding AI, graphics and the like.

So, with this idea in mind, I set to work building a blackjack game. I started off making a console application as I wanted to get my eye back in when it came to C++. Needless to say, after a week or so I’d perfected the game in text format (at least so I thought anyway…) and wanted a new challenge. This is where I came across SDL and so the fun began. I started following Lazy Foo’s tutorials, amazed at the potential of something that seemed so easy creating something that for years I had thought was beyond a mere mortal like myself. I raced to google to search for playing card images and found that Wikipedia has a whole pack of images on the CC licence, which I obviously helped myself to (a card game is no fun if you have to make 52 playing cards yourself in paint – my graphical skills are slim to none). And so to the code…

This is where things are going to look pretty horrible, but you have to realise this was my first attempt making a game in SDL and I hadn’t yet ventured into state machines and their potential (something which was rectified for project 2) or even properly into splitting code up into multiple files (it looks even worse now I look back at it a month or two later). So with all of that in mind, I’m going to start going through my code from this project and explain why I made certain decisions and how I designed certain parts of the game.

//Screen attributes
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 640;
const int SCREEN_BPP = 32;
//card positions
const int PLAYER_CARD_X = 50;
const int PLAYER_CARD_Y = 470;
const int DEALER_CARD_X = 350;
const int DEALER_CARD_Y = 50;
//number of players
const int PLAYERCOUNT = 6;
const int USER = 0;
const int THE_DEALER = PLAYERCOUNT - 1;

Player players[PLAYERCOUNT];
PlayingCard pack[52];

//The surfaces, font, color & event
SDL_Surface* screen = NULL;
SDL_Surface* backofcard = NULL;
SDL_Surface* playerinstructions = NULL;
TTF_Font* font = NULL;
TTF_Font* playerBust = NULL;
SDL_Color whiteText = {255,255,255};
SDL_Color blackText = {0,0,0};
SDL_Event event;

int main(int argc, char* args[]) {
    srand(time(0));
    bool quit = false, setup = false;

    if(!init())
        return 1;

    while(!quit) {
        if(!setup) {
            generatePack();
            setPlayerNames();
            SDL_Delay(500);
            dealStartingCards();

            setup = true;
        }

        while(SDL_PollEvent(&amp;event)) {
            user_plays();

            Uint8* keystates = SDL_GetKeyState(NULL);
            if(event.type == SDL_QUIT || (keystates[SDLK_LALT] &amp;&amp; keystates[SDLK_F4]))
                quit = true;
        }

        computer_plays();
        dealer_plays();

        findWinner();
    }

    clean_up();

    return 0;
}

The constants at the top are obvious, USER and THE_DEALER are there so I don’t have to have a lot of references to players[0] and players[playercount – 1] in the code, I can use players[USER] and players[THE_DEALER] to make things a lot easier to follow and understand. I now know the surfaces, fonts, colors and events should be hidden away so they’re not global variables but at the time it was the only way I could get my head round things.

Then we have the main program, containing the game loop which runs until the user quits the application. Not the most graceful, and it only allows one play through, but to someone who hadn’t done anything approaching a “game” before, this was cutting edge stuff. Each function does exactly what it says on the tin (I like my function names to be blindingly obvious, it makes reading and following code so much easier when you come back to it after a while away). The setup boolean that’s initialised at the start of main() is so that the game setup is only run the first time through the game loop, and not on every subsequent loop of the program. Within the computer_plays(), dealer_plays() and findWinner() functions there is a check so they don’t run before they are due, as we will see later.

So now I’ll quickly go through the setup functions to give a rough idea of what’s going on here:

init() just initializes everything: SDL; TTF; the main screen; the window caption; it opens any fonts, sets their styles and then paints the window green. All pretty basic stuff. It didn’t have to be squirelled away in a function, I’ve seen plenty of examples where all the initialization was in the main program, but I like to keep my main program clean so I banished the initialization away into the init() function.

generatePack() creates the pack of playing cards, giving each a suit, score, name and filename. It then shuffles the pack (like a dealer would). I now realise that the multiple for loops could be improved upon massively through the use of modulo, or even using nested for loops. The reason for the loops starting at 0, 13, 26 and 39 is because each card used to have an ID, but that got removed and the for loops never got updated. Horrible code I know, but it worked and I was happy.

void generatePack() {
    for(int a=0; a<13; a++) {
        pack[a].setSuit("spades");
        pack[a].setScore(a+1);
    }

    for(int b=13; b<26; b++) {
        pack[b].setSuit("hearts");
        pack[b].setScore(b+1-13);
    }

    for(int c=26; c<39; c++) {
        pack[c].setSuit("clubs");
        pack[c].setScore(c+1-26);
    }

    for(int d=39; d<52; d++) {
        pack[d].setSuit("diamonds");
        pack[d].setScore(d+1-39);
    }

    for(int e=0; e<52; e++) {
        switch(pack[e].getScore()) {
            case (1):
                pack[e].setName("ace");
                break;
            case (2):
                pack[e].setName("two");
                break;
            case (3):
                pack[e].setName("three");
                break;
            case (4):
                pack[e].setName("four");
                break;
            case (5):
                pack[e].setName("five");
                break;
            case (6):
                pack[e].setName("six");
                break;
            case (7):
                pack[e].setName("seven");
                break;
            case (8):
                pack[e].setName("eight");
                break;
            case (9):
                pack[e].setName("nine");
                break;
            case (10):
                pack[e].setName("ten");
                break;
            case (11):
                pack[e].setName("jack");
                break;
            case (12):
                pack[e].setName("queen");
                break;
            case (13):
                pack[e].setName("king");
                break;
        }
        if(pack[e].getScore() > 10)
            pack[e].setScore(10);
        else if (pack[e].getScore() == 1)
            pack[e].setScore(11);

        pack[e].setFilename();
    }

    random_shuffle(pack, pack+52);
}

setPlayerNames() does what it says on the tin (I told you these functions did). It sets the player’s name as You (so you see You underneath your cards…), the dealer name is set and the CPU names are chosen from an randomly shuffled array of 30. It then draws the player’s names onto the screen, using the conveniently titled drawPlayerNames()

void setPlayerNames() {
    string plnm, nameslist[30] = {"Dave", "Tim", "Gina", "Fred", "Bill", "Alan", "Robert", "Elaine", "Jane", "Richard", "Linda",
                                    "Holly", "Freya", "Craig", "Lionel", "Bob", "Jenny", "Andrew", "Tony", "Sarah", "Peter",
                                    "Danny", "Stephen", "Darren", "Alex", "Charles", "Gerald", "Scott", "Tracy", "Victoria"};

    players[USER].setName("You");
    players[USER].setNameSurface(font, whiteText);

    players[THE_DEALER].setName("The Dealer");
    players[THE_DEALER].setNameSurface(font, whiteText);

    random_shuffle(nameslist, nameslist+30);

    //start at 1 because 0 is the user, and end before plct-1 because that's the dealer
    for(int i=1; i<THE_DEALER; i++) {
        players[i].setName(nameslist[i]);
        players[i].setNameSurface(font, whiteText);
    }

    drawPlayerNames();
}

void drawPlayerNames() {
    apply_surface(PLAYER_CARD_X+20, PLAYER_CARD_Y+130, players[USER].getNameSurface(), screen);

    SDL_Surface* sName = players[THE_DEALER].getNameSurface();
    apply_surface(DEALER_CARD_X+int((300-sName->w)/2), DEALER_CARD_Y-40, players[THE_DEALER].getNameSurface(), screen);

    for(int i=1; i<THE_DEALER; i++) {
        SDL_Surface* sName = players[i].getNameSurface();
        apply_surface(PLAYER_CARD_X+(i*200)+int((100-sName->w)/2), PLAYER_CARD_Y+130, players[i].getNameSurface(), screen);
    }

    SDL_Flip(screen);
}

drawPlayerNames() places the users name first and then draws the dealer, finally using a for loop to position each player’s name, centring it on the player’s cards

I’ll leave this post here because this is getting a little bit too long and the more interesting parts of the game are coming up.

Read Full Post »

So in my limited time developing SDL and C++ applications I’ve come across a few sites that I consider to be a godsend to help me along my way:

Learn CPP – the best C++ tutorial I’ve found on the internet. It covers absolutely everything – classes, inheritance, templating, polymorphism, pointers, functions – the whole shebang. Excellent site.

CPlusPlus.com – it’s got a good tutorial, but more importantly, it’s got the C++ reference documentation, which is invaluable to have if you want to know what’s already possible and programmed for you

Lazy Foo Productions – a goldmine for SDL code, with working, up to date examples and excellent explanations on how the code works. Covering everything from your first SDL application through to collision detection and even multi-threading. I cannot rate this site highly enough

The SDL documentation – a cliche, but if you want to do anything in SDL, you’re going to have to use the SDL docs at some point, so you might as well bookmark it now. It includes SDL_ttf and SDL_image docs as well, which will come in useful for text and images that aren’t bmps (so every image then…)

Google – yeah, you might have heard of this one, so shoot me. If ever I get stuck, my first port of call is google. Chances are someone else has had your problem and asked it, and someone has provided a perfectly good answer.

Stackoverflow, Dream in code – two sites that I’ve stumbled upon through my endless google searches and always give a more than adequate answer to my current conundrum

Without these sites, I wouldn’t have put together half of what I currently have.

Read Full Post »

So this blog is going again, and this time it’s going to be about my adventures with C++ and SDL with a possible progression further down the line into OpenGL (dependent on up to date books and/or web tutorials becoming available). I’ve been working on SDL for a month or two now and have a couple of projects to show for it: a blackjack clone, used to get my eye in to SDL; Pong Clone 3000, my first attempt at animation, collision detection and state machines and my current project, Brick Buster, a breakout clone with all the trimmings.

I’ll go into further detail on each project in later posts, but for now, here’s some obligatory screenshots:

Read Full Post »