how to eliminate multiple if statements?

Started by
9 comments, last by alnite 12 years, 8 months ago
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);
}
Advertisement
Thats what Switch statements is for..
I thought about that, but it's a string so it won't work.
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]
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 );
}
}


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;
}

I totally forgot to mention I'm using Java lol

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.

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.
I really like the map idea, I just don't understand how to implement it.

This topic is closed to new replies.

Advertisement