SparseMatchFinder.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: SparseMatchFinder.h /////////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, March 2002
  25. // Desc:
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #pragma once
  28. #ifndef __SparseMatchFinder_H_
  29. #define __SparseMatchFinder_H_
  30. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  31. #include "Common/BitFlags.h"
  32. #include "Common/STLTypedefs.h"
  33. #if defined(_DEBUG) || defined(_INTERNAL)
  34. #define SPARSEMATCH_DEBUG
  35. #else
  36. #undef SPARSEMATCH_DEBUG
  37. #endif
  38. //-------------------------------------------------------------------------------------------------
  39. template<class MATCHABLE, class BITSET>
  40. class SparseMatchFinder
  41. {
  42. private:
  43. //-------------------------------------------------------------------------------------------------
  44. // TYPEDEFS
  45. //-------------------------------------------------------------------------------------------------
  46. struct HashMapHelper
  47. {
  48. size_t operator()(const BITSET& p) const
  49. {
  50. /// @todo srj -- provide a better hash function for BITSET
  51. size_t result = 0;
  52. for (int i = 0; i < p.size(); ++i)
  53. if (p.test(i))
  54. result += (i + 1);
  55. return result;
  56. }
  57. Bool operator()(const BITSET& a, const BITSET& b) const
  58. {
  59. return (a == b);
  60. }
  61. };
  62. struct MapHelper
  63. {
  64. bool operator()(const BITSET& a, const BITSET& b) const
  65. {
  66. int i;
  67. if (a.size() < b.size()) {
  68. return true;
  69. }
  70. for (i = 0; i < a.size(); ++i) {
  71. bool aVal = a.test(i);
  72. bool bVal = b.test(i);
  73. if (aVal && bVal) continue;
  74. if (!aVal && !bVal) continue;
  75. if (!aVal) return true;
  76. return false;
  77. }
  78. return false; // all bits match.
  79. }
  80. };
  81. //-------------------------------------------------------------------------------------------------
  82. //typedef std::hash_map< BITSET, const MATCHABLE*, HashMapHelper, HashMapHelper > HashMatchMap;
  83. typedef std::map< const BITSET, const MATCHABLE*, MapHelper> MatchMap;
  84. //-------------------------------------------------------------------------------------------------
  85. // MEMBER VARS
  86. //-------------------------------------------------------------------------------------------------
  87. mutable MatchMap m_bestMatches;
  88. //mutable HashMatchMap m_bestHashMatches;
  89. //-------------------------------------------------------------------------------------------------
  90. // METHODS
  91. //-------------------------------------------------------------------------------------------------
  92. //-------------------------------------------------------------------------------------------------
  93. inline static Int countConditionIntersection(const BITSET& a, const BITSET& b)
  94. {
  95. return a.countIntersection(b);
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. inline static Int countConditionInverseIntersection(const BITSET& a, const BITSET& b)
  99. {
  100. return a.countInverseIntersection(b);
  101. }
  102. //-------------------------------------------------------------------------------------------------
  103. const MATCHABLE* findBestInfoSlow(const std::vector<MATCHABLE>& v, const BITSET& bits) const
  104. {
  105. const MATCHABLE* result = NULL;
  106. Int bestYesMatch = 0; // want to maximize this
  107. Int bestYesExtraneousBits = 999; // want to minimize this
  108. #ifdef SPARSEMATCH_DEBUG
  109. Int numDupMatches = 0;
  110. AsciiString curBestMatchStr, dupMatchStr;
  111. #endif
  112. for (std::vector<MATCHABLE>::const_iterator it = v.begin(); it != v.end(); ++it)
  113. {
  114. for (Int i = it->getConditionsYesCount()-1; i >= 0; --i)
  115. {
  116. const BITSET& yesFlags = it->getNthConditionsYes(i);
  117. // the best match has the most "yes" matches and the smallest number of "no" matches.
  118. // if there are ties, then prefer the model with the smaller number of irrelevant 'yes' bits.
  119. // (example of why tiebreaker is necessary: if we want to match FRONTCRUSHED,
  120. // it would tie with both FRONTCRUSHED and FRONTCRUSHED|BACKCRUSHED, since they
  121. // both match a single YES bit.)
  122. Int yesMatch = countConditionIntersection(bits, yesFlags);
  123. Int yesExtraneousBits = countConditionInverseIntersection(bits, yesFlags);
  124. #ifdef SPARSEMATCH_DEBUG
  125. if (yesMatch == bestYesMatch &&
  126. yesExtraneousBits == bestYesExtraneousBits)
  127. {
  128. ++numDupMatches;
  129. dupMatchStr = it->getDescription();
  130. }
  131. #endif
  132. if ((yesMatch > bestYesMatch) ||
  133. (yesMatch >= bestYesMatch && yesExtraneousBits < bestYesExtraneousBits))
  134. {
  135. result = &(*it);
  136. bestYesMatch = yesMatch;
  137. bestYesExtraneousBits = yesExtraneousBits;
  138. #ifdef SPARSEMATCH_DEBUG
  139. numDupMatches = 0;
  140. curBestMatchStr = it->getDescription();
  141. #endif
  142. }
  143. } // end for i
  144. } // end for it
  145. #ifdef SPARSEMATCH_DEBUG
  146. if (numDupMatches > 0)
  147. {
  148. AsciiString curConditionStr;
  149. bits.buildDescription(&curConditionStr);
  150. DEBUG_CRASH(("ambiguous model match in findBestInfoSlow \n\nbetween \n(%s)\n<and>\n(%s)\n\n(%d extra matches found)\n\ncurrent bits are (\n%s)\n",
  151. curBestMatchStr.str(),
  152. dupMatchStr.str(),
  153. numDupMatches,
  154. curConditionStr.str()));
  155. }
  156. #endif
  157. return result;
  158. }
  159. //-------------------------------------------------------------------------------------------------
  160. public:
  161. //-------------------------------------------------------------------------------------------------
  162. void clear()
  163. {
  164. m_bestMatches.clear();
  165. }
  166. //-------------------------------------------------------------------------------------------------
  167. const MATCHABLE* findBestInfo(const std::vector<MATCHABLE>& v, const BITSET& bits) const
  168. {
  169. MatchMap::const_iterator it = m_bestMatches.find(bits);
  170. const MATCHABLE *first = NULL;
  171. if (it != m_bestMatches.end())
  172. {
  173. first = (*it).second;
  174. }
  175. if (first != NULL) {
  176. return first;
  177. }
  178. const MATCHABLE* info = findBestInfoSlow(v, bits);
  179. DEBUG_ASSERTCRASH(info != NULL, ("no suitable match for criteria was found!\n"));
  180. if (info != NULL) {
  181. m_bestMatches[bits] = info;
  182. }
  183. return info;
  184. }
  185. };
  186. #endif // __SparseMatchFinder_H_