Files
2026-05-19 19:43:23 -07:00

915 lines
17 KiB
C++

// Bot_wieghts.cpp
//
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
idBotFuzzyWeightManager botFuzzyWeightManager;
/*
========================
idBotFuzzyWeightManager::Init
========================
*/
void idBotFuzzyWeightManager::Init( void )
{
memset( &fuzzyseperators[0], 0, sizeof( fuzzyseperators ) );
}
/*
========================
idBotFuzzyWeightManager::AllocFuzzyWeight
========================
*/
fuzzyseperator_t* idBotFuzzyWeightManager::AllocFuzzyWeight( void )
{
for( int i = 0; i < MAX_FUZZY_OPERATORS; i++ )
{
if( fuzzyseperators[i].inUse == false )
{
memset( &fuzzyseperators[i], 0, sizeof( fuzzyseperator_t ) );
fuzzyseperators[i].inUse = true;
return &fuzzyseperators[i];
}
}
gameLocal.Error( "AllocFuzzyWeight: Not enough fuzzy weights\n" );
return NULL;
}
/*
========================
idBotFuzzyWeightManager::ReadValue
========================
*/
bool idBotFuzzyWeightManager::ReadValue( idParser& source, float* value )
{
idToken token;
if( value == NULL || !source.ReadToken( &token ) )
{
return false;
}
bool negative = false;
if( token == "-" )
{
negative = true;
if( !source.ExpectTokenType( TT_NUMBER, 0, &token ) )
{
return false;
}
}
if( token.type != TT_NUMBER )
{
source.Error( "invalid return value %s\n", token.c_str() );
return false;
}
*value = negative ? -token.GetFloatValue() : token.GetFloatValue();
return true;
}
/*
===================
idBotFuzzyWeightManager::ReadFuzzyWeight
===================
*/
int idBotFuzzyWeightManager::ReadFuzzyWeight( idParser& source, fuzzyseperator_t* fs )
{
if( source.CheckTokenString( "balance" ) )
{
fs->type = WT_BALANCE;
if( !source.ExpectTokenString( "(" ) )
{
return false;
}
if( !ReadValue( source, &fs->weight ) )
{
return false;
}
if( !source.ExpectTokenString( "," ) )
{
return false;
}
if( !ReadValue( source, &fs->minweight ) )
{
return false;
}
if( !source.ExpectTokenString( "," ) )
{
return false;
}
if( !ReadValue( source, &fs->maxweight ) )
{
return false;
}
if( !source.ExpectTokenString( ")" ) )
{
return false;
}
}
else
{
fs->type = 0;
if( !ReadValue( source, &fs->weight ) )
{
return false;
}
fs->minweight = fs->weight;
fs->maxweight = fs->weight;
}
if( !source.ExpectTokenString( ";" ) )
{
return false;
}
return true;
}
/*
===================
idBotFuzzyWeightManager::FreeFuzzySeperators_r
===================
*/
void idBotFuzzyWeightManager::FreeFuzzySeperators_r( fuzzyseperator_t* fs )
{
if( !fs )
{
return;
}
if( fs->child )
{
FreeFuzzySeperators_r( fs->child );
}
if( fs->next )
{
FreeFuzzySeperators_r( fs->next );
}
fs->inUse = false;
}
/*
===================
FreeWeightConfig2
===================
*/
void idBotFuzzyWeightManager::FreeWeightConfig2( weightconfig_t* config )
{
int i;
for( i = 0; i < config->numweights; i++ )
{
FreeFuzzySeperators_r( config->weights[i].firstseperator );
// jmarshall - todo
//if (config->weights[i].name)
// FreeMemory(config->weights[i].name);
// jmarshall end
}
}
/*
===================
idBotFuzzyWeightManager::FreeWeightConfig
===================
*/
void idBotFuzzyWeightManager::FreeWeightConfig( weightconfig_t* config )
{
//if (!LibVarGetValue("bot_reloadcharacters"))
// return;
FreeWeightConfig2( config );
}
/*
===================
idBotFuzzyWeightManager::ReadFuzzySeperators_r
===================
*/
fuzzyseperator_t* idBotFuzzyWeightManager::ReadFuzzySeperators_r( idParser& source )
{
int newindent, index, founddefault;
bool def;
idToken token;
fuzzyseperator_t* fs, * lastfs, * firstfs;
founddefault = false;
firstfs = NULL;
lastfs = NULL;
if( !source.ExpectTokenString( "(" ) )
{
return NULL;
}
if( !source.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) )
{
return NULL;
}
index = token.GetIntValue();
if( !source.ExpectTokenString( ")" ) )
{
return NULL;
}
if( !source.ExpectTokenString( "{" ) )
{
return NULL;
}
if( !source.ExpectAnyToken( &token ) )
{
return NULL;
}
do
{
def = ( token == "default" ); //!strcmp(token.string, "default");
if( def || ( token == "case" ) )
{
fs = AllocFuzzyWeight();
fs->index = index;
if( lastfs )
{
lastfs->next = fs;
}
else
{
firstfs = fs;
}
lastfs = fs;
if( def )
{
if( founddefault )
{
FreeFuzzySeperators_r( firstfs );
gameLocal.Error( "switch already has a default\n" );
return NULL;
}
fs->value = MAX_INVENTORYVALUE;
founddefault = true;
}
else
{
if( !source.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
fs->value = token.GetIntValue();
}
if( !source.ExpectTokenString( ":" ) || !source.ExpectAnyToken( &token ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
newindent = false;
if( token == "{" )
{
newindent = true;
if( !source.ExpectAnyToken( &token ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
}
if( token == "return" )
{
if( !ReadFuzzyWeight( source, fs ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
}
else if( token == "switch" )
{
fs->child = ReadFuzzySeperators_r( source );
if( !fs->child )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
}
else
{
gameLocal.Error( "invalid name %s\n", token.c_str() );
return NULL;
}
if( newindent )
{
if( !source.ExpectTokenString( "}" ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
}
}
else
{
gameLocal.Error( "invalid name %s\n", token.c_str() );
FreeFuzzySeperators_r( firstfs );
return NULL;
}
if( !source.ExpectAnyToken( &token ) )
{
FreeFuzzySeperators_r( firstfs );
return NULL;
}
}
while( token != "}" );
if( !founddefault )
{
source.Warning( "switch without default\n" );
fs = AllocFuzzyWeight();
fs->index = index;
fs->value = MAX_INVENTORYVALUE;
fs->weight = 0;
fs->next = NULL;
fs->child = NULL;
if( lastfs )
{
lastfs->next = fs;
}
else
{
firstfs = fs;
}
lastfs = fs;
}
return firstfs;
}
/*
=====================
idBotFuzzyWeightManager::ReadWeightConfig
=====================
*/
weightconfig_t* idBotFuzzyWeightManager::ReadWeightConfig( char* filename )
{
int newindent, avail = 0, n;
idToken token;
idParser source;
fuzzyseperator_t* fs;
weightconfig_t* config = NULL;
avail = -1;
for( n = 0; n < MAX_WEIGHT_FILES; n++ )
{
config = &weightFileList[n];
if( config->inUse )
{
if( config->filename == filename )
{
return config;
}
continue;
}
if( avail == -1 )
{
avail = n;
}
}
if( avail == -1 )
{
gameLocal.Error( "weightFileList was full trying to load %s\n", filename );
return NULL;
}
rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
if( !source.LoadFile( filename ) )
{
gameLocal.Error( "couldn't load %s\n", filename );
return NULL;
}
config = &weightFileList[avail];
config->Reset();
config->filename = filename;
//parse the item config file
while( source.ReadToken( &token ) )
{
if( token == "weight" )
{
if( config->numweights >= MAX_WEIGHTS )
{
gameLocal.Error( "too many fuzzy weights\n" );
break;
}
if( !source.ExpectTokenType( TT_STRING, 0, &token ) )
{
FreeWeightConfig( config );
return NULL;
}
token.StripDoubleQuotes();
config->weights[config->numweights].name = token.c_str();
if( !source.ExpectAnyToken( &token ) )
{
FreeWeightConfig( config );
return NULL;
}
newindent = false;
if( token == "{" )
{
newindent = true;
if( !source.ExpectAnyToken( &token ) )
{
FreeWeightConfig( config );
return NULL;
}
}
if( token == "switch" )
{
fs = ReadFuzzySeperators_r( source );
if( !fs )
{
FreeWeightConfig( config );
return NULL;
}
config->weights[config->numweights].firstseperator = fs;
}
else if( token == "return" )
{
fs = ( fuzzyseperator_t* )AllocFuzzyWeight();
fs->index = 0;
fs->value = MAX_INVENTORYVALUE;
fs->next = NULL;
fs->child = NULL;
if( !ReadFuzzyWeight( source, fs ) )
{
FreeWeightConfig( config );
return NULL;
}
config->weights[config->numweights].firstseperator = fs;
}
else
{
gameLocal.Error( "invalid name %s\n", token.c_str() );
FreeWeightConfig( config );
return NULL;
}
if( newindent )
{
if( !source.ExpectTokenString( "}" ) )
{
FreeWeightConfig( config );
return NULL;
}
}
config->numweights++;
}
else
{
gameLocal.Error( "invalid name %s\n", token.c_str() );
FreeWeightConfig( config );
return NULL;
}
}
//if the file was located in a pak file
common->Printf( "idBotFuzzyWeightManager::ReadWeightConfig: loaded %s\n", filename );
config->inUse = true;
return config;
}
/*
==================
idBotFuzzyWeightManager::FindFuzzyWeight
==================
*/
int idBotFuzzyWeightManager::FindFuzzyWeight( weightconfig_t* wc, char* name )
{
int i;
for( i = 0; i < wc->numweights; i++ )
{
if( wc->weights[i].name == name )
{
return i;
}
}
return -1;
}
/*
==================
idBotFuzzyWeightManager::FuzzyWeight_r
==================
*/
float idBotFuzzyWeightManager::FuzzyWeight_r( int* inventory, fuzzyseperator_t* fs )
{
float scale, w1, w2;
if( inventory[fs->index] < fs->value )
{
if( fs->child )
{
return FuzzyWeight_r( inventory, fs->child );
}
else
{
return fs->weight;
}
}
else if( fs->next )
{
if( inventory[fs->index] < fs->next->value )
{
//first weight
if( fs->child )
{
w1 = FuzzyWeight_r( inventory, fs->child );
}
else
{
w1 = fs->weight;
}
//second weight
if( fs->next->child )
{
w2 = FuzzyWeight_r( inventory, fs->next->child );
}
else
{
w2 = fs->next->weight;
}
//the scale factor
scale = ( float )( inventory[fs->index] - fs->value ) / ( float )( fs->next->value - fs->value );
//scale between the two weights
return ( 1 - scale ) * w1 + scale * w2;
}
return FuzzyWeight_r( inventory, fs->next );
}
return fs->weight;
}
/*
============================
idBotFuzzyWeightManager::FuzzyWeightUndecided_r
============================
*/
float idBotFuzzyWeightManager::FuzzyWeightUndecided_r( int* inventory, fuzzyseperator_t* fs )
{
float scale, w1, w2;
if( inventory[fs->index] < fs->value )
{
if( fs->child )
{
return FuzzyWeightUndecided_r( inventory, fs->child );
}
else
{
return fs->minweight + rvmBotUtil::random() * ( fs->maxweight - fs->minweight );
}
}
else if( fs->next )
{
if( inventory[fs->index] < fs->next->value )
{
//first weight
if( fs->child )
{
w1 = FuzzyWeightUndecided_r( inventory, fs->child );
}
else
{
w1 = fs->minweight + rvmBotUtil::random() * ( fs->maxweight - fs->minweight );
}
//second weight
if( fs->next->child )
{
w2 = FuzzyWeight_r( inventory, fs->next->child );
}
else
{
w2 = fs->next->minweight + rvmBotUtil::random() * ( fs->next->maxweight - fs->next->minweight );
}
//the scale factor
scale = ( float )( inventory[fs->index] - fs->value ) / ( float )( fs->next->value - fs->value );
//scale between the two weights
return ( 1 - scale ) * w1 + scale * w2;
}
return FuzzyWeightUndecided_r( inventory, fs->next );
}
return fs->weight;
}
/*
=================
idBotFuzzyWeightManager::FuzzyWeight
=================
*/
float idBotFuzzyWeightManager::FuzzyWeight( int* inventory, weightconfig_t* wc, int weightnum )
{
return FuzzyWeight_r( inventory, wc->weights[weightnum].firstseperator );
}
/*
=================
idBotFuzzyWeightManager::FuzzyWeightUndecided
=================
*/
float idBotFuzzyWeightManager::FuzzyWeightUndecided( int* inventory, weightconfig_t* wc, int weightnum )
{
return FuzzyWeightUndecided_r( inventory, wc->weights[weightnum].firstseperator );
}
/*
====================
idBotFuzzyWeightManager::EvolveFuzzySeperator_r
====================
*/
void idBotFuzzyWeightManager::EvolveFuzzySeperator_r( fuzzyseperator_t* fs )
{
if( fs->child )
{
EvolveFuzzySeperator_r( fs->child );
}
else if( fs->type == WT_BALANCE )
{
//every once in a while an evolution leap occurs, mutation
if( rvmBotUtil::random() < 0.01 )
{
fs->weight += rvmBotUtil::crandom() * ( fs->maxweight - fs->minweight );
}
else
{
fs->weight += rvmBotUtil::crandom() * ( fs->maxweight - fs->minweight ) * 0.5;
}
//modify bounds if necesary because of mutation
if( fs->weight < fs->minweight )
{
fs->minweight = fs->weight;
}
else if( fs->weight > fs->maxweight )
{
fs->maxweight = fs->weight;
}
}
if( fs->next )
{
EvolveFuzzySeperator_r( fs->next );
}
}
/*
====================
idBotFuzzyWeightManager::EvolveWeightConfig
====================
*/
void idBotFuzzyWeightManager::EvolveWeightConfig( weightconfig_t* config )
{
int i;
for( i = 0; i < config->numweights; i++ )
{
EvolveFuzzySeperator_r( config->weights[i].firstseperator );
}
}
/*
====================
idBotFuzzyWeightManager::ScaleWeight
====================
*/
void idBotFuzzyWeightManager::ScaleFuzzySeperator_r( fuzzyseperator_t* fs, float scale )
{
if( fs->child )
{
ScaleFuzzySeperator_r( fs->child, scale );
}
else if( fs->type == WT_BALANCE )
{
fs->weight = fs->minweight + ( fs->maxweight - fs->minweight ) * scale;
//get the weight between bounds
if( fs->weight < fs->minweight )
{
fs->weight = fs->minweight;
}
else if( fs->weight > fs->maxweight )
{
fs->weight = fs->maxweight;
}
}
if( fs->next )
{
ScaleFuzzySeperator_r( fs->next, scale );
}
}
/*
====================
idBotFuzzyWeightManager::ScaleWeight
====================
*/
void idBotFuzzyWeightManager::ScaleWeight( weightconfig_t* config, char* name, float scale )
{
int i;
if( scale < 0 )
{
scale = 0;
}
else if( scale > 1 )
{
scale = 1;
}
for( i = 0; i < config->numweights; i++ )
{
if( config->weights[i].name == name )
{
ScaleFuzzySeperator_r( config->weights[i].firstseperator, scale );
break;
}
}
}
/*
====================
idBotFuzzyWeightManager::ScaleFuzzySeperatorBalanceRange_r
====================
*/
void idBotFuzzyWeightManager::ScaleFuzzySeperatorBalanceRange_r( fuzzyseperator_t* fs, float scale )
{
if( fs->child )
{
ScaleFuzzySeperatorBalanceRange_r( fs->child, scale );
}
else if( fs->type == WT_BALANCE )
{
float mid = ( fs->minweight + fs->maxweight ) * 0.5;
//get the weight between bounds
fs->maxweight = mid + ( fs->maxweight - mid ) * scale;
fs->minweight = mid + ( fs->minweight - mid ) * scale;
if( fs->maxweight < fs->minweight )
{
fs->maxweight = fs->minweight;
}
}
if( fs->next )
{
ScaleFuzzySeperatorBalanceRange_r( fs->next, scale );
}
}
/*
====================
idBotFuzzyWeightManager::ScaleFuzzyBalanceRange
====================
*/
void idBotFuzzyWeightManager::ScaleFuzzyBalanceRange( weightconfig_t* config, float scale )
{
int i;
if( scale < 0 )
{
scale = 0;
}
else if( scale > 100 )
{
scale = 100;
}
for( i = 0; i < config->numweights; i++ )
{
ScaleFuzzySeperatorBalanceRange_r( config->weights[i].firstseperator, scale );
}
}
/*
====================
idBotFuzzyWeightManager::InterbreedFuzzySeperator_r
====================
*/
int idBotFuzzyWeightManager::InterbreedFuzzySeperator_r( fuzzyseperator_t* fs1, fuzzyseperator_t* fs2, fuzzyseperator_t* fsout )
{
if( fs1->child )
{
if( !fs2->child || !fsout->child )
{
gameLocal.Error( "cannot interbreed weight configs, unequal child\n" );
return false;
}
if( !InterbreedFuzzySeperator_r( fs1->child, fs2->child, fsout->child ) )
{
return false;
}
}
else if( fs1->type == WT_BALANCE )
{
if( fs2->type != WT_BALANCE || fsout->type != WT_BALANCE )
{
gameLocal.Error( "cannot interbreed weight configs, unequal balance\n" );
return false;
}
fsout->weight = ( fs1->weight + fs2->weight ) / 2;
if( fsout->weight > fsout->maxweight )
{
fsout->maxweight = fsout->weight;
}
if( fsout->weight < fsout->minweight )
{
fsout->minweight = fsout->weight;
}
}
if( fs1->next )
{
if( !fs2->next || !fsout->next )
{
gameLocal.Error( "cannot interbreed weight configs, unequal next\n" );
return false;
}
if( !InterbreedFuzzySeperator_r( fs1->next, fs2->next, fsout->next ) )
{
return false;
}
}
return true;
}
/*
====================
idBotFuzzyWeightManager::InterbreedWeightConfigs
====================
*/
void idBotFuzzyWeightManager::InterbreedWeightConfigs( weightconfig_t* config1, weightconfig_t* config2, weightconfig_t* configout )
{
int i;
if( config1->numweights != config2->numweights ||
config1->numweights != configout->numweights )
{
gameLocal.Error( "cannot interbreed weight configs, unequal numweights\n" );
return;
}
for( i = 0; i < config1->numweights; i++ )
{
InterbreedFuzzySeperator_r( config1->weights[i].firstseperator,
config2->weights[i].firstseperator,
configout->weights[i].firstseperator );
}
}
/*
=======================
idBotFuzzyWeightManager::BotShutdownWeights
=======================
*/
void idBotFuzzyWeightManager::BotShutdownWeights( void )
{
int i;
for( i = 0; i < MAX_WEIGHT_FILES; i++ )
{
if( weightFileList[i].inUse )
{
FreeWeightConfig2( &weightFileList[i] );
}
weightFileList[i].Reset();
}
}