string parser text adventure - c++

Hello! I am currently working on a text adventure in C++ and could use some help.
What I'm trying to do is let the user input a command like the following:
'go kitchen'
'open door with key'
and make the game react accordingly.
Our teacher gave us the following code (which I have modified) and I'm having difficulty understanding what exactly it is doing and how I can use it to make the game. I modified it so that the user can input strings and it does tokenize the string wonderfully into a verb, object, preposition and object2.
But what I need to do then is somehow compare the input to a list of available commands. This is what I'm having trouble accomplishing at the moment. I am new to programming and need to do this as a homework assignment for my studies. Any help would be much appreciated.
struct command {
char* verb;
char* object;
char* preposition;
char* object2;
};
bool getTokens(char * acInput,
const char token_delimiter,
command * pTargetCommand)
{
char * pCurToken;
pCurToken = strtok (acInput, &token_delimiter);
if (pCurToken == NULL) {
printf("Error: Found no verb");
getchar();
return 1;
}
pTargetCommand->verb = pCurToken;
pCurToken = strtok (NULL, &token_delimiter);
if (pCurToken == NULL) {
printf("Error: Found no object");
getchar();
return 1;
}
pTargetCommand->object = pCurToken;
pCurToken = strtok (NULL, &token_delimiter);
if (pCurToken != NULL) {
pTargetCommand->preposition = pCurToken;
pCurToken = strtok (NULL, &token_delimiter);
if (pCurToken == NULL) {
printf("Error: Found no second object for preposition");
getchar();
return 1;
}
pTargetCommand->object2 = pCurToken;
}
pCurToken = strtok (NULL, &token_delimiter);
if (pCurToken != NULL) {
printf("Error: too many tokens.");
getchar();
return 1;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char acInput[256];
cin.getline (acInput,256);
command myCommand = { NULL };
int RoomChoice = 0;
printf ("Splitting string \"%s\" into tokens:\n", acInput);
getTokens(acInput, *TOKEN_DELIMITER, &myCommand);
printf ("Verb: %s\n", myCommand.verb);
printf ("object: %s\n", myCommand.object);
printf ("preposition: %s\n", myCommand.preposition);
printf ("object2: %s\n", myCommand.object2);
getchar();
return 0;
}

Without giving too much of your homework assignment away, you'll need to somehow read the list of all available actions into a structure, then compare against that structure.
As a hint, depending on the pattern, that might be a switch() {} statement or a collection like an array.
Consider
switch (myCommand.verb)
Case "go":
In a real-world application, you'd spin up a factory of command objects, then invoke one of those. Here, however, I would suggesting thinking through your control statements.

You cannot do a switch with strings (as you already noted, switch only work with constant numbers)
To do compare strings you can use strcmp, strncmp, or better yet, use String.compare. You should be able to find enough information about them with a Google search.

Related

Damaged heap while working with dynamically allocated variables and _chdir windows function

while working with those function i'm getting stuck with an error.
The debbuger says "damged heap" on _chdir(dirCorrente); line.
The main calls those function as follow:
- char* temp = getCartellaCorrente();
- some other stuff not relative to these function...
- temp = setCartellaCorrente("cd test")
when the execution stops the setCartellaCorrente's dirCorrente value is '"C:\Users\Luca\Desktop\remote-control-project\FirstService\Debug\test"'
I think i'm doing something wrong with dynamically allocated variables.
I'm working on this problem since 48 hours now, i've serached on the internet but nothing. I guess that i don't know something important about allocated variable or _chdir function.
I will be really gratefull if you can explain me what i miss.
char* getCartellaCorrente() {
char* temp;
size_t size;
LPWSTR dirCorrente = new TCHAR[DEFAULT_BUFLEN];
GetCurrentDirectory(DEFAULT_BUFLEN, dirCorrente);
size = wcslen(dirCorrente);
temp = (char *)malloc(size);
wcstombs_s(NULL, temp, size+1, dirCorrente, size);
return temp;
}
char* setCartellaCorrente(char* relative) {
char *dirCorrente;
if (strlen(relative)>=5 && relative[4] == ':') {
dirCorrente = (char *)malloc(DEFAULT_BUFLEN);
strcpy_s(dirCorrente, DEFAULT_BUFLEN, &relative[3]);
}
else {
dirCorrente = getCartellaCorrente();
relative[2] = '\\';
strcat_s(dirCorrente, DEFAULT_BUFLEN, &relative[2]);
printf("goode %s \n", dirCorrente);
}
//fixPathSlash(dirCorrente);
printf("\n2: %s\n", dirCorrente);
int i = _chdir(dirCorrente); //HERE IT STOPS
printf("wtf: %d\n", i);
free(dirCorrente);
printf("boh\n");
return getCartellaCorrente();
}
It's my first question. Sorry if i missed some important information, i'll edit fast.
Ok i managed to solve the problem, as i thoght the problem was the allocation.
Since i need to solve this fast for now i've used the method i listed above, anyway i will study how to work with allocated variable and modify it to improve it.
void getCartellaCorrente(char* temp) {
size_t size;
LPWSTR dirCorrente = new TCHAR[DEFAULT_BUFLEN];
GetCurrentDirectory(DEFAULT_BUFLEN, dirCorrente);
size = wcslen(dirCorrente);
wcstombs_s(NULL, temp, DEFAULT_BUFLEN, dirCorrente, size);
}
void setCartellaCorrente(char* relative, char* dirCorrente) {
if (strlen(relative)>=5 && relative[4] == ':') {
strcpy_s(dirCorrente, DEFAULT_BUFLEN, &relative[3]);
}
else {
getCartellaCorrente(dirCorrente);
relative[2] = '\\';
strcat_s(dirCorrente, DEFAULT_BUFLEN, &relative[2]);
}
int i = _chdir(dirCorrente);
return getCartellaCorrente(dirCorrente);
}
The path variable is now allocated into the main with static size, the functions does not return the correct value, instead they change it directly.

Manually implement c string function with security enhancements

I'm trying to write a replacement for the _tcstok_s function that doesn't skip initial delimiters. (_tcstok_s is a variant of the strtok function that uses TCHARs and includes security enhancements documented at https://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx). I'm doing this due to the annoying behavior of strtok that skips consecutive delimiters. I got the idea from
c++ strtok skips second token or consecutive delimiter.
The thing I don't understand is how to implement the security enhancements that make _tcstok_s better than _tcstok. How to I make sure my re-implementation is as secure as _tcstok_s?
Although my code is C++, I am using C-style strings because I am implementing an MSI DLL.
This is what I have so far, but I don't know how to add the _s security enhancements.
TCHAR *GetNextToken(TCHAR *strToken, const TCHAR *szDelimit, TCHAR **pszContext)
{
if (strToken == NULL)
{
if ((pszContext == NULL) || (*pszContext == NULL))
return NULL;
TCHAR *pToken = _tcspbrk(*pszContext, szDelimit);
TCHAR *szResult = *pszContext;
if (!pToken)
{
*pszContext = NULL;
}
else
{
*pToken++ = _T('\0');
*pszContext = pToken;
}
return szResult;
}
else
{
TCHAR *pToken = _tcspbrk(strToken, szDelimit);
if (!pToken)
{
*pszContext = NULL;
}
else
{
*pToken++ = _T('\0');
*pszContext = pToken;
}
return strToken;
}
}

Fastest way to parse a XML file with libxml2?

Hi is there any "faster" way to parse a XML file with libxml2?
Right now i do it that way following C++ Code:
void parse_element_names(xmlNode * a_node, int *calls)
{
xmlNode *cur_node = NULL;
for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
(*calls)++;
if(xmlStrEqual(xmlCharStrdup("to"),cur_node->name)){
//printf("node type: <%d>, name <%s>, content: <%s> \n", cur_node->children->type, cur_node->children->name, cur_node->children->content);
//do something with the content
parse_element_names(cur_node->children->children,calls);
}
else if(xmlStrEqual(xmlCharStrdup("from"),cur_node->name)) {
//printf("node type: <%d>, name <%s>, content: <%s> \n", cur_node->children->type, cur_node->children->name, cur_node->children->content);
//do something with the content
parse_element_names(cur_node->children->children,calls);
}
else if(xmlStrEqual(xmlCharStrdup("note"),cur_node->name)) {
//printf("node type: <%d>, name <%s>, content: <%s> \n", cur_node->children->type, cur_node->children->name, cur_node->children->content);
//do something with the content
parse_element_names(cur_node->children->children,calls);
}
.
.
.
//about 100 more node names comming
else{
parse_element_names(cur_node->children,calls);
}
}
}
int main(int argc, char **argv)
{
xmlDoc *doc = NULL;
xmlNode *root_element = NULL;
if (argc != 2)
return(1);
/*parse the file and get the DOM */
doc = xmlReadFile(argv[1], NULL, XML_PARSE_NOBLANKS);
if (doc == NULL) {
printf("error: could not parse file %s\n", argv[1]);
}
int calls = 0;
/*Get the root element node */
root_element = xmlDocGetRootElement(doc);
parse_element_names(root_element,&calls);
/*free the document */
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}
Is it really the fastest way? Or is there any better/faster solution which you can advice me?
Thank you
xmlReadFile et al. are based on libxml2's SAX parser interface (actually, the SAX2 interface), so it's generally faster to use your own SAX parser if you don't need the resulting xmlDoc.
If you have to distinguish between many different element names like in your example, the fastest approach is usually to create separate functions for every type of node and use a hash table to lookup these functions.

Exc access error

Very new to C++ and having problems returning a vector. I put a breakpoint and the array is correct (populated with all the objects I would expect from the query). But when it returns I get an error:
EXC_BAD_ACCESS
on line m_pComponentContainer->removeAll();
from CCNode.cpp
Which is strange since this is a base class (does NOT inherit from any kind of CC object) although I am extensively using the Cocos2dx framework, its not included in this class.
Im fairly sure this is because something is being deallocated. However like I said Im very new to C++ and not really sure where the problem is. I was hoping to get a little further in development before I had to start worrying about memory management.
int numberOfCards = DatabaseHelper::getNumberOfCards();
//cant be zero
assert(numberOfCards);
std::vector<CardSlot> returnArray(numberOfCards);
sqlite3_stmt * statement;
if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)
{
const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";
if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
CardSlot *aCard;
const char* cardID = (const char*)sqlite3_column_text(statement, 0);
const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);
if (cardID != NULL) {
std::string imageName = ".png";
imageName = cardID + imageName;
aCard = (CardSlot *)CardSlot::spriteWithFile(imageName.c_str());
}
if (cardID != NULL) {
aCard->cardID = std::string(cardID);
cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
}
if (cardHP != NULL) {
aCard->cardHP = std::string(cardHP);
cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
}
if (cardMP != NULL) {
aCard->cardMP = std::string(cardMP);
cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
}
if (cardAbility != NULL) {
aCard->cardAbility = std::string(cardAbility);
cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
}
numberOfCards--;
returnArray[numberOfCards] = *aCard;
}
sqlite3_finalize(statement);
}
sqlite3_close(this->cardWarsDB);
return returnArray;
}
Here is a screenshot of the stack trace. I was just looking at it, and it seems that it is the CardSlot objects are the culprits.
But still dont know how to "retain" them, but Ill look at some Cocos documentation.
NOTE1
It looks like your CardSlot is not safe to copy. You copy CardSlots in at least two places:
aCard = * CardSlot::spriteWithFile(imageName.c_str()); (also a memory leak assuming spriteWithFile returns CardSlot *; the "temporary" is not destructed)
returnArray[numberOfCards] = aCard;
From what I can tell, you are probably keeping a CCSprite pointer in CardSlot and destroying it (with delete) in your CardSlot destructor. However, this pointer gets destroyed multiple times because of the copies, which causes your crash.
You need to redesign your class so it can either be safely copied, or refactor your code so that you make no copies (e.g. by using a vector<shared_ptr<CardSlot> > to hold pointers to the instances).
I have edited the code to use more pointers rather then passing around and filling my array with objects. However I think a big thing that helped fix it was using cocos2d::CCArray instead of a std::vector. Most of my classes are children of Cocos2d classes (CCSprites, and CCLayers) and so using its own array data type makes sense.
cocos2d::CCArray DatabaseHelper::getAllCards()
{
int numberOfCards = DatabaseHelper::getNumberOfCards();
//cant be zero
assert(numberOfCards);
cocos2d::CCArray returnArray(numberOfCards);
sqlite3_stmt * statement;
if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)
{
const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";
if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
CardSlot* aCard;
const char* cardID = (const char*)sqlite3_column_text(statement, 0);
const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);
if (cardID != NULL) {
std::string imageName = ".png";
imageName = cardID + imageName;
aCard = CardSlot::spriteWithFile(imageName.c_str());
}
if (cardID != NULL) {
aCard->cardID = std::string(cardID);
cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
}
if (cardHP != NULL) {
aCard->cardHP = std::string(cardHP);
cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
}
if (cardMP != NULL) {
aCard->cardMP = std::string(cardMP);
cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
}
if (cardAbility != NULL) {
aCard->cardAbility = std::string(cardAbility);
cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
}
numberOfCards--;
returnArray.addObject(aCard);
}
sqlite3_finalize(statement);
}
sqlite3_close(this->cardWarsDB);
return returnArray;
}
//incase sql fails, close db and created a "FAILED" card
sqlite3_close(this->cardWarsDB);
cocos2d::CCLog("DB returned error: cant open char catagories file");
cocos2d::CCArray failedReturnArray(1);
CardSlot * aCard;
aCard->cardID = std::string("FAILED");
aCard->cardHP = std::string("FAILED");
aCard->cardMP = std::string("FAILED");
aCard->cardAbility = std::string("FAILED");
failedReturnArray.addObject(aCard);
return failedReturnArray;
}
Also in case anyone cares here is CardSlot (not much to it, only built the constructor at this time):
CardSlot * CardSlot::spriteWithFile(const char *pszFileName)
{
CCLOG("CardSlot::spriteWithFile");
CardSlot * aCard = new CardSlot();
if (aCard && aCard->initWithFile(pszFileName))
{
aCard->cardID = pszFileName;
aCard->scheduleUpdate();
aCard->autorelease();
return aCard;
}
CC_SAFE_DELETE(aCard);
return NULL;
}
The only thing Im concerned about is that I think my CCArray should be a pointer. But its working now and learning all the memory management "tricks of the trade" will come in time, the more I work with C++
Thanks #nneonneo for all the help Im sure your fix would have worked and I tried but no matter what I did couldnt get the vector to work. I 1up'd you as much as I could but really this is the "Answer" I implemented.
The returnArray is declared local to the function, it will be deallocated when the function returns. You would need to either declare it as static or move the declaration to outside the function.

another C++ equivalent for the followingtwo lines of code

Sorry for such a vague title.
Basically, I am trying to hack a function to suit my needs. But lately I have been working a lot on python and my c++ is bit rusty.
So earlier my function took a
int func(FILE *f)
{ .....
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
return MM_PREMATURE_EOF;
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type,
storage_scheme) != 5)
return MM_PREMATURE_EOF;
}
now instead of this I am directly inputing the string data
int func(std::string *data)
{ .....
// how should I modify this if statment..I want to parse the same file
// but instead it is in form of one giant string
}
Thanks
You can use the same code, just convert the data in the std::string into a C string.
sscanf(data->c_str(), "%s %s %s %s %s", //...);
However, you should consider passing in a const reference, since you are probably not planning on modifying your input data:
int func(const std::string &data) {
//...
if (sscanf(data.c_str(), //...)) {
//...
}
}