1

I am working on a program that is supposed to read in a file (each line in the file represents a hand of 13 cards) and evaluate each bridge hand.

I will ask my specific questions at the end, but because there is a good bit to this program I am going to include all the instructions so you get an idea of what is required.

Here is the text file that will be read in:

2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH
3C 4C 2D AC QC 7S 7C TD 9C 4D KS 8D 6C
2C 3C KC JC 4C 8C 7C QC AC 5C 9C 6C TC
5H 3S 4D KC 9S 3D 4S 8H JC TC 8S 2S 4C
2S 5D 6S 8S 9D 3C 2H TH
2H 6D %S 8S 7S 4D 3H 4S KS QH JH 5C 9S
2C QD TC AD 6C 3D TD 3C 5H 7H AS JH KD QS
2C QD TC AD 6C 3D TD 2C 5D 7H AS JH KD
2H 6D TS 8Z 7S 4D 3H 4S KS QD JH 5C 9S

With each pair representing a card (the value and the suit).

Legal values include:

2-9
T(10), A(Ace), K(King), Q(Queen), and J(Jack)

And suits:

C(Clubs), S(Spades), D(Diamonds), and H(Hearts)

Once the file is read in, each hand must be sorted first by suit and then by the rank within the suit (aces are high). When the sorting is complete, each hand must be evaluated using the following rules:

Aces = 4
Kings = 3
Queens = 2
Jacks = 1
voids (no cards in a suit) = 3
singletons (one card in a suit) = 2
doubletons (two cards in a suit) = 1
long suits (more than 5 cards in a suit) = 1 count for each card over 5 in number

After being evaluated, each hand should be displayed in the following format:

Example Input:
2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH

Example Output:
Clubs    10  6  2
Diamonds A   Q  10  3
Hearts   K   J  7   5  3
Spades   A
Points = 16

Here are a few specifics about what the program must include:

1. A data structure to hold cards in an ordered manner.
2. A function to read in the hand.
3. A function to evaluate the hand (with support functions).
4. A function to display the hand.

Here is what little code I've been able to come up with. In case it's not clear, the comments are steps I think will need to be done in order for the program to work properly. Right now all it does is open the file, and yes I will be removing the "File is Open" message, I just wanted to be sure the file was actually being open.

//#include <program3.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

int main() {
    //Create Array
    //char bridgeHands[];
    //Open file, if it doesn't exist, exit the program
    ifstream bridgeFile;
    bridgeFile.open("prog3.dat");
    if(!bridgeFile) {
        cerr << "Open Failure" << endl;
        exit(1);
    }
    else {
        //Read file into array
        //Sort array
        //Evaluate hands
        //Display hands
        cout << "File is open" << endl;
    }

    return 0;
}

I guess my specific question at the moment is this. How do I need to go about creating and loading the array? I have never worked with loading an array from input that is in pairs. Also, how does this work with the data structure?

I'm sure you can tell by now that I'm extremely new at this and am learning as I go (pretty much everything I know how to do in C++ is written in that code), so any help is greatly appreciated. Thank you for your time.

  • 1
    You'll need a data structure to represent a card (a struct will be fine). Define an array of these (use `std::vector` if you're allowed to). Write a translation function from a two-character sequence to the card type. In a loop, read two-character "words", translate each one to a card, and store it in the array. And don't use `eof`. – molbdnilo Oct 07 '14 at 08:56
  • Sounds good, but I don't know how to do any of that :-/ –  Oct 07 '14 at 08:59
  • In *this* case you will find help [here](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – TobiMcNamobi Oct 07 '14 at 09:06
  • Well, you'll just have to learn ;-) Since you didn't make this up yourself, I suspect it's an exercise from a book. If it is, everything you need should already have been covered. Perhaps you should go back a bit, read some more, and do some smaller exercises first. (If that code covers everything you know, you definitely should.) If you don't have a book, you should get one. – molbdnilo Oct 07 '14 at 09:08
  • @molbdnilo Actually, this is one case where there probably isn't much advantage in using `std::vector`. If you can use C++11, `std::array` would seem more appropriate; in pre-C++11, this is one of the rare cases where I would probably use `Card hand[13];` – James Kanze Oct 07 '14 at 09:11
  • @molbdnilo Under what the code must contain, he actually lists "A data structure to hold cards in an organized manner". This suggests that the author of the problem _is_ looking for a `struct Hand` (which would contain `struct Card`). Which implies that he has seen `struct` or `class` before. – James Kanze Oct 07 '14 at 09:14
  • @molbdnilo, I wish this was from a book, those are normally much easier than these that our professor comes up with. Thanks for all the input, looks like I've got a long night ahead of me haha –  Oct 07 '14 at 09:29
  • It looks to me like some sort of final test, since it requires knowledge of a number of different aspects. The fact that he does have errors in the input, and that he requires data structures and separate functions suggests that the course is actually fairly good. And @BethTanner you shouldn't have waited until the last night. (I could probably implement the assignment in a couple of hours, but I've been programming in C++ for close to 30 years. I would expect a couple of days, or even a week from someone without much C++ experience.) – James Kanze Oct 07 '14 at 16:14

2 Answers2

1

There are a lot of open questions in my mind. First and foremost: should each hand be on a separate line, or is it just the next 13 cards, regardless of line breaks. This changes the way you read hands. In either case, your input file has errors which must be detected: in the first case, the fifth and seventh lines have an incorrect format, and in the second, the number of cards isn't a multiple of 13, so there must be an error somewhere.

Anyway, the proper way to approach this is to define types (classes) for the cards and the hand, and defined a user defined operator>> for each, with the operator>> for hands using the one for cards. For example:

std::istream& operator>>( std::istream& source, Card& object)
{
    char value;
    char suit;
    source >> std::ws;  //  skip leading whitespace.
    source.get(value);  //  these do _not_ skip whitespace
    source.get(suit);
    if ( source ) {     //  no errors on input...
        //  Map the characters to the internal representation and write
        //  them into `object`.  This operator may have to be a friend
        //  to do this.
        //
        //  If one of the characters isn't legal:
        //      source.setstate( std::ios_base::failbit );
    }
    return source;
}

For the hand, if your input is line oriented, the best solution is probably to use std::getline to read the line, then std::istringstream to parse it. Make sure you check that there is nothing but white space left once you've read the 13 cards. (If the input ignores line endings, then just reading 13 cards should be sufficient.) Regardless of the strategy, be sure the check for errors after each read, before using the values you've read. So you're loop (either on the std::istringstream or the original source, depending) might look something like:

int i = 0;
while ( i != 13 && source >> dest[i] ) {
    ++ i;
}
if ( i == 13 ) {
    //  Input of 13 cards succeeded...
}

Finally: You're input contains errors (probably intentionally, to ensure that you test them correctly). Which means the simplest form of the outer loop won't work correctly (since it will stop at the first error). If we suppose line oriented input globally, but the >> operator for hand ignores line endings and just looks for the next 13 Card:

std::string line;
int lineNumber = 0;
while ( std::getline( source, line ) ) {
    ++ lineNumber;
    std::istringstream parser( line );
    if ( parser >> hand >> std::ws && parser.get() == EOF) {
        //  Line is good, hand contains the instance to be evaluated
    } else {
        std::cerr << "Input format error in line " << lineNumber << std::endl;
    }
}

Concering the condition in the if: we read a hand, then skip white space; if that succeeds, we verify that we have reached end of file (otherwise, there's extra garbage at the end of the line). You can give a more detailed error message if you separate these different operations out, although to indicate which card is in error, you'll have to input the 13 cards directly at this level, rather than using the >> for Hand.

One other suggestion: I would choose an internal representation which made processing simple, with mapping functions for input and output. This is probably two enums: one for values (with the values in the order of their ranking), one for suits (also in the order of their ranking). This will make the sorting and the counting significantly easier, and mapping functions are very easy: for small sets like these, nothing more than an array with the legal representations: on input, a linear search, and on output, just index. (Note that this may result in the value 2 having the numeric value 0 internally, since it will be the first value of the enum.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Yes you're correct, I forgot to mention that we are also supposed to incorporate error checking so our professor threw in some lines with some invalid input (too many cards, too few cards, and invalid suits or values). –  Oct 07 '14 at 09:28
  • @BethTanner Which is a good idea; input errors are inevitable in the real world, and real programs have to be able to handle them. – James Kanze Oct 07 '14 at 16:10
0

Use the ifstream::getline() function for reading and parsing the file. At this link you also find a nice example how you might read the file directly into a std::array:

#include <iostream>
#include <sstream>
#include <vector>
#include <array>

int main()
{
    std::istringstream input("abc|def|gh");
    std::vector<std::array<char, 4>> v;

    // note: the following loop terminates when std::ios_base::operator bool()
    // on the stream returned from getline() returns false
    for (std::array<char, 4> a; input.getline(&a[0], 4, '|'); ) {
        v.push_back(a);
    }

    for (auto& a : v) {
        std::cout << &a[0] << '\n';
    }
}

But take a close look whether this is suitable for your case. As an alternative you could omit the last parameter of getline so you really get the lines one by one. Then you'd have to parse these lines using std::string::find() and std::string::substr().

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52