Evaluating Bridge Hands in C++ - c++

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.

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.)

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().

Related

EDITED How do I split it up into char and int arrays

I don't know what happened to the post but it must have happened on my first edit.
I got this info to go in and work when it was in separate files. But I need it on one file.
I have looked in my text book and a bunch of other places but I can not find how to only get the text or characters out of the file.
I have got all the info into a single array but it looks like I will need to pull out each group piece by piece and put it where I want it but that looks slow, tedious and very susceptible to errors.
Johnson 85 83 77 91 76
Aniston 80 90 95 93 48
Cooper 78 81 11 90 73
Gupta 92 83 30 69 87
Blair 23 45 96 38 59
Clark 60 85 45 39 67
Kennedy 77 31 52 74 83
Bronson 93 94 89 77 97
Sunny 79 85 28 93 82
Smith 85 72 49 75 63
If this looks familiar it is the same assignment as my previous post, now I just need to figure out how to parse this info and use it again.
Chances are, you'll need to take your input as string value and check through it to find the beginning of the numerical characters.
only after having separated the alphabetical part of the input string from it's numerical part, you start creating your target arrays.
this might help: How can I check if a string has special characters in C++ effectively?
/e: wording
There are multiple ways of doing it. You could read it into a string and process it manually based on spaces. Or you could use stringstream to extract numerical values into array/vector. That however, still requires you to remove the name before you do it.
Here is a little code that reads the file content into an unordered_map which is essentially a dictionary as defined in other languages.
void read_file(const std::string& path) {
std::ifstream in(path); // file stream to read file
std::unordered_map<std::string, std::vector<double>> map;
/*
* map structure to hold data, you do not have to use this.
* I am using it only for demonstration purposes.
* map takes string (name) as KEY and vector<double> as VALUE
* so given a NAME you can get the corresponding grades VECTOR
* i.e.: map["Johnson"] --> [85, 83, 77, 91, 76]
*/
std::string line;
while (std::getline(in, line)) { // read entire line
if (line == "") continue; // ignore empty lines
int last_alpha_idx = 0; // name ends when last alphabetic is encountered
for (size_t i = 0; i < line.size(); i++)
if (std::isalpha(line[i])) last_alpha_idx = i; // get index of last alpha
std::string name = line.substr(0, last_alpha_idx + 1); // name is from index 0 to last_alpha_idx inclusive (hence +1)
std::string numbers = line.substr(last_alpha_idx + 1); // array values the rest of the line after the name
std::stringstream ss(numbers); // this is an easy way to convert whitespace delimated string to array of numbers
double value;
while (ss >> value) // each iteration stops after whitespace is encountered
map[name].push_back(value);
}
}
You could read it into an array, the code will not change dramatically. I chose string as KEY and vector<double> as VALUE to form KEY/VALUE pairs for the dictionary (map).
As you can see in the code, it looks for the last alphabetic character in each line and takes its index to extract the name from the read line. Then it takes the rest of the string (just the numbers) and puts them into a stringstream which will extract each number individually in its inner loop.
Note: the code above supports having full names (e.g. "Johnson Smith 85 83 77 91 76").

Input file not displaying properly because of ¤ ¬ (currency symbol)

So I've been using this input file and beating myself up because no matter what I did, it would not display properly for me. I did figure out why it was happening but I want to know what the ¤ (Currency symbol) next to the EOL (End of line) ¬ means, and why it isn't easy to add or remove them (¤). I have never came across this problem or seen anything posted about it, primarly because I don't know what the ¤ really is, if I had to take a guess I'd say it is some sort of linebreak or newline. Maybe this is one of those nice Apple bugs in xCode.
Update 3
Input function:
while (!mDataFile.eof())
{
MLSInfo temp;
//mDataFile >> temp.mlsId;
mDataFile >> temp.mlsId;
mDataFile >> temp.salePrice;
mDataFile.ignore();
getline(mDataFile, temp.address);
if (!mDataFile.eof()){
list.listInsertEnd(temp);
}
}
Output function:
DListNode* tempPtr = list.getHead();
while (tempPtr != nullptr) {
MLSInfo temp = tempPtr->dataVal ;
cout << temp.mlsId << " " << temp.salePrice << " " << temp.address << "";
tempPtr = tempPtr->nextNodePtr;
}
Update 2:
Download link of broken file.
https://ufile.io/jzyxx
Update 1:
STRING:
00111 75000.00 123 Main Street¤¬
HEX:
30 30 31 31 31 20 37 35 30 30 30 2e 30 30 20 31
32 33 20 4d 61 69 6e 20 53 74 72 65 65 74 0d 0a
UNICODE CODE FOR ¤:
U+00A4
What the output looked like
To fix the problem I just removed all the ¤'s by retyping the input file.
It looks like those light gray Unicode characters are Xcode's way of displaying whitespace characters. The dots are spaces and the arrows are newlines, so I'd guess your currency symbols are carriage returns.
Looking at your hexdump, we see character 0x0d. Comparing with an ascii table confirms that it is indeed a carriage return.
Since the default delimiter for getline is a newline*, this carriage return is going straight into your string! This will affect things when you then try to output that string later. (without seeing your code, it's hard to say why it's printing the way it is)
How these could've gotten into your file is a mystery without more information (was the file on a windows machine at some point?), but simply removing them from the file should be enough to solve your problems.
*Realized I'm assuming the use of getline here because if you were using cin with the >> operator, it would stop at any whitespace, including carriage returns.

2D array of structs

Description:
This program will read a data file that will contain students’ scores for 5
Computer Science (CS) tests. Each student record will contain his/her last name, first
name, numeric student ID, student username, 5 test scores, average for these five scores,
and grade for CS course. The file will be processed by the program and will produce a
report.
The report contains seven parts:
The students’ usernames
The average score for each student’s five tests.
The grade for each student’s CS course.
The average CS scores for the class.
The total number of students with grades A, B, C, D, and F.
The sorted student names and IDs based on user choice: descending or ascending order
of grades.
Search a student grade by entering student user name ID.
(See the attached sample input and output for example.)
Specifications:
All input data will be in an input file. The name of the data file that will be used
for your program MUST BE grade.dat. You may need to create your own
version of grade.dat to test your program before you turn it in. You must also
submit a printout of the contents of this test file when you submit this project.
The student user name is generated by the students’ names and ID numbers
according to the following rule. The student user name will be the first two initials
and the four digits of the student ID number. For example, for student John (last
name), Doe (first name), with ID number 1122, his user name would be: jd1122.
The user names are all lower cases.
Your program needs to use a structure to store the student information including
last name, first name numeric student ID, student user name, 5 test scores,
average for these five scores, and grade for CS course.
Your program needs to write at least three functions in your program: For
example: find average, find grade, search score etc.
2
Your program needs to do input checking: (1) correct input choices for sorting:
ascending or descending; and (2) correct format for user ids: first two are letters
and last four are digits.
Requirements for Submission:
You must
submit your C++ source code, which is .cpp file through myClasses#SU, and hand
in:
Design/pseudocode/hierarchy chart. of your algorithm.
A printed copy of your source code.
Your set of sample input data files
Your set of sample screen outputs corresponding to each input data file.
Program report: state clearly if your program doesn’t work well. What’s the
problem? Or anything you want me to know, for example, you get the help from
other students. You can not copy others’ work. It is individual program
assignment!
You will need to be ready to demo your project and ready to answer any questions
related with the project.
Grading Rubric:
Algorithm design/Pseudocode 10
Correct outputs 20
Structures 20
Functions 10
File reading 10
Input checking 10
Searching and sorting 10
Readability/comments of program (including project report) 10
3
Sample Run
Sample Input File grade.dat contains:
Bonebrake Nicole 1111 90 85 50 78 85
Boyer Dennie 2222 100 90 99 89 88
Bozick Julia 3333 52 85 44 66 87
Carroll Sandra 4444 87 88 95 85 100
Creighton Sarah 5555 91 55 80 88 75
Everett Terry 6666 60 70 59 79 89
Freeman Andrew 7777 92 95 94 96 97
Fugett Brandon 8888 77 88 75 95 80
Here is what I have so far:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct StudentData
{
string first;
string last;
int studentID;
int exam1;
int exam2;
int exam3;
int exam4;
int exam5;
};
int main()
{
ifstream file;
file.open("grade.dat");
StudentData students[8][8];
file.close();
}
I am having trouble bringing the data out of the file and putting it into an array.
What about the following:
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;
struct StudentData
{
string first;
string last;
int studentID;
int exam1;
int exam2;
int exam3;
int exam4;
int exam5;
};
int main()
{
ifstream file;
string temp;
char fname[100];
char lname[100];
file.open("grade.dat");
StudentData students[8];
for (int i = 0; i<8; i++)
{
getline(file, temp);
sscanf(temp.c_str(),"%s %s %d %d %d %d %d %d", fname,lname,&students[i].studentID
,&students[i].exam1,&students[i].exam2,
&students[i].exam3,&students[i].exam4,&students[i].exam5);
students[i].first = fname;
students[i].last =lname;
}
file.close();
for (int i = 0; i<8; i++)
{
cout<<students[i].first<<" "<<students[i].last<<" "<<students[i].studentID
<<" "<< students[i].exam1<<" "<< students[i].exam2<<" "<< students[i].exam3
<<" "<< students[i].exam4<<" "<< students[i].exam5<<endl;
}
}
OK, since all you need is a head start, here are some hints:
First of all, your assignment says nothing about 2d arrays, if you are doing it for extra credit or something - don't, it is not mandated neither by your assignment, nor by the actual task at hand.
Second - your problem at hand - reading the file. You should start by reading it one line at a time. The line contains the names, the id and the grades. Once you have the line, you can extract the fields, by say splitting the string at the spacebars. This will give you a string for every field. Then you can assign the student name fields to the strings. There is also a function that converts strings to integers - that will come in handy for the rest of the fields.
Just to make things look a little more professional, you might want to consider using a container class instead of a plain array, std::vector is a good candidate. Also, if you are going to be sorting, you might want to use pointers (preferably smart pointers) and dynamic memory allocation. Also, in order to be able to sort your array, you will need to write functions which compare students based on different criteria. A container will give you an easy way to add and remove students, instead of some stiff static construct.
Every step of the way is already covered in answers here on SO, so all you need to get things done is search whenever you get stuck.

C++ Reading Through File

If I open a new file for input, and I call input >> listSize; outside of a while loop and then continue calling input >> anothervariable will it automatically progress through the file or will it read the first line again?
Example:
input >> listSize;
BaseStudent* studentlist = new BaseStudent[listSize.atoi()];
while (!input.eof())
{
input >> anothervariable; // I want this to start on the second line, not the first
}
The input file looks like this and we can code to the pattern (ignore the extra blank lines):
12
Bunny, Bugs
Math 90 86 80 95 100 99 96 93
Schmuckatelli, Joe
History 88 75 90
Dipwart, Marvin
English 95 76 72 88
Crack Corn, Jimmy
Math 44 58 23 76 50 59 77 68
Kirk, James T.
English 40 100 68 88
Lewinsky, Monica
History 60 72 78
Nixon, Richard
English 35 99 70 70
Lincoln, Abraham
History 59 71 75
Clinton, William
Math 43 55 25 76 50 58 65
Duck, Donald
English 34 100 65 65
Duck, Daffy
History 55 70 70
Bush, George
Math 44 54 29 75 50 55 60
When you read from a stream the data is used.
Next time will read from the last point read "up-to"
Though stream operator >> will skip proceeding white space but will not read the white space after the object (which can be a pain if your input is line orientated, as you don't see new lines easily).
The best way to get around this is to explicitly read lines at a time then parse the line:
std::string line;
std::getline(input, line); // Read a line
// Now look what is in the line.
std::stringstream linestream(line);
linestream >> dataItem1 >> dataItem2 >> dataItem3; /// etc.
Also note:
// This is not good. If there are any errors in your file it will loop forever.
while (!input.eof())
It is normal to loop over the input using:
while(input >> data)
{
// If you get in here then the read worked.
}
// You will fall out of the loop when
// 1) EOF
// 2) There is an error.
So if we combine both techniques:
std::string line1; // Name,
std::string line2; // Scores
std::string empty; // Empty line in data file.
while(std::getline(input, line1) && std::getline(line, empty) && std::getline(input, line2))
{
// line1 => Bunny, Bugs
// empty =>
// line2 => Math 90 86 80 95 100 99 96 93
// Enter the loop only if we get a name and a score line
std::getline(line, empty); // Optionally read a trailing line (there may not be a last one);
// dataStream => Math 90 86 80 95 100 99 96 93
std::stringstream dataStream(line2);
std::string subject;
int score;
dataStream >> subject; // subject => Math
while(dataStream >> score)
{
// We read another score from the data line;
// score is 90 first time 86 second time etc.
}
}
Others have already pointed out that the answer to your question is "yes", so I won't worry about that part.
As for the rest, I think I'd write it a bit differently. Your file obviously represents structured data, and I'd write the code to reflect that fact reasonably directly. I'd start by defining a structure reflecting the data in the file:
struct test_scores { // maybe not tests. Change if appropriate
std::string student;
std::string course;
std::vector<int>
};
Then, I'd write a function to read one of those items from the file:
std::istream &operator>>(std::istream &is, test_scores &ts) {
// for the moment assuming there are no blank lines in the file.
std::getline(is, ts.student);
std::string temp;
std::istringstream buffer(temp);
buffer >> ts.course;
int score;
while (buffer>>score)
ts.scores.push_back(score);
return is;
}
In C++, however, it's really easier to just read whatever amount of data is there, than to prefix the data with the count. Given that count is present, the easiest thing to do is probably to just read and ignore it:
std::string ignore;
std::getline(infile, ignore);
Then we can read the real data pretty easily:
std::vector<test_scores> student_scores;
test_scores tmp;
while (infile >> tmp)
student_scores.push_back(tmp);
...or, we can use C++'s handy-dandy istream_iterators to simplify the code even more:
std::vector<test_scores> student_scores((std::istream_iterator<test_scores>(infile)),
std::istream_iterator<test_scores>());
That's it -- it defines the vector and initializes it from the input file, all in one (fairly) simple operation.
Thew file pointer is always advanced past what you have already read.

Parsing columns into arrays, while discriminating whats in the rows

I'm trying to parse a text file that is outputted like the example below, my example has limited entries but my actual one has over 15000 lines, so i can't read these in individually:
ID IC TIME
15:23:43.867 /g/mydata/dataoutputfile.txt identifier
0003 1233 abcd
0043 eb54 abf3
000f 0bb4 ac24
000a a325 ac75
0023 0043 ac91
15:23:44.000 /g/mydata/dataoutputfile.txt identifier
0003 1233 abcd
0043 eb54 abf3
000f 0bb4 ac24
000a a325 ac75
0023 0043 ac91
Is kind of the output I have. The time column resets every so often.
What I am doing now is making 2 additional columns in addition to the 3 i have in my example. The first column is the conversion of the ID column, into a translation into an understandable message. The second additional column will calculate the difference between each time code, except when the time code resets.
My logic is, is to read each column into an array so I can perform the necessary translations and operations.
I am focusing on getting the timecode differential first, as I think getting the translation will be a bit simpler.
The problem I'm having is getting the entries read into their matrices:
my code looks a bit like this:
while(readOK && getline(myfile,line))
{
stringstream ss(line);
string ident,IC,timehex,time,filelocation;
string junk1,junk2;
int ID[count];
int timecode[count2];
int idx=0;
if(line.find("ID") !=string::npos)
{
readOK=ss>>ident>>IC>>timehex;
myfile2<<ident<<"\t\t"<<IC<<"\t\t"<<timehex<<"\t\t"<<"ID Decoded"<<"\t\t"<<"DT"<<endl;
myfile3<<"headers read"<<endl
}
else if(line.find("identifier") != string::npos)
{
readOK=ss>>time>>filelocation;
myfile3<<"time and location read";
myfile2<<time<<"\t\t"<<filelocation<<endl;
}
else //this is for the hex code lines
{
readOK=ss>>hex>>ID[idx]>>IC>>timecode[idx];
if (readOK)
{
myfile2<<setw(4)<<setfill('0')<<hex<<ID[1000]<<"\t\t"<<IC<<"\t\t"<<timecode[1000]<<endl;
myfile3<<"success reading info into arrays"<<endl;
}
else
myfile3<<"error reading hex codes"<<endl;
}
idx++;
}
Although this code doesn't work correctly. I can't just read in every line quite the same because of the intervening time and file location entries that are inserted to help keep track of when I am looking at in my code.
My gut is telling me that I'm calling the matrix entries too early and they haven't been filled yet, because if I cout number 1000, I get a 0 (i have well over 15000 lines in my input file and I have the boundaries of my arrays set dynamically in another part of my program).
I can't seem to figure out how to get the entries assigned correctly as I am having some inheritance issues with the count variable resetting to 0 every time through the loop.
Define int idx outside of the scope of the while loop (before the while). As it is now, each time through the loop it will be reset.