Is there a more elegant way to achieve a "cheat code" implementation for a game in C++?
I have been learning C++ by diving into a project, a simple 2d game. I have attempted to implement a set of cheats but I'm seriously novice at string manipulation. I'm sure there would be a more elegant way to achieve what I would like than my code below.
As requested, stringBuffer
is just a string that contains the last 12 pressed characters. I prepend it because it's trimmed at the end later with resize, and hence my cheats have to be backwards. I'm very much a noob at string manipulation, and I know something is wrong here, which I why I asked for it to be looked at and possibly improved.
//The following code is in my keyPressed function
cheatBuffer = (char)key + cheatBuffer;
cheatBuffer.resize(12);
string tempBuffer;
string cheats[3] = {"1taehc","2taehc","3taehc"};
for(int i = 0;i < 3;i++){
tempBuffer = cheatBuffer;
tempBuffer.resize(cheats[i].size());
if(cheats[i] == tempBuffer){
switch(i){
开发者_开发问答case 1:
//activate cheat 1
break;
case 2:
//active cheat 2
break;
case 3:
//active cheat 3
break;
}
}
}
The codes are "cheat1", "cheat2" and "cheat3" respectively. I can't help thinking that this could be a lot better. Any insight would be greatly appreciated.
You might want to consider using:
std::map<std::string, std::function<void ()>>
(If you can use C++0x)
std::map<std::string, std::tr1::function<void ()>>
(If you can use TR1)
std::map<std::string, boost::function<void ()>>
(If you can use Boost)
(Of course, the signature of the function can differ)
Example using C++0x
#include <map>
#include <functional>
#include <string>
#include <iostream>
typedef std::map<std::string, std::function<void ()>> cheat_map;
inline void cheat1()
{
std::cout << "cheat 1 used!" << std::endl;
}
inline void cheat2()
{
std::cout << "cheat 2 used!" << std::endl;
}
int main()
{
cheat_map myCheats;
myCheats.insert(std::pair<std::string, std::function<void ()>>("cheat1", std::function<void ()>(cheat1)));
myCheats.insert(std::pair<std::string, std::function<void ()>>("cheat2", std::function<void ()>(cheat2)));
std::string buffer;
while (std::getline(std::cin, buffer)) {
if (!std::cin.good()) {
break;
}
cheat_map::iterator itr = myCheats.find(buffer);
if (itr != myCheats.end()) {
myCheats[buffer]();
}
}
}
Input:
notourcheat
cheat1
cheat2
cheat1
Output:
cheat 1 used!
cheat 2 used!
cheat 1 used!
Live demo
I would store the strings in a trie:
http://en.wikipedia.org/wiki/Trie
(in the Wikipedia article there are also links to C++ implementations).
In the leafs of the trie you can add additional data concerning the cheat.
When you lookup a string you simple check whether the string is contained in the trie with the cheat codes. If yes, you get the additional data (for example a function pointer to a function that does what you want to do; or more Object-Oriented: a pointer to a class instance that when you call one of its member functions does the "cheating stuff").
This code can be improved in several ways.
Make your immutable strings const static
static const std::string const foo[] = { "1taehc", "2taehc", "3taehc" };
So they don't have to be allocated every time in your "KeyPressed" handler.
Associate each cheat with a value that you can use in a case
statement
I don't think all the additional logic to facilitate the switch
is a good idea. What about something like this:
const int CHEAT_ONE = 1;
const int CHEAT_TWO = 2;
const int CHEAT_THREE = 3;
static const std::pair<std::string, int> const foo[] = {
std::make_pair("1taehc", CHEAT_ONE),
std::make_pair("2taehc", CHEAT_TWO),
std::make_pair("3taehc", CHEAT_THREE),
};
This way you get an integer which you can use as a case label for each of your cheat codes.
Search for the cheat to activate
You want to be able to search for a cheat code that has been activated easily. Let's break up the std::pair
instances and use a std::map
instead.
const int CHEAT_ONE = 1;
const int CHEAT_TWO = 2;
const int CHEAT_THREE = 3;
std::pair<std::string, int> cheatcodes[] = {
std::make_pair("1taehc", CHEAT_ONE),
std::make_pair("2taehc", CHEAT_TWO),
std::make_pair("3taehc", CHEAT_THREE),
};
std::map<std::string, int> cheatmap(
cheatcodes, cheatcodes + sizeof (cheatcodes) / sizeof (cheatcodes[0]));
Now, assuming that candidate
is your keypress buffer, you can just do
auto pair = cheatmap.find(candidate);
if (pair != cheatmap.end()) {
switch(pair->second) {
case CHEAT_ONE:
case CHEAT_TWO:
case CHEAT_THREE:
}
}
I would do it this way: when pressing some specific button first, for example enter, it would start to fill the cheatBuffer, and after one char was filled in the end of the buffer, it would use that buffer to check if a value of it exists in an std::map. And when that value exists, it would execute a function, which pointer was stored in std::map.
This might be overkill and too much work, but you could make a finite state machine to handle your key inputs, storing your current state instead of storing a key history. Your starting state 0 represents no applicable keys pressed. A 'c' will put you into state 1, where an 'h' will bring you to state 2, a 'c' will keep you at state 1, and anything else will send you back to 0. So on a given keypress you switch on the current state, and inside of that, switch on the character supplied to see which state you transition into. If it was a final state then execute the cheat and transition back to 0.
精彩评论