Public Group

how to eliminate multiple if statements?

This topic is 2464 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I need to get rid of this pile up of if statements. I'm working on a game similar to zork and here is what I have

-My parser gets input, splits into two words and stores them as strings(rejects more than two words)

-passes into another class to handle the command.

Where the problem is, is that when it gets to the other class, I wasn't sure how to implement it correctly. I used a ton of ifs and else ifs to handle the command. I'm sure there is a better way to do this though. I have like 30 commands right now in early stages in development of the game. When it's done it will probably have a few hundred. I don't want to have all these if statements in here. It looks like this:

 if(verb.equals("take")&&noun.equals("something")){ take(something); } 

Share on other sites
Thats what Switch statements is for..

Share on other sites
I thought about that, but it's a string so it won't work.

Share on other sites
I'm assuming C++ and a text adventure here. Hope that is correct. C++ is not a great choice for text adventures but that is not really the point of the thread I guess.

Maybe you could consider something like:

[source lang="cpp"]
class Object
{
public:
Object(/*...*/);

int location;
std::string noun;
std::string description;
};

// simplified...
std::vector<Object> objects;
int playerLocation;

class Action
{
public:
virtual void do(const std::string &noun)=0;
};

std::map<std::string,Action*> actions;

class TakeAction
{
public:
virtual void do(const std::string &noun);
};

void TakeAction::do(const std::string &noun)
{
for(auto o=objects.begin();o!=objects.end();++o)
{
if(o.noun==noun && o.location==playerLocation)
{
std::cout << "You take the " << o.description << ".\n";
return;
}
}

std::cout << "There isn't one of those here.\n";
}

void init()
{
objects.push_back(Object("hat","large, floppy hat"));
// etc

actions["take"]=new TakeAction();
// etc
}

void handleCommand(const std::string &verb,const std::string &noun)
{
auto a=actions.find(verb);

if(a==actions.end()) std::cout << "I do not understand.\n";
else a.second->do(noun);
}
[/source]

Share on other sites
First off use something like a std::map to map your tokens to information.  You could then make a bunch of contained classes that modify your gamestate and store them in the map.  That way your command logic boils down to a search of the map.  Likewise, you can store your objects the same way you store the commands, to allow the same searching.
 // To take care of your commands, you'd make a CommandInfo structure like so: typedef std::vector< std::string > tCommandListType; class Command { public: virtual ~CommandInfo() {} virtual void parse( const tCommandListType & ) = 0; }; // and some commands class EatCommand : public Command { public: virtual void parse( const tCommandListType &commands ) { assert( commands[0] == "eat" ); if( commands.size() < 2 ) { std::cout << "Eat what?" << std::endl; } else if( commands.size() > 2 ) { std::cout << "You can only eat one thing at a time." << std::endl; } else { // logic to remove the item and whatnot std::cout << "That <" << commands[1] << "> sure was yummy." << std::endl; } } }; // Now setup a mapping of all the things you want to accept as commands typedef std::map< std::string, Command * > tCommandMapType; tCommandMapType commandMap; commandMap["eat"] = new EatCommand; commandMap["consume"] = new EatCommand; while ( player_not_dead ) { // read the input "eat rope", "use match on candle" etc. // tokenize it on each space into a tCommandListType, so you have // tokens[0] == "eat" // tokens[1] == "rope" // locate the command and run it. tMapType::iterator itr = commandMap.find( tokens[0] ); if( itr != commandMap.end() ) { itr->second->parse( tokens ); } } 

Share on other sites
You could try creating a data structure that represents a mapping from (verb, noun) -> handler class/function pointer, then search that data structure to process a command. It is also easy to maintain flexibility by supporting wildcards with that. Something like

 using namespace std; // Define the type signature of a function which handles input commands. typedef void (*CommandHandler)(string, string); // Define the type signature of the data structure which stores our mappings. typedef map<pair<string, string>, CommandHandler> CommandHandlerMap; // Create the map to contain the handler mappings. CommandHandlerMap handlers; // This function handles "pickup" "*" if "pickup" was not handled by some other means. void PickupHandler(string verb, string noun) { cout << "You picked up a " << noun << endl; } // This function handles "pickup" "moon". void PickupMoonHandler(string verb, string noun) { cout << "Are you silly? You cannot pick up the moon!" << endl; } // This function handles "destroy" "*". void DestroyHandler(string verb, string noun) { cout << "You destroyed a " << noun << endl; } // Register our mapping table. void Init() { handlers[make_pair("pickup", "*")] = PickupHandler; handlers[make_pair("pickup", "moon")] = PickupMoonHandler; handlers[make_pair("destroy", "*")] = DestroyHandler; } // For each input command, dispatch it through the mapping table to its proper handler. void ProcessInputCommand(string verb, string noun) { CommandHandlerMap::iterator iter = handlers.find(make_pair(verb, noun)); if (iter == handlers.end()) iter = handlers.find(make_pair(verb, "*")); if (iter != handlers.end()) iter->second(verb, noun); else cout << "No handler found for command \"" << verb << "\" \"" << noun << "\"" << endl; } 

Share on other sites
I totally forgot to mention I'm using Java lol

Share on other sites

Thats what Switch statements is for..

Not a huge amount of advantage in this case of a switch over an if chain really in terms of maintenance, plus OP is switching on two values, so switch is not a good suggestion really.

I thought about that, but it's a string so it won't work.

On that note, there is a lot to be said for mapping verbs and nouns to integer IDs before processing. You can then have two objects share the same noun, for example, without having the word duplicated. Sort of like a database normalisation principle really.

Share on other sites

I totally forgot to mention I'm using Java lol

Java has a Map interface with a number of implementations. You can use that to do somthing similar as with std::map.

Share on other sites
I really like the map idea, I just don't understand how to implement it.

• 10
• 16
• 14
• 18
• 15