RandomValue.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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. // RandomValue.cpp
  24. // Pseudo-random number generators
  25. // Author: Michael S. Booth, January 1998
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Lib/BaseType.h"
  28. #include "Common/RandomValue.h"
  29. #include "Common/CRC.h"
  30. #include "Common/Debug.h"
  31. #include "GameLogic/GameLogic.h"
  32. //#define DETERMINISTIC // to allow repetition for debugging
  33. #ifdef _INTERNAL
  34. // for occasional debugging...
  35. //#pragma optimize("", off)
  36. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  37. #endif
  38. #undef DEBUG_RANDOM_CLIENT
  39. #undef DEBUG_RANDOM_LOGIC
  40. #undef DEBUG_RANDOM_AUDIO
  41. //#define DEBUG_RANDOM_CLIENT
  42. //#define DEBUG_RANDOM_LOGIC
  43. //#define DEBUG_RANDOM_AUDIO
  44. static const Real theMultFactor = 1.0f / (powf(2, 8 * sizeof(UnsignedInt)) - 1.0f);
  45. // Initial seed values.
  46. static UnsignedInt theGameClientSeed[6] =
  47. {
  48. 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
  49. };
  50. static UnsignedInt theGameAudioSeed[6] =
  51. {
  52. 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
  53. };
  54. static UnsignedInt theGameLogicBaseSeed = 0;
  55. static UnsignedInt theGameLogicSeed[6] =
  56. {
  57. 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
  58. };
  59. // Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one.
  60. #define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B)))
  61. static UnsignedInt randomValue(UnsignedInt *seed)
  62. {
  63. UnsignedInt ax;
  64. UnsignedInt c = 0;
  65. ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */
  66. /* add ax,seed+16 */
  67. seed[4] = ax; /* mov seed+8,ax */
  68. ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */
  69. seed[3] = ax; /* mov seed+12,ax */
  70. ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */
  71. seed[2] = ax; /* mov seed+8,ax */
  72. ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */
  73. seed[1] = ax; /* mov seed+4,ax */
  74. ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */
  75. seed[0] = ax; /* mov seed+0,ax */
  76. /* Increment seed array, bubbling up the carries. */
  77. if (!++seed[5])
  78. {
  79. if (!++seed[4])
  80. {
  81. if (!++seed[3])
  82. {
  83. if (!++seed[2])
  84. {
  85. if (!++seed[1])
  86. {
  87. ++seed[0];
  88. ++ax;
  89. }
  90. }
  91. }
  92. }
  93. }
  94. return(ax);
  95. }
  96. static void seedRandom(UnsignedInt SEED, UnsignedInt *seed)
  97. {
  98. UnsignedInt ax;
  99. ax = SEED; /* mov eax,SEED */
  100. ax += 0xf22d0e56; /* add eax,0f22d0e56h */
  101. seed[0] = ax; /* mov seed,eax */
  102. ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */
  103. seed[1] = ax; /* mov seed+4,eax */
  104. ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */
  105. seed[2] = ax; /* mov seed+8,eax */
  106. ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */
  107. seed[3] = ax; /* mov seed+12,eax */
  108. ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */
  109. seed[4] = ax; /* mov seed+16,eax */
  110. ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */
  111. seed[5] = ax; /* mov seed+20,eax */
  112. }
  113. //
  114. // It is necessary to separate the GameClient and GameLogic usage of random
  115. // values to ensure that the GameLogic remains deterministic, regardless
  116. // of the effects displayed on the GameClient.
  117. //
  118. UnsignedInt GetGameLogicRandomSeed( void )
  119. {
  120. return theGameLogicBaseSeed;
  121. }
  122. UnsignedInt GetGameLogicRandomSeedCRC( void )
  123. {
  124. CRC c;
  125. c.computeCRC(theGameLogicSeed, 6*sizeof(UnsignedInt));
  126. return c.get();
  127. }
  128. void InitRandom( void )
  129. {
  130. #ifdef DETERMINISTIC
  131. // needs to be the same every time
  132. seedRandom(0, theGameClientSeed);
  133. seedRandom(0, theGameAudioSeed);
  134. seedRandom(0, theGameLogicSeed);
  135. theGameLogicBaseSeed = 0;
  136. #else
  137. time_t seconds = time( NULL );
  138. seedRandom(seconds, theGameAudioSeed);
  139. seedRandom(seconds, theGameClientSeed);
  140. seedRandom(seconds, theGameLogicSeed);
  141. theGameLogicBaseSeed = seconds;
  142. #endif
  143. }
  144. void InitRandom( UnsignedInt seed )
  145. {
  146. seedRandom(seed, theGameAudioSeed);
  147. seedRandom(seed, theGameClientSeed);
  148. seedRandom(seed, theGameLogicSeed);
  149. theGameLogicBaseSeed = seed;
  150. #ifdef DEBUG_RANDOM_LOGIC
  151. DEBUG_LOG(( "InitRandom %08lx\n",seed));
  152. #endif
  153. }
  154. void InitGameLogicRandom( UnsignedInt seed )
  155. {
  156. #ifdef DETERMINISTIC
  157. // needs to be the same every time
  158. seedRandom(0, theGameLogicSeed);
  159. theGameLogicBaseSeed = 0;
  160. #else
  161. seedRandom(seed, theGameLogicSeed);
  162. theGameLogicBaseSeed = seed;
  163. #endif
  164. #ifdef DEBUG_RANDOM_LOGIC
  165. DEBUG_LOG(( "InitRandom Logic %08lx\n",seed));
  166. #endif
  167. }
  168. //
  169. // Integer random value
  170. //
  171. Int GetGameLogicRandomValue( int lo, int hi, char *file, int line )
  172. {
  173. //Int delta = hi - lo + 1;
  174. //Int rval;
  175. //if (delta == 0)
  176. //return hi;
  177. //rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
  178. UnsignedInt delta = hi - lo + 1;
  179. //UnsignedInt temp;
  180. Int rval;
  181. if (delta == 0)
  182. return hi;
  183. rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
  184. //temp = randomValue(theGameLogicSeed);
  185. //temp = temp % delta;
  186. //rval = temp + lo;
  187. /**/
  188. #ifdef DEBUG_RANDOM_LOGIC
  189. DEBUG_LOG(( "%d: GetGameLogicRandomValue = %d (%d - %d), %s line %d\n",
  190. TheGameLogic->getFrame(), rval, lo, hi, file, line ));
  191. #endif
  192. /**/
  193. return rval;
  194. }
  195. //
  196. // Integer random value
  197. //
  198. Int GetGameClientRandomValue( int lo, int hi, char *file, int line )
  199. {
  200. UnsignedInt delta = hi - lo + 1;
  201. Int rval;
  202. if (delta == 0)
  203. return hi;
  204. rval = ((Int)(randomValue(theGameClientSeed) % delta)) + lo;
  205. /**/
  206. #ifdef DEBUG_RANDOM_CLIENT
  207. DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n",
  208. TheGameLogic->getFrame(), rval, lo, hi, file, line ));
  209. #endif
  210. /**/
  211. return rval;
  212. }
  213. //
  214. // Integer random value
  215. //
  216. Int GetGameAudioRandomValue( int lo, int hi, char *file, int line )
  217. {
  218. UnsignedInt delta = hi - lo + 1;
  219. Int rval;
  220. if (delta == 0)
  221. return hi;
  222. rval = ((Int)(randomValue(theGameAudioSeed) % delta)) + lo;
  223. /**/
  224. #ifdef DEBUG_RANDOM_AUDIO
  225. DEBUG_LOG(( "%d: GetGameAudioRandomValue = %d (%d - %d), %s line %d\n",
  226. TheGameLogic->getFrame(), rval, lo, hi, file, line ));
  227. #endif
  228. /**/
  229. return rval;
  230. }
  231. //
  232. // Real valued random value
  233. //
  234. Real GetGameLogicRandomValueReal( Real lo, Real hi, char *file, int line )
  235. {
  236. Real delta = hi - lo;
  237. Real rval;
  238. if (delta <= 0.0f)
  239. return hi;
  240. rval = ((Real)(randomValue(theGameLogicSeed)) * theMultFactor ) * delta + lo;
  241. DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
  242. /**/
  243. #ifdef DEBUG_RANDOM_LOGIC
  244. DEBUG_LOG(( "%d: GetGameLogicRandomValueReal = %f, %s line %d\n",
  245. TheGameLogic->getFrame(), rval, file, line ));
  246. #endif
  247. /**/
  248. return rval;
  249. }
  250. //
  251. // Real valued random value
  252. //
  253. Real GetGameClientRandomValueReal( Real lo, Real hi, char *file, int line )
  254. {
  255. Real delta = hi - lo;
  256. Real rval;
  257. if (delta <= 0.0f)
  258. return hi;
  259. rval = ((Real)(randomValue(theGameClientSeed)) * theMultFactor ) * delta + lo;
  260. DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
  261. /**/
  262. #ifdef DEBUG_RANDOM_CLIENT
  263. DEBUG_LOG(( "%d: GetGameClientRandomValueReal = %f, %s line %d\n",
  264. TheGameLogic->getFrame(), rval, file, line ));
  265. #endif
  266. /**/
  267. return rval;
  268. }
  269. //
  270. // Real valued random value
  271. //
  272. Real GetGameAudioRandomValueReal( Real lo, Real hi, char *file, int line )
  273. {
  274. Real delta = hi - lo;
  275. Real rval;
  276. if (delta <= 0.0f)
  277. return hi;
  278. rval = ((Real)(randomValue(theGameAudioSeed)) * theMultFactor ) * delta + lo;
  279. DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
  280. /**/
  281. #ifdef DEBUG_RANDOM_AUDIO
  282. DEBUG_LOG(( "%d: GetGameAudioRandomValueReal = %f, %s line %d\n",
  283. TheGameLogic->getFrame(), rval, file, line ));
  284. #endif
  285. /**/
  286. return rval;
  287. }
  288. //--------------------------------------------------------------------------------------------------------------
  289. // GameClientRandomVariable
  290. //
  291. /*static*/ const char *GameClientRandomVariable::DistributionTypeNames[] =
  292. {
  293. "CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
  294. };
  295. /**
  296. define the range of random values, and the distribution of values
  297. */
  298. void GameClientRandomVariable::setRange( Real low, Real high, DistributionType type )
  299. {
  300. DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameClientRandomVariables should have low == high"));
  301. m_low = low;
  302. m_high = high;
  303. m_type = type;
  304. }
  305. /**
  306. * Return a value from the random distribution
  307. */
  308. Real GameClientRandomVariable::getValue( void ) const
  309. {
  310. switch( m_type )
  311. {
  312. case CONSTANT:
  313. DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameClientRandomVariable\n"));
  314. if (m_low == m_high) {
  315. return m_low;
  316. } // else return as though a UNIFORM.
  317. case UNIFORM:
  318. return GameClientRandomValueReal( m_low, m_high );
  319. default:
  320. /// @todo fill in support for nonuniform GameClientRandomVariables.
  321. DEBUG_CRASH(("unsupported DistributionType in GameClientRandomVariable::getValue\n"));
  322. return 0.0f;
  323. }
  324. }
  325. //--------------------------------------------------------------------------------------------------------------
  326. // GameLogicRandomVariable
  327. //
  328. /*static*/ const char *GameLogicRandomVariable::DistributionTypeNames[] =
  329. {
  330. "CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
  331. };
  332. /**
  333. define the range of random values, and the distribution of values
  334. */
  335. void GameLogicRandomVariable::setRange( Real low, Real high, DistributionType type )
  336. {
  337. DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameLogicRandomVariables should have low == high"));
  338. m_low = low;
  339. m_high = high;
  340. m_type = type;
  341. }
  342. /**
  343. * Return a value from the random distribution
  344. */
  345. Real GameLogicRandomVariable::getValue( void ) const
  346. {
  347. switch( m_type )
  348. {
  349. case CONSTANT:
  350. DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameLogicRandomVariable"));
  351. if (m_low == m_high) {
  352. return m_low;
  353. } // else return as though a UNIFORM.
  354. case UNIFORM:
  355. return GameLogicRandomValueReal( m_low, m_high );
  356. default:
  357. /// @todo fill in support for nonuniform GameLogicRandomVariables.
  358. DEBUG_CRASH(("unsupported DistributionType in GameLogicRandomVariable::getValue\n"));
  359. return 0.0f;
  360. }
  361. }