Ok, I'm not to sure what to put in and what to leave out, so I'm just going to put it all out there. I'm going to attempt to format this as best I can for ease of readability.
Fair warning: Some of what I'm going to say here isn't going to make sense if you haven't read my other thread (Which I will add a link to for completeness shortly).
******************************
Index:
- Why I chose to do this.
- What it's supposed to do.
- Problems with my implementation.
- The 'Why' section.
- Things I haven't used before (and probably messed up)
- My environment
- Code
- Why did I post this?
******************************
=== Section 1: Why I chose to do this ===
Almost every programmer I've read about or talked to has told me that the easiest way to learn programming is to have a project. While I have that database program I want to write for a family friend, I feel I should put that one off for later as it involves a lot of advanced stuff (Encryption, Network synchronization, advanced database features, etc). So I chose this project; one specific feature in a game. Who doesn't love games? (I could probably go on for hours about how almost everything when boiled down to it's essence can be likened to a game - but that's a subject for another time.)
I didn't want it to be the sort of beginner game project most folks put in their books or on their blogs though, I wanted to try something new.=== /Section 1 ===
=== Section 2: What it's supposed to do ===
So the basic idea is that this is like a choose your own adventure book. The program is supposed to get a block of text and provide the user with a number of choices. When the user inputs his choice, the program is supposed to go retrieve the next block of text based upon what the user chose. === /Section 2 ===
=== Section 3: Problems with my implementation ===
Currently the program retrieves all the blocks of text from a file and stores it in memory - very inefficient considering when this project is finished I anticipate that I will be contending with thousands of blocks of story with all their associated choices, putting the total probably somewhere in the hundreds of thousands of lines (Which is probably only going to be like 10 or so MB depending, but the operations on that text will probably be very intense. You will see what I mean when you see the code).
There's also a function call that isn't working properly. I have marked it with an appropriate comment.
=== /Section 3 ===
=== Section 4: The 'Why' Section ===
Why did I do things a certain way? That's what this section is for.
Why did I use so many vectors when I could have used arrays?
Basically because you can't have a function return an array without some finan'gling (©Fizzgig 2012) with pointers. And I'm just now beginning to learn about them. Plus most of the times I didn't know what the size was supposed to be beforehand. Vectors solve all this.
Why did I make the IDs strings and not ints? (Which would have made comparisons so much easier)
More options. Like I said, I'm going to have a lot of text when all is said and done. Assuming I keep it to 4 digits and don't use special characters:
int = 9^4 = 6'461 IDs.
string = 35^4 = 1'500'625 IDs
Why declare my data members private when they are automatically set that way anyway? (Don't know if this is a big issue, but I will put this here just in case)
I'm conflicted on this honestly. On the one hand, I feel it should be there for completeness and just because you don't have to do something doesn't mean you shouldn't. On the other hand, I'm new to C++ and doing it this way is stopping the fact that it's private by default to sink in. (I had to go Google it just to make sure)
=== /Section 4 ===
=== Section 5: Things I haven't used before ===
Before starting work on this program I haven't used or even seen any of the following so chances are higher than normal I might have used them incorrectly: (If you have the time and inclination I ask that you point out if/when I did)
Vectors, I/O and streams other than cout/cin (getline was a huge pain to get it working the way a wanted to in the end), .atoi() and the string compare (I'm still not sure exactly what it does and I need to fix this. Any recommended, easy to read/understand online documentation?).
=== /Section 5 ===
=== Section 6: My environment ===
Just including everything; don't know what's necessary.
I'm using Dev-C++ 4.9.9.2 with it's 'make' option set to "mingw32-make.exe" (Was told to do this), and minigw (Not sure which version).
Also running all this on Win 7.
=== /Section 6 ===
=== Section 7: Code ===
The .txt file is attached should you want to run this yourself. Just change the extension back to .dat . Reverse engineering one if going to take you a while.
First the header, contentblock.h:
Code:
#ifndef CONTENTBLOCK_H
#define CONTENTBLOCK_H
#include <vector>
#include <string>
using namespace std;
class contentBlock {
public:
void printBlock();
// Set Functions
void setBlockID(string BlockID);
void setText(string Text);
void setChoiceAmount(int Choices);
void addNextBlockID(string ChoiceID);
void addChoiceText(string ChoiceText);
// Get Functions
int getChoiceAmount();
string getNextBlock(int ChoiceIdentifier);
string getBlockID();
private:
int iChoices;
string sText, sBlockID;
vector<string> svChoiceText, svNextBlockID;
};
#endif
Think this is called the implementation file for contentblock.h, contentblock.cpp:
Code:
#include <iostream>
#include "contentblock.h"
void contentBlock::printBlock() {
cout << "==========" << sBlockID << "==========" << endl;
cout << sText << endl;
cout << "************************" << endl;
cout << "**Please Select:[1.." << iChoices << "]**" << endl;
cout << "************************" << endl;
for (int i = 0; i < iChoices; i++)
cout << i+1 << ") (" << svNextBlockID[i] << "):" << svChoiceText[i] << endl;
cout << "=========/" << sBlockID << "==========" << endl;
cout << "Option (999 exits): ";
}
void contentBlock::setBlockID(string BlockID) {
sBlockID = BlockID;
}
void contentBlock::setText(string Text) {
sText = Text;
}
void contentBlock::setChoiceAmount(int Choices) {
iChoices = Choices;
}
void contentBlock::addNextBlockID(string ChoiceID) {
svNextBlockID.push_back(ChoiceID);
}
void contentBlock::addChoiceText(string ChoiceText) {
svChoiceText.push_back(ChoiceText);
}
int contentBlock::getChoiceAmount() {
return iChoices;
}
string contentBlock::getNextBlock(int ChoiceIdentifier) {
return svNextBlockID[ChoiceIdentifier];
}
string contentBlock::getBlockID() {
return sBlockID;
}
Main:
Code:
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include "contentblock.h"
using namespace std;
vector<contentBlock> readContent(string iFile);
void treeLoop();
int main () {
treeLoop();
system("PAUSE");
return 0;
}
vector<contentBlock> readContent(string iFile) {
//variable setup
string sTemp;
vector<contentBlock> vBlock;
//stream setup
ifstream contentStream;
contentStream.open("Story.dat"); // It does not accept iFile as a parameter. Any thoughts?
//if to check file access.
if (contentStream.fail()) {
cout << "File access failed.\n";
system("PAUSE");
exit(1);
}//end if
//while to read contents of file until .eof() returns true.
int i = 0;
while (!contentStream.eof()) {
vBlock.resize(vBlock.size() + 1);
// Retrieve and set BlockID
getline(contentStream, sTemp, '#');
vBlock[i].setBlockID(sTemp);
// Retrieve and set Text
getline(contentStream, sTemp, '#');
vBlock[i].setText(sTemp);
// Retrieve and set ChoiceAmount
getline(contentStream, sTemp, '#');
vBlock[i].setChoiceAmount(atoi(sTemp.c_str()));
// Retrieve and set Choice Texts and their associated NextBlocks
for (int j = 0; j <= vBlock[i].getChoiceAmount() - 1; j++) {
getline(contentStream, sTemp, '#');
vBlock[i].addNextBlockID(sTemp);
getline(contentStream, sTemp, '#');
vBlock[i].addChoiceText(sTemp);
} // end for
i++;
}// end while
contentStream.close();
return vBlock;
} // end readContent function
void treeLoop() {
// variable setup
vector<contentBlock> primaryVector = readContent("Story.dat");
int iChoiceSelector, iPrimaryVectorSize, iCurrentBlock;
iPrimaryVectorSize = primaryVector.size() - 1;
string saTemp[iPrimaryVectorSize], sTemp;
// Populate saTemp with all the BlockIDs
for (int i = 0; i < iPrimaryVectorSize; i++) {
saTemp[i] = primaryVector[i].getBlockID();
}
// For debugging purposes.
/*cout << "Array contents:\n";
for (int i = 0; i < primaryVectorSize; i++) {
cout << sTemp[i] << endl;
} */
// Initial setup. Will change this when I add "save" functionality.
iCurrentBlock = 0;
primaryVector[0].printBlock();
cin >> iChoiceSelector;
// Runs until the user enters '999' as input.
while (iChoiceSelector != 999) {
// while to check and correct input.
while (iChoiceSelector < 1 || iChoiceSelector > primaryVector[iCurrentBlock].getChoiceAmount()) {
cout << "You have entered an invalid choice. Please select one from the list. [1.." << primaryVector[iCurrentBlock].getChoiceAmount() << "]: " ;
cin >> iChoiceSelector;
}// end inner while
sTemp = primaryVector[iCurrentBlock].getNextBlock(iChoiceSelector - 1);
// runs string comparison to determine what the NextBlock is going to be.
for (int i = 0; i < iPrimaryVectorSize; i++) {
if (saTemp[i].compare(0, sTemp.length(), sTemp) == 0) {
primaryVector[i].printBlock();
iCurrentBlock = i;
break;
} // end if
}// end for
cin >> iChoiceSelector;
}// end outer while
}// end treeLoop function
=== /Section 7 ===
=== Section 8: Why did I post this? ===
Well, at first I didn't know how I was going to make the program move between different blocks. My thought was to take it as far as I can and then ask for help. As I went along, I figured out a way to do this and tried a lot of things to make it work. Now I just want to see if someone can make a more efficient(algorithm-wise)/eloquent solution. Also how my coding style holds up.
If I'm close to the way you would have done it, and I just need to change one little thing to make it better; Could you give me a hint rather than a straight up solution?
=== /Section 8 ===
Also, is it worth it to write a function if you only ever going to call it once, like I did here?
Lastly I just want to say: Be as brutal in your feedback as you like.