| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // RandomValue.cpp
- // Pseudo-random number generators
- // Author: Michael S. Booth, January 1998
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Lib/BaseType.h"
- #include "Common/RandomValue.h"
- #include "Common/CRC.h"
- #include "Common/Debug.h"
- #include "GameLogic/GameLogic.h"
- //#define DETERMINISTIC // to allow repetition for debugging
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #undef DEBUG_RANDOM_CLIENT
- #undef DEBUG_RANDOM_LOGIC
- #undef DEBUG_RANDOM_AUDIO
- //#define DEBUG_RANDOM_CLIENT
- //#define DEBUG_RANDOM_LOGIC
- //#define DEBUG_RANDOM_AUDIO
- static const Real theMultFactor = 1.0f / (powf(2, 8 * sizeof(UnsignedInt)) - 1.0f);
- // Initial seed values.
- static UnsignedInt theGameClientSeed[6] =
- {
- 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
- };
- static UnsignedInt theGameAudioSeed[6] =
- {
- 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
- };
- static UnsignedInt theGameLogicBaseSeed = 0;
- static UnsignedInt theGameLogicSeed[6] =
- {
- 0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
- };
- // 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.
- #define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B)))
- static UnsignedInt randomValue(UnsignedInt *seed)
- {
- UnsignedInt ax;
- UnsignedInt c = 0;
-
-
- ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */
- /* add ax,seed+16 */
- seed[4] = ax; /* mov seed+8,ax */
-
- ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */
- seed[3] = ax; /* mov seed+12,ax */
-
- ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */
- seed[2] = ax; /* mov seed+8,ax */
-
- ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */
- seed[1] = ax; /* mov seed+4,ax */
-
- ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */
- seed[0] = ax; /* mov seed+0,ax */
-
- /* Increment seed array, bubbling up the carries. */
- if (!++seed[5])
- {
- if (!++seed[4])
- {
- if (!++seed[3])
- {
- if (!++seed[2])
- {
- if (!++seed[1])
- {
- ++seed[0];
- ++ax;
- }
- }
- }
- }
- }
- return(ax);
- }
- static void seedRandom(UnsignedInt SEED, UnsignedInt *seed)
- {
- UnsignedInt ax;
- ax = SEED; /* mov eax,SEED */
- ax += 0xf22d0e56; /* add eax,0f22d0e56h */
- seed[0] = ax; /* mov seed,eax */
- ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */
- seed[1] = ax; /* mov seed+4,eax */
- ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */
- seed[2] = ax; /* mov seed+8,eax */
- ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */
- seed[3] = ax; /* mov seed+12,eax */
- ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */
- seed[4] = ax; /* mov seed+16,eax */
- ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */
- seed[5] = ax; /* mov seed+20,eax */
- }
- //
- // It is necessary to separate the GameClient and GameLogic usage of random
- // values to ensure that the GameLogic remains deterministic, regardless
- // of the effects displayed on the GameClient.
- //
- UnsignedInt GetGameLogicRandomSeed( void )
- {
- return theGameLogicBaseSeed;
- }
- UnsignedInt GetGameLogicRandomSeedCRC( void )
- {
- CRC c;
- c.computeCRC(theGameLogicSeed, 6*sizeof(UnsignedInt));
- return c.get();
- }
- void InitRandom( void )
- {
- #ifdef DETERMINISTIC
- // needs to be the same every time
- seedRandom(0, theGameClientSeed);
- seedRandom(0, theGameAudioSeed);
- seedRandom(0, theGameLogicSeed);
- theGameLogicBaseSeed = 0;
- #else
- time_t seconds = time( NULL );
- seedRandom(seconds, theGameAudioSeed);
- seedRandom(seconds, theGameClientSeed);
- seedRandom(seconds, theGameLogicSeed);
- theGameLogicBaseSeed = seconds;
- #endif
- }
- void InitRandom( UnsignedInt seed )
- {
- seedRandom(seed, theGameAudioSeed);
- seedRandom(seed, theGameClientSeed);
- seedRandom(seed, theGameLogicSeed);
- theGameLogicBaseSeed = seed;
- #ifdef DEBUG_RANDOM_LOGIC
- DEBUG_LOG(( "InitRandom %08lx\n",seed));
- #endif
- }
- void InitGameLogicRandom( UnsignedInt seed )
- {
- #ifdef DETERMINISTIC
- // needs to be the same every time
- seedRandom(0, theGameLogicSeed);
- theGameLogicBaseSeed = 0;
- #else
- seedRandom(seed, theGameLogicSeed);
- theGameLogicBaseSeed = seed;
- #endif
- #ifdef DEBUG_RANDOM_LOGIC
- DEBUG_LOG(( "InitRandom Logic %08lx\n",seed));
- #endif
- }
- //
- // Integer random value
- //
- Int GetGameLogicRandomValue( int lo, int hi, char *file, int line )
- {
- //Int delta = hi - lo + 1;
- //Int rval;
- //if (delta == 0)
- //return hi;
- //rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
- UnsignedInt delta = hi - lo + 1;
- //UnsignedInt temp;
- Int rval;
- if (delta == 0)
- return hi;
- rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
- //temp = randomValue(theGameLogicSeed);
- //temp = temp % delta;
- //rval = temp + lo;
- /**/
- #ifdef DEBUG_RANDOM_LOGIC
- DEBUG_LOG(( "%d: GetGameLogicRandomValue = %d (%d - %d), %s line %d\n",
- TheGameLogic->getFrame(), rval, lo, hi, file, line ));
- #endif
- /**/
- return rval;
- }
- //
- // Integer random value
- //
- Int GetGameClientRandomValue( int lo, int hi, char *file, int line )
- {
- UnsignedInt delta = hi - lo + 1;
- Int rval;
- if (delta == 0)
- return hi;
- rval = ((Int)(randomValue(theGameClientSeed) % delta)) + lo;
- /**/
- #ifdef DEBUG_RANDOM_CLIENT
- DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n",
- TheGameLogic->getFrame(), rval, lo, hi, file, line ));
- #endif
- /**/
- return rval;
- }
- //
- // Integer random value
- //
- Int GetGameAudioRandomValue( int lo, int hi, char *file, int line )
- {
- UnsignedInt delta = hi - lo + 1;
- Int rval;
- if (delta == 0)
- return hi;
- rval = ((Int)(randomValue(theGameAudioSeed) % delta)) + lo;
- /**/
- #ifdef DEBUG_RANDOM_AUDIO
- DEBUG_LOG(( "%d: GetGameAudioRandomValue = %d (%d - %d), %s line %d\n",
- TheGameLogic->getFrame(), rval, lo, hi, file, line ));
- #endif
- /**/
- return rval;
- }
- //
- // Real valued random value
- //
- Real GetGameLogicRandomValueReal( Real lo, Real hi, char *file, int line )
- {
- Real delta = hi - lo;
- Real rval;
- if (delta <= 0.0f)
- return hi;
- rval = ((Real)(randomValue(theGameLogicSeed)) * theMultFactor ) * delta + lo;
- DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
- /**/
- #ifdef DEBUG_RANDOM_LOGIC
- DEBUG_LOG(( "%d: GetGameLogicRandomValueReal = %f, %s line %d\n",
- TheGameLogic->getFrame(), rval, file, line ));
- #endif
- /**/
- return rval;
- }
- //
- // Real valued random value
- //
- Real GetGameClientRandomValueReal( Real lo, Real hi, char *file, int line )
- {
- Real delta = hi - lo;
- Real rval;
- if (delta <= 0.0f)
- return hi;
- rval = ((Real)(randomValue(theGameClientSeed)) * theMultFactor ) * delta + lo;
- DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
- /**/
- #ifdef DEBUG_RANDOM_CLIENT
- DEBUG_LOG(( "%d: GetGameClientRandomValueReal = %f, %s line %d\n",
- TheGameLogic->getFrame(), rval, file, line ));
- #endif
- /**/
- return rval;
- }
- //
- // Real valued random value
- //
- Real GetGameAudioRandomValueReal( Real lo, Real hi, char *file, int line )
- {
- Real delta = hi - lo;
- Real rval;
- if (delta <= 0.0f)
- return hi;
- rval = ((Real)(randomValue(theGameAudioSeed)) * theMultFactor ) * delta + lo;
- DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
- /**/
- #ifdef DEBUG_RANDOM_AUDIO
- DEBUG_LOG(( "%d: GetGameAudioRandomValueReal = %f, %s line %d\n",
- TheGameLogic->getFrame(), rval, file, line ));
- #endif
- /**/
- return rval;
- }
- //--------------------------------------------------------------------------------------------------------------
- // GameClientRandomVariable
- //
- /*static*/ const char *GameClientRandomVariable::DistributionTypeNames[] =
- {
- "CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
- };
- /**
- define the range of random values, and the distribution of values
- */
- void GameClientRandomVariable::setRange( Real low, Real high, DistributionType type )
- {
- DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameClientRandomVariables should have low == high"));
- m_low = low;
- m_high = high;
- m_type = type;
- }
- /**
- * Return a value from the random distribution
- */
- Real GameClientRandomVariable::getValue( void ) const
- {
- switch( m_type )
- {
- case CONSTANT:
- DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameClientRandomVariable\n"));
- if (m_low == m_high) {
- return m_low;
- } // else return as though a UNIFORM.
- case UNIFORM:
- return GameClientRandomValueReal( m_low, m_high );
- default:
- /// @todo fill in support for nonuniform GameClientRandomVariables.
- DEBUG_CRASH(("unsupported DistributionType in GameClientRandomVariable::getValue\n"));
- return 0.0f;
- }
- }
- //--------------------------------------------------------------------------------------------------------------
- // GameLogicRandomVariable
- //
- /*static*/ const char *GameLogicRandomVariable::DistributionTypeNames[] =
- {
- "CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
- };
- /**
- define the range of random values, and the distribution of values
- */
- void GameLogicRandomVariable::setRange( Real low, Real high, DistributionType type )
- {
- DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameLogicRandomVariables should have low == high"));
- m_low = low;
- m_high = high;
- m_type = type;
- }
- /**
- * Return a value from the random distribution
- */
- Real GameLogicRandomVariable::getValue( void ) const
- {
- switch( m_type )
- {
- case CONSTANT:
- DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameLogicRandomVariable"));
- if (m_low == m_high) {
- return m_low;
- } // else return as though a UNIFORM.
- case UNIFORM:
- return GameLogicRandomValueReal( m_low, m_high );
- default:
- /// @todo fill in support for nonuniform GameLogicRandomVariables.
- DEBUG_CRASH(("unsupported DistributionType in GameLogicRandomVariable::getValue\n"));
- return 0.0f;
- }
- }
|