C ++ - 如何在字符串中提取有效字符串?

时间:2022-08-18 16:01:33

The Problem: I am trying to extract a valid game mode for Defense of the Ancients (DotA) from a game name using C++.

问题:我正在尝试使用C ++从游戏名称中提取有效的游戏模式(DotA)。

Details:

  • Game names can be, at most, 31 characters long
  • 游戏名称最多可以包含31个字符

  • There are three game mode categories: primary, secondary, and miscellaneous
    • There can only be 1 primary game mode selected
    • 只能选择1种主要游戏模式

    • Certain primary game modes are incompatible with some secondary game modes
    • 某些主要游戏模式与某些辅助游戏模式不兼容

    • Certain secondary game modes are incompatible with other secondary game modes
    • 某些二级游戏模式与其他二级游戏模式不兼容

    • Miscellaneous game modes can be combined with all other game modes
    • 其他游戏模式可以与所有其他游戏模式结合使用

  • 有三种游戏模式类别:主要,次要和杂项只能选择1种主要游戏模式某些主要游戏模式与某些次要游戏模式不兼容某些次要游戏模式与其他次要游戏模式不兼容其他游戏模式可以组合与所有其他游戏模式

Here are a list of the various game modes, with a chart showing which secondary modes each mode is compatible with (X means incompatible):

以下是各种游戏模式的列表,其中的图表显示了每种模式兼容的辅助模式(X表示不兼容):

// Only 1 primary allowed
static char *Primary[] = {
          // Compatible with > | dm | rv | mm | du | sh | aa | ai | as | id | em | np | sc | om | nt | nm | nb | ro | mo | sp | 
    "ap", // All Pick          |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "ar", // All Random        |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "tr", // Team Random       | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "mr", // Mode Random       | X  | X  |    |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    "lm", // League Mode       | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |    |
    "rd", // Random Draft      | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    "vr", // Vote Random       | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    "el", // Extended League   | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |    |
    "sd", // Single Draft      | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    "cm", // Captains Mode     | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  | X  |
    "cd"  // Captains Draft    | X  | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
};

static char *Secondary[] = {
          // Compatible with > | dm | rv | mm | du | sh | aa | ai | as | id | em | np | sc | om | nt | nm | nb | ro | mo | sp | 
    "dm", // Death Match       |    | X  | X  |    | X  | X  | X  | X  |    |    |    |    |    |    |    |    | X  | X  |    |
    "rv", // Reverse Mode      | X  |    |    |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "mm", // Mirror Match      | X  |    |    |    | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "du", // Duplicate Mode    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "sh", // Same Hero         | X  | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "aa", // All Agility       | X  |    |    |    |    |    | X  | X  |    |    |    |    |    |    |    |    |    |    |    |
    "ai", // All Intelligence  | X  |    |    |    |    | X  |    | X  |    |    |    |    |    |    |    |    |    |    |    |
    "as", // All Strength      | X  |    |    |    |    | X  | X  |    |    |    |    |    |    |    |    |    |    |    |    |
    "id", // Item Drop         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "em", // Easy Mode         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "np", // No Powerups       |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "sc", // Super Creeps      |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | 
    "om", // Only Mid          |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "nt", // No Top            |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "nm", // No Middle         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
    "nb", // No Bottom         |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | 
    "ro", // Range Only        | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | X  |    | 
    "mo", // Melee Only        | X  |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    | X  |    |    | 
    "sp"  // Shuffle Players   |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
};

// These options are always available
static char *Misc[] = {
    "ns", // No Swap
    "nr", // No Repick
    "ts", // Terrain Snow
    "pm", // Pooling Mode
    "oi", // Observer Info
    "mi", // Mini Heroes
    "fr", // Fast Respawn
    "so"  // Switch On
};

Examples: Here are some example input, with the desired output:

示例:以下是一些示例输入,具有所需的输出:

"DotA v6.60 -RDSOSP USA/CA LC!" -> "rdsosp"

“DotA v6.60 -RDSOSP USA / CA LC!” - >“rdsosp”

"DOTA AREMDM USA LC" -> "aremdm"

“DOTA AREMDM USA LC” - >“aremdm”

"DotA v6.60 -ApEmDuSpId USA BL" -> "apemduspid"

“DotA v6.60 -ApEmDuSpId USA BL” - >“apemduspid”

Notes: The solution doesn't necessarily have to provide actual code, pseudo-code and even just an explanation of how you would handle it is acceptable and preferred. Also, the solution needs to be flexible enough to where I can add another game mode fairly easily. It is also safe to assume that within the game name, the game mode will always start with a primary game mode.

注意:解决方案不一定必须提供实际代码,伪代码,甚至只是解释如何处理它是可接受的和首选的。此外,解决方案需要足够灵活,以便我可以相当容易地添加另一种游戏模式。假设在游戏名称中,游戏模式将始终以主游戏模式开始也是安全的。


Result:

#include <cstdarg>
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <vector>

std::map<std::string, std::vector<std::string> > ModeCompatibilityMap;

static const unsigned int PrimaryModesCount = 11;
static char *PrimaryModes[] = { 
    "ap", "ar", "tr", "mr", "lm", "rd", "vr", "el", "sd", "cm", "cd"
};

static const unsigned int SecondaryModesCounts = 19;
static char *SecondaryModes[] = {
    "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np",
    "sc", "om", "nt", "nm", "nb", "ro", "mo", "sp"
};

static const unsigned int MiscModesCount = 8;
static char *MiscModes[] = {
    "ns", "nr", "ts", "pm", "oi", "mi", "fr", "so" 
};

std::vector<std::string> Vectorize( int count, ... ) {
    std::vector<std::string> result;

    va_list vl;
    va_start( vl, count );

    for ( int i = 0; i < count; ++i ) {
        char *buffer = va_arg( vl, char * );
        result.push_back( buffer );
    }

    va_end( vl );

    return result;
}

void InitializeModeCompatibilityMap() {
    // Primary
    ModeCompatibilityMap["ar"] = Vectorize( 1, "rv" );
    ModeCompatibilityMap["tr"] = Vectorize( 2, "dm", "rv" );
    ModeCompatibilityMap["mr"] = Vectorize( 8, "dm", "rv", "sh", "aa", "ai", "as", "ro", "mo" );
    ModeCompatibilityMap["lm"] = Vectorize( 18, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo" );
    ModeCompatibilityMap["rd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
    ModeCompatibilityMap["vr"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
    ModeCompatibilityMap["el"] = Vectorize( 18, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo" );
    ModeCompatibilityMap["sd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
    ModeCompatibilityMap["cm"] = Vectorize( 19, "dm", "rv", "mm", "du", "sh", "aa", "ai", "as", "id", "em", "np", "sc", "om", "nt", "nm", "nb", "ro", "mo", "sp" );
    ModeCompatibilityMap["cd"] = Vectorize( 9, "dm", "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );   
    // Secondary
    ModeCompatibilityMap["dm"] = Vectorize( 8, "rv", "mm", "sh", "aa", "ai", "as", "ro", "mo" );
    ModeCompatibilityMap["rv"] = Vectorize( 2, "dm", "sh" );
    ModeCompatibilityMap["mm"] = Vectorize( 2, "dm", "sh" );
    ModeCompatibilityMap["sh"] = Vectorize( 3, "dm", "rv", "mm" );
    ModeCompatibilityMap["aa"] = Vectorize( 3, "dm", "ai", "as" );
    ModeCompatibilityMap["ai"] = Vectorize( 3, "dm", "aa", "as" );
    ModeCompatibilityMap["as"] = Vectorize( 3, "dm", "aa", "ai" );
    ModeCompatibilityMap["ro"] = Vectorize( 2, "dm", "mo" );
    ModeCompatibilityMap["mo"] = Vectorize( 2, "dm", "ro" );
}

std::vector<std::string> Tokenize( const std::string &string ) {
    std::vector<std::string> tokens;
    std::string token;
    std::stringstream ss( string );

    while ( ss >> token ) {
        tokens.push_back( token );
    }

    return tokens;
}

void SanitizeString( std::string &in ) {
    std::transform( in.begin(), in.end(), in.begin(), tolower );

    for ( size_t i = 0; i < in.size(); ++i ) {
        if ( in[i] < 'a' || in[i] > 'z' ) {
            in.erase( i--, 1 );
        }
    }
}

std::vector<std::string> SplitString( const std::string &in, int count ) {
    std::vector<std::string> result;

    if ( in.length() % count != 0 ) {
        return result;
    }

    for ( std::string::const_iterator i = in.begin(); i != in.end(); i += count ) {
        result.push_back( std::string( i, i + count ) );
    }

    return result;
}

bool IsPrimaryGameMode( const std::string &in ) {
    for ( int i = 0; i < PrimaryModesCount; ++i ) {
        if ( strcmp( PrimaryModes[i], in.c_str() ) == 0 ) {
            return true;
        }
    }

    return false;
}

bool IsSecondaryGameMode( const std::string &in ) {
    for ( int i = 0; i < SecondaryModesCounts; ++i ) {
        if ( strcmp( SecondaryModes[i], in.c_str() ) == 0 ) {
            return true;
        }
    }

    return false;
}

bool IsMiscGameMode( const std::string &in ) {
    for ( int i = 0; i < MiscModesCount; ++i ) {
        if ( strcmp( MiscModes[i], in.c_str() ) == 0 ) {
            return true;
        }
    }

    return false;
}

bool IsValidGameMode( std::string in, std::string &out ) {
    // 1. Strip all non-letters from the string and convert it to lower-case
    SanitizeString( in );

    // 2. Confirm that it is a multiple of 2
    if ( in.length() == 0 || in.length() % 2 != 0 ) {
        return false;
    }

    // 3. Split the string further into strings of 2 characters
    std::vector<std::string> modes = SplitString( in, 2 );

    // 4. Verify that each game mode is a valid game mode and is compatible with the others
    bool primaryModeSet = false;

    for ( size_t i = 0; i < modes.size(); ++i ) {
        if ( IsPrimaryGameMode( modes[i] ) || IsSecondaryGameMode( modes[i] ) ) {
            if ( IsPrimaryGameMode( modes[i] ) ) {
                if ( primaryModeSet ) {
                    return false;
                }

                primaryModeSet = true;
            }

            if ( ModeCompatibilityMap.count( modes[i] ) > 0 ) {
                std::vector<std::string> badModes = ModeCompatibilityMap[modes[i]];

                for ( size_t j = 0; j < badModes.size(); ++j ) {
                    for ( size_t k = 0; k < modes.size(); ++k ) {
                        if ( badModes[j] == modes[k] ) {
                            return false;
                        }
                    }
                }
            }
        } else if ( !IsMiscGameMode( modes[i] ) ) {
            return false;
        }
    }

    // 5. Assign the output variable with the game mode and return true
    out = in;

    return true;
}

std::string ExtractGameMode( const std::string &gameName ) {
    std::vector<std::string> tokens = Tokenize( gameName );

    std::string gameMode;

    for ( size_t i = 0; i < tokens.size(); ++i ) {
        if ( IsValidGameMode( tokens[i], gameMode ) ) {
            return gameMode;
        }
    }

    return "";
}

int main( int argc, char *argv[] ) {
    InitializeModeCompatibilityMap();

    std::string gameName = "DotA v6.60 -RDEM USA/CA LC";
    std::string gameMode = ExtractGameMode( gameName );

    std::cout << "Name: " << gameName << std::endl;
    std::cout << "Mode: " << gameMode << std::endl;

    return 0;
}

Output:

Name: DotA v6.60 -RDEM USA/CA LC

名称:DotA v6.60 -RDEM USA / CA LC

Mode: rdem


If anyone would like to review this code and let me know what they would change, that would be appreciated.

如果有人想查看此代码并让我知道他们会改变什么,那将不胜感激。

Thanks.

4 个解决方案

#1


Extracting the game type from the host's game name will be difficult without more rules in place. If you really want to avoid giving the end user more rules you could try the following...

如果没有更多规则,从主机的游戏名称中提取游戏类型将很困难。如果你真的想避免给最终用户更多的规则,你可以试试以下......

  • ToLower() the entire game name string.
  • ToLower()整个游戏名称字符串。

  • Split the game name using a space ' ' delimiter.
  • 使用空格“分隔符”拆分游戏名称。

  • Analyse each word, do the following. If anything fails, go to next word.
  • 分析每个单词,执行以下操作。如果有任何失败,请转到下一个单词。


  • taking [0] and determining if it has an ascii value of 97-122 (making sure it's a letter). If it's not within those values, go the next character, and so on until it does (without exceeding the array bounds obviously). This removes any user formatting like a hyphen -apem.
  • 取[0]并确定它的ascii值是否为97-122(确保它是一个字母)。如果它不在这些值内,则转到下一个字符,依此类推,直到它完成(显然不超过数组边界)。这将删除任何用户格式,如连字符。

  • strcmp() the next 2 characters with each of primary game types until you reach a match. Otherwise failing and moving onto next word.
  • strcmp()每个主要游戏类型的下两个字符,直到你达到匹配。否则失败并转移到下一个单词。

  • With the remaining characters, strcmp each next pair of characters with each secondary or misc gametype. If any don't match fail out to next word, or if there is only 1 character left over fail out to next word
  • 对于剩余的字符,strcmp每个下一对字符与每个secondary或misc游戏类型。如果任何不匹配,则对下一个单词失败,或者如果剩下的只有一个字符,则对下一个单词失败

That should extract the game type, or you can blame the user for using a bad game name.

这应该提取游戏类型,或者你可以责怪用户使用糟糕的游戏名称。


Now for the harder part, verifying if the gametypes are compatible with each other. I suggest that you make a struct that holds a data structure of booleans representing each of the secondary game types. A std::map, or a single boolean array that could be accessed using a enum.

现在更难的部分,验证游戏类型是否相互兼容。我建议你创建一个结构,它包含代表每种二级游戏类型的布尔数据结构。可以使用枚举访问的std :: map或单个布尔数组。

Now you're going to need a data object to represent each primary game type as well as each secondary game type.

现在,您将需要一个数据对象来表示每个主要游戏类型以及每个辅助游戏类型。

Then just make an array of every gametype, both primary and secondary. Refer to code sample:

然后只需制作一个包含主要和次要游戏类型的数组。参考代码示例:

map<const char*, bool> mapSecondaryCompatibility;

struct tGameType
{
    char szName[3];
    mapSecondaryCompatibility m_compat;
}

As you see, there technically isn't a difference between your primary and secondary game types, they both have the same restriction... may not be compatible with another secondary game type.

如您所见,从技术上讲,您的主要和次要游戏类型之间没有区别,它们都有相同的限制......可能与其他次要游戏类型不兼容。

With this I'm sure you can figure out the rest. I hope it helps, I gotta get back to work :)

有了这个,我相信你可以弄明白其余的。我希望它有所帮助,我得回去工作:)

Oh and I'm a big fan of DotA... keep up the good work!

哦,我是DotA的忠实粉丝......保持良好的工作!

#2


Create bool arrays which replicate the tables you've put into comments. Except instead of an "X" or blank put "true" or "false" (so "true" means the combination of modes is valid and "false" means invalid).

创建bool数组,复制您放入注释的表。除了“X”或空白而不是“true”或“false”(因此“true”表示模式的组合有效,“false”表示无效)。

Use this table to lookup whether the combination is valid:

使用此表来查找组合是否有效:

 bool IsSecondaryValidWithPrimary(unsigned int primaryIndex, unsigned int secondaryIndex)
 {
     static bool secondaryValidWithPrimary[numPrimaryModes][numSecondaryModes] = {...}

     if (primaryIndex < numPrimaryModes && secondaryIndex < numSecondaryModes)
     {
         return secondaryValidWithPrimary[primaryIndex][secondaryIndex]
     }
     else
     {
         //... this should never happen, throw your favorite exception
     }
 }

Naturally this requires you to convert each 2 character string to the correct array index to be tested. Loop over every possible combination and check to see if its valid. I doubt you seriously care about performance in this setup, so this should work nicely.

当然,这需要您将每个2个字符的字符串转换为要测试的正确数组索引。循环遍历每个可能的组合,并检查它是否有效。我怀疑你是否真的关心这个设置中的性能,所以这应该很好用。

Do the same for the other validity check (secondary with another secondary) or any other combination of modes for which you have compatibility rules.

对其他有效性检查(辅助与另一个辅助)或您具有兼容性规则的任何其他模式组合执行相同操作。

#3


I might try putting each mode into a std::set with a white list of modes that are compatible with the given node. When you want to parse a mode string you make a copy of the primary white list. Then you step down through the string. If the next node is not in the white list then you have an invalid mode string. If the next mode is in the white list then you intersect the next node's white list with your working white list. Continue until you reach the end of the string or the white list is empty. If the white list is empty and you are not at the end of the string, it is invalid, otherwise it is good.

我可能会尝试将每个模式放入一个std :: set,其中包含与给定节点兼容的白色模式列表。如果要解析模式字符串,请复制主白名单。然后你走下字符串。如果下一个节点不在白名单中,那么您的模式字符串无效。如果下一个模式位于白名单中,则您将下一个节点的白名单与工作白名单相交。继续,直到到达字符串的末尾或白名单为空。如果白名单为空并且您不在字符串的末尾,则它无效,否则它是好的。

The misc modes probably don't belong in the white list (because they are on every white list).

misc模式可能不属于白名单(因为它们在每个白名单上)。

You might also try instead using a black list and creating a union at each step of the way, then bailing out if the mode is in the list.

您也可以尝试使用黑名单并在每一步创建一个联合,然后在模式列表中拯救。

#4


I would lean towards converting the game mode types into an enumeration. I might even wrap the mode in a class that can store the current game mode state and provide friendly accessors for other parts of the game to quickly query the current mode.

我倾向于将游戏模式类型转换为枚举。我甚至可以将模式包装在可以存储当前游戏模式状态的类中,并为游戏的其他部分提供友好的访问器以快速查询当前模式。

Internally I would create a std::map< int, std::vector< int > > to store a list of compatable modes. As soon as the command line is entered, I would convert the two character strings to the enumeration value. Then I would do a lookup in the compatable mode mapping to see if it's an allowed mode.

在内部我会创建一个std :: map >来存储一个可兼容的模式列表。输入命令行后,我会将两个字符串转换为枚举值。然后我会在兼容模式映射中进行查找,看看它是否是允许模式。 ,std>

How you fill the map is up to you - I'm thinking you could have a class that does it - a compatability loader, or you could drive it from a configuration file if you wanted end users to be able to modify available modes.

你如何填写地图取决于你 - 我认为你可以有一个类 - 一个兼容性加载器,或者如果你希望最终用户能够修改可用模式,你可以从配置文件中驱动它。

An enumeration would be nicer to work with so that you could do a lot of checking at compile time, instead of runtime checks. You do a single runtime check when you convert from the input string to the enumerations, and return a fail message to the user if it's not valid. Then all your other code is checked at compile time.

枚举可以更好地使用,以便您可以在编译时进行大量检查,而不是运行时检查。从输入字符串转换为枚举时,执行单个运行时检查,如果用户无效,则将失败消息返回给用户。然后在编译时检查所有其他代码。

#1


Extracting the game type from the host's game name will be difficult without more rules in place. If you really want to avoid giving the end user more rules you could try the following...

如果没有更多规则,从主机的游戏名称中提取游戏类型将很困难。如果你真的想避免给最终用户更多的规则,你可以试试以下......

  • ToLower() the entire game name string.
  • ToLower()整个游戏名称字符串。

  • Split the game name using a space ' ' delimiter.
  • 使用空格“分隔符”拆分游戏名称。

  • Analyse each word, do the following. If anything fails, go to next word.
  • 分析每个单词,执行以下操作。如果有任何失败,请转到下一个单词。


  • taking [0] and determining if it has an ascii value of 97-122 (making sure it's a letter). If it's not within those values, go the next character, and so on until it does (without exceeding the array bounds obviously). This removes any user formatting like a hyphen -apem.
  • 取[0]并确定它的ascii值是否为97-122(确保它是一个字母)。如果它不在这些值内,则转到下一个字符,依此类推,直到它完成(显然不超过数组边界)。这将删除任何用户格式,如连字符。

  • strcmp() the next 2 characters with each of primary game types until you reach a match. Otherwise failing and moving onto next word.
  • strcmp()每个主要游戏类型的下两个字符,直到你达到匹配。否则失败并转移到下一个单词。

  • With the remaining characters, strcmp each next pair of characters with each secondary or misc gametype. If any don't match fail out to next word, or if there is only 1 character left over fail out to next word
  • 对于剩余的字符,strcmp每个下一对字符与每个secondary或misc游戏类型。如果任何不匹配,则对下一个单词失败,或者如果剩下的只有一个字符,则对下一个单词失败

That should extract the game type, or you can blame the user for using a bad game name.

这应该提取游戏类型,或者你可以责怪用户使用糟糕的游戏名称。


Now for the harder part, verifying if the gametypes are compatible with each other. I suggest that you make a struct that holds a data structure of booleans representing each of the secondary game types. A std::map, or a single boolean array that could be accessed using a enum.

现在更难的部分,验证游戏类型是否相互兼容。我建议你创建一个结构,它包含代表每种二级游戏类型的布尔数据结构。可以使用枚举访问的std :: map或单个布尔数组。

Now you're going to need a data object to represent each primary game type as well as each secondary game type.

现在,您将需要一个数据对象来表示每个主要游戏类型以及每个辅助游戏类型。

Then just make an array of every gametype, both primary and secondary. Refer to code sample:

然后只需制作一个包含主要和次要游戏类型的数组。参考代码示例:

map<const char*, bool> mapSecondaryCompatibility;

struct tGameType
{
    char szName[3];
    mapSecondaryCompatibility m_compat;
}

As you see, there technically isn't a difference between your primary and secondary game types, they both have the same restriction... may not be compatible with another secondary game type.

如您所见,从技术上讲,您的主要和次要游戏类型之间没有区别,它们都有相同的限制......可能与其他次要游戏类型不兼容。

With this I'm sure you can figure out the rest. I hope it helps, I gotta get back to work :)

有了这个,我相信你可以弄明白其余的。我希望它有所帮助,我得回去工作:)

Oh and I'm a big fan of DotA... keep up the good work!

哦,我是DotA的忠实粉丝......保持良好的工作!

#2


Create bool arrays which replicate the tables you've put into comments. Except instead of an "X" or blank put "true" or "false" (so "true" means the combination of modes is valid and "false" means invalid).

创建bool数组,复制您放入注释的表。除了“X”或空白而不是“true”或“false”(因此“true”表示模式的组合有效,“false”表示无效)。

Use this table to lookup whether the combination is valid:

使用此表来查找组合是否有效:

 bool IsSecondaryValidWithPrimary(unsigned int primaryIndex, unsigned int secondaryIndex)
 {
     static bool secondaryValidWithPrimary[numPrimaryModes][numSecondaryModes] = {...}

     if (primaryIndex < numPrimaryModes && secondaryIndex < numSecondaryModes)
     {
         return secondaryValidWithPrimary[primaryIndex][secondaryIndex]
     }
     else
     {
         //... this should never happen, throw your favorite exception
     }
 }

Naturally this requires you to convert each 2 character string to the correct array index to be tested. Loop over every possible combination and check to see if its valid. I doubt you seriously care about performance in this setup, so this should work nicely.

当然,这需要您将每个2个字符的字符串转换为要测试的正确数组索引。循环遍历每个可能的组合,并检查它是否有效。我怀疑你是否真的关心这个设置中的性能,所以这应该很好用。

Do the same for the other validity check (secondary with another secondary) or any other combination of modes for which you have compatibility rules.

对其他有效性检查(辅助与另一个辅助)或您具有兼容性规则的任何其他模式组合执行相同操作。

#3


I might try putting each mode into a std::set with a white list of modes that are compatible with the given node. When you want to parse a mode string you make a copy of the primary white list. Then you step down through the string. If the next node is not in the white list then you have an invalid mode string. If the next mode is in the white list then you intersect the next node's white list with your working white list. Continue until you reach the end of the string or the white list is empty. If the white list is empty and you are not at the end of the string, it is invalid, otherwise it is good.

我可能会尝试将每个模式放入一个std :: set,其中包含与给定节点兼容的白色模式列表。如果要解析模式字符串,请复制主白名单。然后你走下字符串。如果下一个节点不在白名单中,那么您的模式字符串无效。如果下一个模式位于白名单中,则您将下一个节点的白名单与工作白名单相交。继续,直到到达字符串的末尾或白名单为空。如果白名单为空并且您不在字符串的末尾,则它无效,否则它是好的。

The misc modes probably don't belong in the white list (because they are on every white list).

misc模式可能不属于白名单(因为它们在每个白名单上)。

You might also try instead using a black list and creating a union at each step of the way, then bailing out if the mode is in the list.

您也可以尝试使用黑名单并在每一步创建一个联合,然后在模式列表中拯救。

#4


I would lean towards converting the game mode types into an enumeration. I might even wrap the mode in a class that can store the current game mode state and provide friendly accessors for other parts of the game to quickly query the current mode.

我倾向于将游戏模式类型转换为枚举。我甚至可以将模式包装在可以存储当前游戏模式状态的类中,并为游戏的其他部分提供友好的访问器以快速查询当前模式。

Internally I would create a std::map< int, std::vector< int > > to store a list of compatable modes. As soon as the command line is entered, I would convert the two character strings to the enumeration value. Then I would do a lookup in the compatable mode mapping to see if it's an allowed mode.

在内部我会创建一个std :: map >来存储一个可兼容的模式列表。输入命令行后,我会将两个字符串转换为枚举值。然后我会在兼容模式映射中进行查找,看看它是否是允许模式。 ,std>

How you fill the map is up to you - I'm thinking you could have a class that does it - a compatability loader, or you could drive it from a configuration file if you wanted end users to be able to modify available modes.

你如何填写地图取决于你 - 我认为你可以有一个类 - 一个兼容性加载器,或者如果你希望最终用户能够修改可用模式,你可以从配置文件中驱动它。

An enumeration would be nicer to work with so that you could do a lot of checking at compile time, instead of runtime checks. You do a single runtime check when you convert from the input string to the enumerations, and return a fail message to the user if it's not valid. Then all your other code is checked at compile time.

枚举可以更好地使用,以便您可以在编译时进行大量检查,而不是运行时检查。从输入字符串转换为枚举时,执行单个运行时检查,如果用户无效,则将失败消息返回给用户。然后在编译时检查所有其他代码。