2009-06-12 113 views
3

問題:我想從使用C++的遊戲名稱中提取古代防禦(DotA)的有效遊戲模式。C++ - 如何提取字符串中的有效字符串?

詳情:

  • 遊戲的名字就可以了,最多31個字符長
  • 有三種遊戲模式類型:初級,中級和雜項
    • 只能有選擇1個主要遊戲模式
    • 某些主要遊戲模式與某些輔助遊戲模式不兼容
    • 某些次級遊戲模式與其他次級遊戲模式
    • 雜遊戲模式可與所有其它的遊戲模式

這裏被組合是各種遊戲模式的列表不相容,具有圖,描述了該次級模式的每個模式與兼容(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 
}; 

實例:下面是一些示例輸入,將具有所需的輸出:

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

「DOTA AREMDM美國LC」 - > 「aremdm」

「的DotA v6.60 -ApEmDuSpId美國BL」 - > 「apemduspid」

注:溶液沒有按不一定必須提供實際的代碼,僞代碼,甚至只是解釋你如何處理它是可以接受的和首選的。此外,解決方案需要足夠靈活,以便我可以輕鬆添加其他遊戲模式。假設在遊戲名稱中,遊戲模式總是以主遊戲模式開始也是安全的。


結果:

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

輸出:

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

模式:rdem


如果有人想查看這段代碼,並讓我知道他們會改變什麼,那將不勝感激。

謝謝。

+0

我完全不明白,你需要:1.從遊戲名稱中提取遊戲模式。 2.驗證它們是否相互兼容? – 2009-06-12 12:44:47

+0

這是正確的。 – xian 2009-06-12 12:55:44

回答

1

如果沒有更多規則,從主機的遊戲名稱中提取遊戲類型將很困難。如果你真的爲了避免給最終用戶更多的規則,你可以嘗試以下...

  • ToLower將()整個遊戲名稱字符串。
  • 使用空格分隔符分隔遊戲名稱。
  • 分析每個單詞,請執行以下操作。如果有任何失敗,請轉到下一個單詞。

  • 取[0],並確定其是否具有的97-122(確保它是一個字母)的ASCII值。如果它不在這些值中,則轉到下一個字符,直到它出現(顯然不超過數組邊界)。這將刪除像連字符一樣的任何用戶格式。
  • strcmp()接下來的每個主要遊戲類型的2個字符,直到您匹配。否則失敗並轉移到下一個單詞。
  • 使用剩餘的字符,strcmp每個下一對字符與每個輔助或misc遊戲類型。如果有任何不匹配失敗到下一個單詞,或者如果只剩下1個字符,則失敗到下一個單詞

這應該提取遊戲類型,或者您可以責怪用戶使用糟糕的遊戲名稱。


現在對於更難的部分,驗證遊戲類型是否相互兼容。我建議你製作一個結構體,它保存代表每種次要遊戲類型的布爾值數據結構。一個std :: map或一個可以使用enum訪問的布爾數組。

現在你需要一個數據對象來表示每個主要遊戲類型以及作爲每個輔助遊戲類型。

然後,只需製作一個包含主要和次要遊戲類型的數組。請參閱代碼示例:

map<const char*, bool> mapSecondaryCompatibility; 

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

正如你看到的,有技術上是不是你的主要和次要的遊戲類型之間的區別,它們都具有相同的限制......可以不與其他輔助遊戲類型兼容。

有了這個,我相信你可以找出其餘的。我希望它有幫助,我得回去工作:)

哦,我是DotA的忠實粉絲......繼續努力!

2

創建bool數組,將您放入註釋的表進行復制。除了用「X」或空白表示「真」或「假」(所以「真」意味着模式的組合是有效的,「假」意味着無效)。

使用此表中查找該組合是否有效:

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

當然,這需要你每2字符串轉換爲正確的數組索引進行測試。循環播放每種可能的組合並檢查其有效性。我懷疑你認真關心這個設置的性能,所以這應該很好地工作。

對其他有效性檢查(輔助輔助輔助功能)或其他兼容性規則的任何其他模式組合執行相同操作。

0

我會傾向於將遊戲模式類型轉換爲枚舉。我甚至可以將模式包裝在一個可以存儲當前遊戲模式狀態的類中,併爲遊戲的其他部分提供友好訪問器以快速查詢當前模式。

在內部我會創建一個std :: map < int,std :: vector < int>>來存儲一系列可兼容模式。只要輸入命令行,我會將兩個字符串轉換爲枚舉值。然後,我會在兼容模式映射中查看它是否是允許的模式。

你如何填寫地圖取決於你 - 我認爲你可以有一個類來完成它 - 兼容性加載器,或者你可以從配置文件中驅動它,如果你想讓最終用戶能夠修改可用的模式。

枚舉會更好,因此您可以在編譯時進行大量檢查而不是運行時檢查。當您從輸入字符串轉換爲枚舉時,您會執行一次運行時檢查,並在用戶無效時向用戶返回失敗消息。然後在編譯時檢查所有其他代碼。

1

我可能會嘗試將每個模式放入一個std :: set中,其中白名單與給定節點兼容。當你想解析一個模式字符串時,你需要製作一個主白名單的副本。然後你逐步穿過繩子。如果下一個節點不在白名單中,那麼你有一個無效的模式字符串。如果下一個模式在白名單中,則相交,將下一個節點的白名單與您的工作白名單相交。繼續,直到到達字符串末尾或白名單爲空。如果白名單是空的,而你不在字符串的末尾,那麼它是無效的,否則它是好的。

misc模式可能不屬於白名單(因爲它們在每個白名單中)。

您也可以嘗試,而不是使用黑名單並在每一步的方式創造一個工會,再想逃出來,如果模式是在列表中。

相關問題