Jump to content


Black Jack in C++


13 replies to this topic

#1 Optiplexicide

    New Member

  • Members
  • Pip
  • 5 posts

Posted 29 October 2006 - 11:12 PM

Hey. What am I doing wrong with this code for Black Jack?

card.h

enum suit {clubs, diamonds, hearts, spades};


class Card {


public:

    Card(suit = clubs, int = 1);

    suit su() {return f; }

    int value() {return v; }

    void write_out();


private:

    suit f;

    int v;

    int s;

};


card.cpp


#include "card.h"

#include <cassert>

#include <iostream>


using namespace std;


Card::Card(suit ss, int vv)

{

    assert(vv >=1 && vv<=13);

    s=ss;

    v=vv;

}

void Card::write_out()

{

    const char *tab1[] = {"Clubs", "Diamonds", "Hearts", "Spades"};

    const char *tab2[] = {"Jack", "Queen", "King"};

    cout << tab1[ s ] << ' ';

    if (v == 1)

        cout << "Ace";

    else if (v <= 10)

        cout << v;

    else

        cout << tab2[v-11];

}


cardstack.h

#include "card.h"

#include <vector>


using namespace std;


class Cardstack {

public:

    Cardstack() {stack.reserve(52);}

    void throw_cards() {stack.clear();}

    int number_cards() { return stack.size();}

    Card look_at(int no);

    Card deal_top();

    void lay_top(Card k);

    void new_pack();

    void shuffle();

private:

    vector<Card> stack;

   

};


cardstack.cpp

#include "cardstack.h"

#include <iostream>

#include <cstdlib>

#include <ctime>


using namespace std;


Card Cardstack::look_at(int no)

{

    return stack.at(stack.size() -no);

}

Card Cardstack::deal_top()

{

    Card top = stack.back();

    stack.pop_back();

    return top;

}

void Cardstack::lay_top(Card k)

{

    stack.push_back(k);

}

void Cardstack::new_pack()

{

    stack.clear();

    for (suit s=clubs; s<spades; s=suit(s+1))

        for (int v=1; v<=13; v++)

            stack.push_back(Card(s,v));

}

void Cardstack::shuffle()

{

    srand(time(0));

    for (int i=1; i<1000; i++)

    {

        int n1 = rand() % stack.size();

        int n2 = rand() % stack.size();

        Card temp = stack[n1];

        stack[n1] = stack[n2];

        stack[n2] = temp;

    }

}


player.h

#include "cardstack.h"

class Player {


public:

    Player(Cardstack& cardpack, bool is_computer)

        : pack(cardpack), computer(is_computer) {};

    int play();

private:

    Cardstack hand;

    Cardstack& pack;

    const bool computer;

    int points();

};



player.cpp

#include "player.h"

#include <iostream>


using namespace std;


main()

{

    Cardstack pack;

    Player you (pack, false);

    Player I (pack, true);

    char answer[10];

    cout << "Welcome to Black Jack, the game of twenty-one!" << endl;


    while (true)

    {

        cout << "New game? "; cin >> answer;

        if (answer[0] != 'y') break;

        pack.new_pack();

        pack.shuffle();

        int p1 = you.play();

        if (p1 > 21)

            cout << "Sorry, you busted.  You lose." << endl;

        else if (p1 == 21)

            cout << "You Win!" << endl;

        else

        {

            //the computer must play

            int p2 = I.play();

            if (p2 <=21 && p2 >=p1)

                cout << "You lost!" << endl;

            else

                cout << "You won!" << endl;

        }

    }

   

}


points.cpp

#include "player.h"


#include "card.h"


#include <iostream>


using namespace std;


int Player::points()

{

    int p = 0, number_aces = 0;


    for (int i = 1; i <= hand.number_cards(); i++)

    {

        int v = hand.look_at(i).value();

        if (v == 1) {

            p+= 14;

            number_aces++;

        }

        else

            p += v;

    }

    for (int j=1; j <= number_aces && p > 21; j++)

        p -= 13; // counts an ace as 1

    return p;

}


play.cpp

#include "points.cpp"



#include <iostream>


using namespace std;


int Player::play()

{

    bool contine = true;

    int p;

    while (contine)

    {

        Card k = pack.deal_top();

        hand.lay_top(k);

        p = points();

        if (computer) {

            cout << "The computer got "; k.write_out();

            cout << endl;


                if (p >= 16)

                {

                    cout << "The computer has " << p << " points" << endl;

                    contine = false;

                }

        }

        else

        { //Person

            cout << "You got "; k.write_out();

            cout << " and have " << p << " points" << endl;

            if (p < 21)

            {

                char answer[10];

                cout << "One more card? Indicate 'y' for yes or 'n' for no "; cin >> answer; // this needs to be fixed

                contine = answer[0] == 'y';

            }

            else

                contine = false;

        }

    }

    hand.throw_cards();

    return p;

}


Any help will be appriciated.

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 5306 posts
  • LocationBellevue, WA

Posted 30 October 2006 - 12:19 AM

Umm, that's a huge amount of code! You don't expect us to read through all that and try to guess what the error is that you're looking for, do you? You need to be a lot more specific. Just asking "what is wrong with this" is not enough; are you talking about a compilation error or does the game compile but not work correctly (and if so, what are the precise symptoms of the problem)? Also, you need to do some work yourself to narrow down the location of the bug, as we are never going to read the amount of code that you just posted. Get it down to a segment of 30 lines or less and we'll try to help.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 jmgk

    New Member

  • Members
  • Pip
  • 5 posts

Posted 30 October 2006 - 12:35 AM

hi,

in card.h, the constructor "Card(suit = clubs, int = 1);" seens wrong

suit = clubs? int = 1? default params dont need a variable name?

jmgk

#4 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 October 2006 - 01:21 AM

jmgk: You indeed don't have to specify variables names when using default parameters, so his code is correct.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#5 Optiplexicide

    New Member

  • Members
  • Pip
  • 5 posts

Posted 30 October 2006 - 01:56 AM

I did fix that, but I am getting these errors:

card.h(1) : error C2011: 'suit' : 'enum' type redefinition

card.h(3) : error C2011: 'Card' : 'class' type redefinition

Error executing cl.exe.

play.exe - 2 error(s), 0 warning(s)

#6 Optiplexicide

    New Member

  • Members
  • Pip
  • 5 posts

Posted 30 October 2006 - 02:03 AM

I also deleted #include "card.h" from points.cpp, and now I'm getting:

play.obj : error LNK2001: unresolved external symbol "public: class Card __thiscall Cardstack::look_at(int)" (?look_at@Cardstack@@QAE?AVCard@@H@Z)

play.obj : error LNK2001: unresolved external symbol "public: void __thiscall Card::write_out(void)" (?write_out@Card@@QAEXXZ)

play.obj : error LNK2001: unresolved external symbol "public: void __thiscall Cardstack::lay_top(class Card)" (?lay_top@Cardstack@@QAEXVCard@@@Z)

play.obj : error LNK2001: unresolved external symbol "public: class Card __thiscall Cardstack::deal_top(void)" (?deal_top@Cardstack@@QAE?AVCard@@XZ)

LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main

Debug/play.exe : fatal error LNK1120: 5 unresolved externals

Error executing link.exe.

---------
I get these alot when trying to experiment on what to do.

#7 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 30 October 2006 - 03:15 AM

When you include header files in more than one source file, the contents are actually declared again if you don't take measures to prevent it. There are two ways to so. The first is to wrap your entire header in a ifndef/endif block like this:


#ifndef __CARD_H_

#define __CARD_H_

enum suit {clubs, diamonds, hearts, spades};


class Card {


public:

    Card(suit = clubs, int = 1);

    suit su() {return f; }

    int value() {return v; }

    void write_out();


private:

    suit f;

    int v;

    int s;

};

#endif // __CARD_H_


or the more elegant solution:

#pragma once


enum suit {clubs, diamonds, hearts, spades};


class Card {


public:

    Card(suit = clubs, int = 1);

    suit su() {return f; }

    int value() {return v; }

    void write_out();


private:

    suit f;

    int v;

    int s;

};


"Stupid bug! You go squish now!!" - Homer Simpson

#8 Optiplexicide

    New Member

  • Members
  • Pip
  • 5 posts

Posted 30 October 2006 - 03:43 AM

Kenneth Gorking said:

When you include header files in more than one source file, the contents are actually declared again if you don't take measures to prevent it. There are two ways to so. The first is to wrap your entire header in a ifndef/endif block like this:


#ifndef __CARD_H_

#define __CARD_H_

enum suit {clubs, diamonds, hearts, spades};


class Card {


public:

    Card(suit = clubs, int = 1);

    suit su() {return f; }

    int value() {return v; }

    void write_out();


private:

    suit f;

    int v;

    int s;

};

#endif // __CARD_H_


or the more elegant solution:

#pragma once


enum suit {clubs, diamonds, hearts, spades};


class Card {


public:

    Card(suit = clubs, int = 1);

    suit su() {return f; }

    int value() {return v; }

    void write_out();


private:

    suit f;

    int v;

    int s;

};


Didn't work...

#9 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 30 October 2006 - 04:51 AM

This probably has not much to do with the original problem but..:whistle: I would do

struct suit { enum type {clubs,diamonds,hearts,spaders}; };

instead of

enum suit {clubs, diamonds, hearts, spades};

to keep enums nicely in their own namespaces.

#10 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 October 2006 - 10:28 AM

Then use a namespace instead of a struct, unless you really encapsulate the enum in the struct (so you would use just suit instead of suit::type)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#11 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 30 October 2006 - 12:47 PM

.oisyn said:

Then use a namespace instead of a struct, unless you really encapsulate the enum in the struct (so you would use just suit instead of suit::type)
Well that doesn't work for enums defined inside a class (which is most of the time, actually I hardly ever declare global enums) so I don't really see the benefits of using namespaces and rather choose a convention which works for the both cases instead.

class IBuffer {
   public:
   struct PrimitiveType {
      enum type {
         Triangle=0,
	 Line=1,
      };
   };

   ...methods..
};


#12 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 October 2006 - 01:44 PM

I see your point, but I still think you have to prevent people for using IBuffer::PrimitiveType as a type -or- make sure you can use that type instead of IBuffer::PrimitiveType::type.

#define IMPLEMENT_CLASS_ENUM(classtype, enumtype) \
    public:\
    classtype::classtype() : m_value() { }\
    classtype::classtype(enumtype v) : m_value(v) { }\
    operator enumtype() { return m_value; }\
    private:\
    enumtype m_value;

class IBuffer
{
public:
    struct PrimitiveType
    {
        enum type { Triangle, Line };
        IMPLEMENT_CLASS_ENUM(PrimitiveType, type)
    };
};

IBuffer::PrimitiveType t = IBuffer::PrimitiveType::Triangle;

C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#13 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 30 October 2006 - 06:45 PM

Optiplexicide said:

Didn't work...

Do you have #include "card.h" in play.cpp?

Also, including source-files in other source-files is considered bad practice. If it is done more than once, the compiler will complain about the function body being defined more than once.
"Stupid bug! You go squish now!!" - Homer Simpson

#14 Optiplexicide

    New Member

  • Members
  • Pip
  • 5 posts

Posted 31 October 2006 - 02:42 AM

Kenneth Gorking said:

Do you have #include "card.h" in play.cpp?

Also, including source-files in other source-files is considered bad practice. If it is done more than once, the compiler will complain about the function body being defined more than once.


I did that, but I still get the same mess of errors.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users