Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

be_ai_weight.c (26686B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 /*****************************************************************************
     24  * name:		be_ai_weight.c
     25  *
     26  * desc:		fuzzy logic
     27  *
     28  * $Archive: /MissionPack/code/botlib/be_ai_weight.c $
     29  *
     30  *****************************************************************************/
     31 
     32 #include "../game/q_shared.h"
     33 #include "l_memory.h"
     34 #include "l_log.h"
     35 #include "l_utils.h"
     36 #include "l_script.h"
     37 #include "l_precomp.h"
     38 #include "l_struct.h"
     39 #include "l_libvar.h"
     40 #include "aasfile.h"
     41 #include "../game/botlib.h"
     42 #include "../game/be_aas.h"
     43 #include "be_aas_funcs.h"
     44 #include "be_interface.h"
     45 #include "be_ai_weight.h"
     46 
     47 #define MAX_INVENTORYVALUE			999999
     48 #define EVALUATERECURSIVELY
     49 
     50 #define MAX_WEIGHT_FILES			128
     51 weightconfig_t	*weightFileList[MAX_WEIGHT_FILES];
     52 
     53 //===========================================================================
     54 //
     55 // Parameter:				-
     56 // Returns:					-
     57 // Changes Globals:		-
     58 //===========================================================================
     59 int ReadValue(source_t *source, float *value)
     60 {
     61 	token_t token;
     62 
     63 	if (!PC_ExpectAnyToken(source, &token)) return qfalse;
     64 	if (!strcmp(token.string, "-"))
     65 	{
     66 		SourceWarning(source, "negative value set to zero\n");
     67 		if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse;
     68 	} //end if
     69 	if (token.type != TT_NUMBER)
     70 	{
     71 		SourceError(source, "invalid return value %s\n", token.string);
     72 		return qfalse;
     73 	} //end if
     74 	*value = token.floatvalue;
     75 	return qtrue;
     76 } //end of the function ReadValue
     77 //===========================================================================
     78 //
     79 // Parameter:				-
     80 // Returns:					-
     81 // Changes Globals:		-
     82 //===========================================================================
     83 int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs)
     84 {
     85 	if (PC_CheckTokenString(source, "balance"))
     86 	{
     87 		fs->type = WT_BALANCE;
     88 		if (!PC_ExpectTokenString(source, "(")) return qfalse;
     89 		if (!ReadValue(source, &fs->weight)) return qfalse;
     90 		if (!PC_ExpectTokenString(source, ",")) return qfalse;
     91 		if (!ReadValue(source, &fs->minweight)) return qfalse;
     92 		if (!PC_ExpectTokenString(source, ",")) return qfalse;
     93 		if (!ReadValue(source, &fs->maxweight)) return qfalse;
     94 		if (!PC_ExpectTokenString(source, ")")) return qfalse;
     95 	} //end if
     96 	else
     97 	{
     98 		fs->type = 0;
     99 		if (!ReadValue(source, &fs->weight)) return qfalse;
    100 		fs->minweight = fs->weight;
    101 		fs->maxweight = fs->weight;
    102 	} //end if
    103 	if (!PC_ExpectTokenString(source, ";")) return qfalse;
    104 	return qtrue;
    105 } //end of the function ReadFuzzyWeight
    106 //===========================================================================
    107 //
    108 // Parameter:				-
    109 // Returns:					-
    110 // Changes Globals:		-
    111 //===========================================================================
    112 void FreeFuzzySeperators_r(fuzzyseperator_t *fs)
    113 {
    114 	if (!fs) return;
    115 	if (fs->child) FreeFuzzySeperators_r(fs->child);
    116 	if (fs->next) FreeFuzzySeperators_r(fs->next);
    117 	FreeMemory(fs);
    118 } //end of the function FreeFuzzySeperators
    119 //===========================================================================
    120 //
    121 // Parameter:			-
    122 // Returns:				-
    123 // Changes Globals:		-
    124 //===========================================================================
    125 void FreeWeightConfig2(weightconfig_t *config)
    126 {
    127 	int i;
    128 
    129 	for (i = 0; i < config->numweights; i++)
    130 	{
    131 		FreeFuzzySeperators_r(config->weights[i].firstseperator);
    132 		if (config->weights[i].name) FreeMemory(config->weights[i].name);
    133 	} //end for
    134 	FreeMemory(config);
    135 } //end of the function FreeWeightConfig2
    136 //===========================================================================
    137 //
    138 // Parameter:			-
    139 // Returns:				-
    140 // Changes Globals:		-
    141 //===========================================================================
    142 void FreeWeightConfig(weightconfig_t *config)
    143 {
    144 	if (!LibVarGetValue("bot_reloadcharacters")) return;
    145 	FreeWeightConfig2(config);
    146 } //end of the function FreeWeightConfig
    147 //===========================================================================
    148 //
    149 // Parameter:			-
    150 // Returns:				-
    151 // Changes Globals:		-
    152 //===========================================================================
    153 fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source)
    154 {
    155 	int newindent, index, def, founddefault;
    156 	token_t token;
    157 	fuzzyseperator_t *fs, *lastfs, *firstfs;
    158 
    159 	founddefault = qfalse;
    160 	firstfs = NULL;
    161 	lastfs = NULL;
    162 	if (!PC_ExpectTokenString(source, "(")) return NULL;
    163 	if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL;
    164 	index = token.intvalue;
    165 	if (!PC_ExpectTokenString(source, ")")) return NULL;
    166 	if (!PC_ExpectTokenString(source, "{")) return NULL;
    167 	if (!PC_ExpectAnyToken(source, &token)) return NULL;
    168 	do
    169 	{
    170 		def = !strcmp(token.string, "default");
    171 		if (def || !strcmp(token.string, "case"))
    172 		{
    173 			fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
    174 			fs->index = index;
    175 			if (lastfs) lastfs->next = fs;
    176 			else firstfs = fs;
    177 			lastfs = fs;
    178 			if (def)
    179 			{
    180 				if (founddefault)
    181 				{
    182 					SourceError(source, "switch already has a default\n");
    183 					FreeFuzzySeperators_r(firstfs);
    184 					return NULL;
    185 				} //end if
    186 				fs->value = MAX_INVENTORYVALUE;
    187 				founddefault = qtrue;
    188 			} //end if
    189 			else
    190 			{
    191 				if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
    192 				{
    193 					FreeFuzzySeperators_r(firstfs);
    194 					return NULL;
    195 				} //end if
    196 				fs->value = token.intvalue;
    197 			} //end else
    198 			if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token))
    199 			{
    200 				FreeFuzzySeperators_r(firstfs);
    201 				return NULL;
    202 			} //end if
    203 			newindent = qfalse;
    204 			if (!strcmp(token.string, "{"))
    205 			{
    206 				newindent = qtrue;
    207 				if (!PC_ExpectAnyToken(source, &token))
    208 				{
    209 					FreeFuzzySeperators_r(firstfs);
    210 					return NULL;
    211 				} //end if
    212 			} //end if
    213 			if (!strcmp(token.string, "return"))
    214 			{
    215 				if (!ReadFuzzyWeight(source, fs))
    216 				{
    217 					FreeFuzzySeperators_r(firstfs);
    218 					return NULL;
    219 				} //end if
    220 			} //end if
    221 			else if (!strcmp(token.string, "switch"))
    222 			{
    223 				fs->child = ReadFuzzySeperators_r(source);
    224 				if (!fs->child)
    225 				{
    226 					FreeFuzzySeperators_r(firstfs);
    227 					return NULL;
    228 				} //end if
    229 			} //end else if
    230 			else
    231 			{
    232 				SourceError(source, "invalid name %s\n", token.string);
    233 				return NULL;
    234 			} //end else
    235 			if (newindent)
    236 			{
    237 				if (!PC_ExpectTokenString(source, "}"))
    238 				{
    239 					FreeFuzzySeperators_r(firstfs);
    240 					return NULL;
    241 				} //end if
    242 			} //end if
    243 		} //end if
    244 		else
    245 		{
    246 			FreeFuzzySeperators_r(firstfs);
    247 			SourceError(source, "invalid name %s\n", token.string);
    248 			return NULL;
    249 		} //end else
    250 		if (!PC_ExpectAnyToken(source, &token))
    251 		{
    252 			FreeFuzzySeperators_r(firstfs);
    253 			return NULL;
    254 		} //end if
    255 	} while(strcmp(token.string, "}"));
    256 	//
    257 	if (!founddefault)
    258 	{
    259 		SourceWarning(source, "switch without default\n");
    260 		fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
    261 		fs->index = index;
    262 		fs->value = MAX_INVENTORYVALUE;
    263 		fs->weight = 0;
    264 		fs->next = NULL;
    265 		fs->child = NULL;
    266 		if (lastfs) lastfs->next = fs;
    267 		else firstfs = fs;
    268 		lastfs = fs;
    269 	} //end if
    270 	//
    271 	return firstfs;
    272 } //end of the function ReadFuzzySeperators_r
    273 //===========================================================================
    274 //
    275 // Parameter:				-
    276 // Returns:					-
    277 // Changes Globals:		-
    278 //===========================================================================
    279 weightconfig_t *ReadWeightConfig(char *filename)
    280 {
    281 	int newindent, avail = 0, n;
    282 	token_t token;
    283 	source_t *source;
    284 	fuzzyseperator_t *fs;
    285 	weightconfig_t *config = NULL;
    286 #ifdef DEBUG
    287 	int starttime;
    288 
    289 	starttime = Sys_MilliSeconds();
    290 #endif //DEBUG
    291 
    292 	if (!LibVarGetValue("bot_reloadcharacters"))
    293 	{
    294 		avail = -1;
    295 		for( n = 0; n < MAX_WEIGHT_FILES; n++ )
    296 		{
    297 			config = weightFileList[n];
    298 			if( !config )
    299 			{
    300 				if( avail == -1 )
    301 				{
    302 					avail = n;
    303 				} //end if
    304 				continue;
    305 			} //end if
    306 			if( strcmp( filename, config->filename ) == 0 )
    307 			{
    308 				//botimport.Print( PRT_MESSAGE, "retained %s\n", filename );
    309 				return config;
    310 			} //end if
    311 		} //end for
    312 
    313 		if( avail == -1 )
    314 		{
    315 			botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename );
    316 			return NULL;
    317 		} //end if
    318 	} //end if
    319 
    320 	PC_SetBaseFolder(BOTFILESBASEFOLDER);
    321 	source = LoadSourceFile(filename);
    322 	if (!source)
    323 	{
    324 		botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
    325 		return NULL;
    326 	} //end if
    327 	//
    328 	config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t));
    329 	config->numweights = 0;
    330 	Q_strncpyz( config->filename, filename, sizeof(config->filename) );
    331 	//parse the item config file
    332 	while(PC_ReadToken(source, &token))
    333 	{
    334 		if (!strcmp(token.string, "weight"))
    335 		{
    336 			if (config->numweights >= MAX_WEIGHTS)
    337 			{
    338 				SourceWarning(source, "too many fuzzy weights\n");
    339 				break;
    340 			} //end if
    341 			if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
    342 			{
    343 				FreeWeightConfig(config);
    344 				FreeSource(source);
    345 				return NULL;
    346 			} //end if
    347 			StripDoubleQuotes(token.string);
    348 			config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1);
    349 			strcpy(config->weights[config->numweights].name, token.string);
    350 			if (!PC_ExpectAnyToken(source, &token))
    351 			{
    352 				FreeWeightConfig(config);
    353 				FreeSource(source);
    354 				return NULL;
    355 			} //end if
    356 			newindent = qfalse;
    357 			if (!strcmp(token.string, "{"))
    358 			{
    359 				newindent = qtrue;
    360 				if (!PC_ExpectAnyToken(source, &token))
    361 				{
    362 					FreeWeightConfig(config);
    363 					FreeSource(source);
    364 					return NULL;
    365 				} //end if
    366 			} //end if
    367 			if (!strcmp(token.string, "switch"))
    368 			{
    369 				fs = ReadFuzzySeperators_r(source);
    370 				if (!fs)
    371 				{
    372 					FreeWeightConfig(config);
    373 					FreeSource(source);
    374 					return NULL;
    375 				} //end if
    376 				config->weights[config->numweights].firstseperator = fs;
    377 			} //end if
    378 			else if (!strcmp(token.string, "return"))
    379 			{
    380 				fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
    381 				fs->index = 0;
    382 				fs->value = MAX_INVENTORYVALUE;
    383 				fs->next = NULL;
    384 				fs->child = NULL;
    385 				if (!ReadFuzzyWeight(source, fs))
    386 				{
    387 					FreeMemory(fs);
    388 					FreeWeightConfig(config);
    389 					FreeSource(source);
    390 					return NULL;
    391 				} //end if
    392 				config->weights[config->numweights].firstseperator = fs;
    393 			} //end else if
    394 			else
    395 			{
    396 				SourceError(source, "invalid name %s\n", token.string);
    397 				FreeWeightConfig(config);
    398 				FreeSource(source);
    399 				return NULL;
    400 			} //end else
    401 			if (newindent)
    402 			{
    403 				if (!PC_ExpectTokenString(source, "}"))
    404 				{
    405 					FreeWeightConfig(config);
    406 					FreeSource(source);
    407 					return NULL;
    408 				} //end if
    409 			} //end if
    410 			config->numweights++;
    411 		} //end if
    412 		else
    413 		{
    414 			SourceError(source, "invalid name %s\n", token.string);
    415 			FreeWeightConfig(config);
    416 			FreeSource(source);
    417 			return NULL;
    418 		} //end else
    419 	} //end while
    420 	//free the source at the end of a pass
    421 	FreeSource(source);
    422 	//if the file was located in a pak file
    423 	botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
    424 #ifdef DEBUG
    425 	if (bot_developer)
    426 	{
    427 		botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime);
    428 	} //end if
    429 #endif //DEBUG
    430 	//
    431 	if (!LibVarGetValue("bot_reloadcharacters"))
    432 	{
    433 		weightFileList[avail] = config;
    434 	} //end if
    435 	//
    436 	return config;
    437 } //end of the function ReadWeightConfig
    438 #if 0
    439 //===========================================================================
    440 //
    441 // Parameter:				-
    442 // Returns:					-
    443 // Changes Globals:		-
    444 //===========================================================================
    445 qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs)
    446 {
    447 	if (fs->type == WT_BALANCE)
    448 	{
    449 		if (fprintf(fp, " return balance(") < 0) return qfalse;
    450 		if (!WriteFloat(fp, fs->weight)) return qfalse;
    451 		if (fprintf(fp, ",") < 0) return qfalse;
    452 		if (!WriteFloat(fp, fs->minweight)) return qfalse;
    453 		if (fprintf(fp, ",") < 0) return qfalse;
    454 		if (!WriteFloat(fp, fs->maxweight)) return qfalse;
    455 		if (fprintf(fp, ");\n") < 0) return qfalse;
    456 	} //end if
    457 	else
    458 	{
    459 		if (fprintf(fp, " return ") < 0) return qfalse;
    460 		if (!WriteFloat(fp, fs->weight)) return qfalse;
    461 		if (fprintf(fp, ";\n") < 0) return qfalse;
    462 	} //end else
    463 	return qtrue;
    464 } //end of the function WriteFuzzyWeight
    465 //===========================================================================
    466 //
    467 // Parameter:				-
    468 // Returns:					-
    469 // Changes Globals:		-
    470 //===========================================================================
    471 qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent)
    472 {
    473 	if (!WriteIndent(fp, indent)) return qfalse;
    474 	if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse;
    475 	if (!WriteIndent(fp, indent)) return qfalse;
    476 	if (fprintf(fp, "{\n") < 0) return qfalse;
    477 	indent++;
    478 	do
    479 	{
    480 		if (!WriteIndent(fp, indent)) return qfalse;
    481 		if (fs->next)
    482 		{
    483 			if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse;
    484 		} //end if
    485 		else
    486 		{
    487 			if (fprintf(fp, "default:") < 0) return qfalse;
    488 		} //end else
    489 		if (fs->child)
    490 		{
    491 			if (fprintf(fp, "\n") < 0) return qfalse;
    492 			if (!WriteIndent(fp, indent)) return qfalse;
    493 			if (fprintf(fp, "{\n") < 0) return qfalse;
    494 			if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse;
    495 			if (!WriteIndent(fp, indent)) return qfalse;
    496 			if (fs->next)
    497 			{
    498 				if (fprintf(fp, "} //end case\n") < 0) return qfalse;
    499 			} //end if
    500 			else
    501 			{
    502 				if (fprintf(fp, "} //end default\n") < 0) return qfalse;
    503 			} //end else
    504 		} //end if
    505 		else
    506 		{
    507 			if (!WriteFuzzyWeight(fp, fs)) return qfalse;
    508 		} //end else
    509 		fs = fs->next;
    510 	} while(fs);
    511 	indent--;
    512 	if (!WriteIndent(fp, indent)) return qfalse;
    513 	if (fprintf(fp, "} //end switch\n") < 0) return qfalse;
    514 	return qtrue;
    515 } //end of the function WriteItemFuzzyWeights_r
    516 //===========================================================================
    517 //
    518 // Parameter:				-
    519 // Returns:					-
    520 // Changes Globals:		-
    521 //===========================================================================
    522 qboolean WriteWeightConfig(char *filename, weightconfig_t *config)
    523 {
    524 	int i;
    525 	FILE *fp;
    526 	weight_t *ifw;
    527 
    528 	fp = fopen(filename, "wb");
    529 	if (!fp) return qfalse;
    530 
    531 	for (i = 0; i < config->numweights; i++)
    532 	{
    533 		ifw = &config->weights[i];
    534 		if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse;
    535 		if (fprintf(fp, "{\n") < 0) return qfalse;
    536 		if (ifw->firstseperator->index > 0)
    537 		{
    538 			if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse;
    539 		} //end if
    540 		else
    541 		{
    542 			if (!WriteIndent(fp, 1)) return qfalse;
    543 			if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse;
    544 		} //end else
    545 		if (fprintf(fp, "} //end weight\n") < 0) return qfalse;
    546 	} //end for
    547 	fclose(fp);
    548 	return qtrue;
    549 } //end of the function WriteWeightConfig
    550 #endif
    551 //===========================================================================
    552 //
    553 // Parameter:				-
    554 // Returns:					-
    555 // Changes Globals:		-
    556 //===========================================================================
    557 int FindFuzzyWeight(weightconfig_t *wc, char *name)
    558 {
    559 	int i;
    560 
    561 	for (i = 0; i < wc->numweights; i++)
    562 	{
    563 		if (!strcmp(wc->weights[i].name, name))
    564 		{
    565 			return i;
    566 		} //end if
    567 	} //end if
    568 	return -1;
    569 } //end of the function FindFuzzyWeight
    570 //===========================================================================
    571 //
    572 // Parameter:				-
    573 // Returns:					-
    574 // Changes Globals:		-
    575 //===========================================================================
    576 float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs)
    577 {
    578 	float scale, w1, w2;
    579 
    580 	if (inventory[fs->index] < fs->value)
    581 	{
    582 		if (fs->child) return FuzzyWeight_r(inventory, fs->child);
    583 		else return fs->weight;
    584 	} //end if
    585 	else if (fs->next)
    586 	{
    587 		if (inventory[fs->index] < fs->next->value)
    588 		{
    589 			//first weight
    590 			if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child);
    591 			else w1 = fs->weight;
    592 			//second weight
    593 			if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
    594 			else w2 = fs->next->weight;
    595 			//the scale factor
    596 			scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
    597 			//scale between the two weights
    598 			return scale * w1 + (1 - scale) * w2;
    599 		} //end if
    600 		return FuzzyWeight_r(inventory, fs->next);
    601 	} //end else if
    602 	return fs->weight;
    603 } //end of the function FuzzyWeight_r
    604 //===========================================================================
    605 //
    606 // Parameter:				-
    607 // Returns:					-
    608 // Changes Globals:		-
    609 //===========================================================================
    610 float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)
    611 {
    612 	float scale, w1, w2;
    613 
    614 	if (inventory[fs->index] < fs->value)
    615 	{
    616 		if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);
    617 		else return fs->minweight + random() * (fs->maxweight - fs->minweight);
    618 	} //end if
    619 	else if (fs->next)
    620 	{
    621 		if (inventory[fs->index] < fs->next->value)
    622 		{
    623 			//first weight
    624 			if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);
    625 			else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);
    626 			//second weight
    627 			if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
    628 			else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight);
    629 			//the scale factor
    630 			scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
    631 			//scale between the two weights
    632 			return scale * w1 + (1 - scale) * w2;
    633 		} //end if
    634 		return FuzzyWeightUndecided_r(inventory, fs->next);
    635 	} //end else if
    636 	return fs->weight;
    637 } //end of the function FuzzyWeightUndecided_r
    638 //===========================================================================
    639 //
    640 // Parameter:				-
    641 // Returns:					-
    642 // Changes Globals:		-
    643 //===========================================================================
    644 float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)
    645 {
    646 #ifdef EVALUATERECURSIVELY
    647 	return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);
    648 #else
    649 	fuzzyseperator_t *s;
    650 
    651 	s = wc->weights[weightnum].firstseperator;
    652 	if (!s) return 0;
    653 	while(1)
    654 	{
    655 		if (inventory[s->index] < s->value)
    656 		{
    657 			if (s->child) s = s->child;
    658 			else return s->weight;
    659 		} //end if
    660 		else
    661 		{
    662 			if (s->next) s = s->next;
    663 			else return s->weight;
    664 		} //end else
    665 	} //end if
    666 	return 0;
    667 #endif
    668 } //end of the function FuzzyWeight
    669 //===========================================================================
    670 //
    671 // Parameter:				-
    672 // Returns:					-
    673 // Changes Globals:		-
    674 //===========================================================================
    675 float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)
    676 {
    677 #ifdef EVALUATERECURSIVELY
    678 	return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);
    679 #else
    680 	fuzzyseperator_t *s;
    681 
    682 	s = wc->weights[weightnum].firstseperator;
    683 	if (!s) return 0;
    684 	while(1)
    685 	{
    686 		if (inventory[s->index] < s->value)
    687 		{
    688 			if (s->child) s = s->child;
    689 			else return s->minweight + random() * (s->maxweight - s->minweight);
    690 		} //end if
    691 		else
    692 		{
    693 			if (s->next) s = s->next;
    694 			else return s->minweight + random() * (s->maxweight - s->minweight);
    695 		} //end else
    696 	} //end if
    697 	return 0;
    698 #endif
    699 } //end of the function FuzzyWeightUndecided
    700 //===========================================================================
    701 //
    702 // Parameter:				-
    703 // Returns:					-
    704 // Changes Globals:		-
    705 //===========================================================================
    706 void EvolveFuzzySeperator_r(fuzzyseperator_t *fs)
    707 {
    708 	if (fs->child)
    709 	{
    710 		EvolveFuzzySeperator_r(fs->child);
    711 	} //end if
    712 	else if (fs->type == WT_BALANCE)
    713 	{
    714 		//every once in a while an evolution leap occurs, mutation
    715 		if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight);
    716 		else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5;
    717 		//modify bounds if necesary because of mutation
    718 		if (fs->weight < fs->minweight) fs->minweight = fs->weight;
    719 		else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight;
    720 	} //end else if
    721 	if (fs->next) EvolveFuzzySeperator_r(fs->next);
    722 } //end of the function EvolveFuzzySeperator_r
    723 //===========================================================================
    724 //
    725 // Parameter:				-
    726 // Returns:					-
    727 // Changes Globals:		-
    728 //===========================================================================
    729 void EvolveWeightConfig(weightconfig_t *config)
    730 {
    731 	int i;
    732 
    733 	for (i = 0; i < config->numweights; i++)
    734 	{
    735 		EvolveFuzzySeperator_r(config->weights[i].firstseperator);
    736 	} //end for
    737 } //end of the function EvolveWeightConfig
    738 //===========================================================================
    739 //
    740 // Parameter:				-
    741 // Returns:					-
    742 // Changes Globals:		-
    743 //===========================================================================
    744 void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)
    745 {
    746 	if (fs->child)
    747 	{
    748 		ScaleFuzzySeperator_r(fs->child, scale);
    749 	} //end if
    750 	else if (fs->type == WT_BALANCE)
    751 	{
    752 		//
    753 		fs->weight = (fs->maxweight + fs->minweight) * scale;
    754 		//get the weight between bounds
    755 		if (fs->weight < fs->minweight) fs->weight = fs->minweight;
    756 		else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight;
    757 	} //end else if
    758 	if (fs->next) ScaleFuzzySeperator_r(fs->next, scale);
    759 } //end of the function ScaleFuzzySeperator_r
    760 //===========================================================================
    761 //
    762 // Parameter:				-
    763 // Returns:					-
    764 // Changes Globals:		-
    765 //===========================================================================
    766 void ScaleWeight(weightconfig_t *config, char *name, float scale)
    767 {
    768 	int i;
    769 
    770 	if (scale < 0) scale = 0;
    771 	else if (scale > 1) scale = 1;
    772 	for (i = 0; i < config->numweights; i++)
    773 	{
    774 		if (!strcmp(name, config->weights[i].name))
    775 		{
    776 			ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);
    777 			break;
    778 		} //end if
    779 	} //end for
    780 } //end of the function ScaleWeight
    781 //===========================================================================
    782 //
    783 // Parameter:				-
    784 // Returns:					-
    785 // Changes Globals:		-
    786 //===========================================================================
    787 void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)
    788 {
    789 	if (fs->child)
    790 	{
    791 		ScaleFuzzySeperatorBalanceRange_r(fs->child, scale);
    792 	} //end if
    793 	else if (fs->type == WT_BALANCE)
    794 	{
    795 		float mid = (fs->minweight + fs->maxweight) * 0.5;
    796 		//get the weight between bounds
    797 		fs->maxweight = mid + (fs->maxweight - mid) * scale;
    798 		fs->minweight = mid + (fs->minweight - mid) * scale;
    799 		if (fs->maxweight < fs->minweight)
    800 		{
    801 			fs->maxweight = fs->minweight;
    802 		} //end if
    803 	} //end else if
    804 	if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);
    805 } //end of the function ScaleFuzzySeperatorBalanceRange_r
    806 //===========================================================================
    807 //
    808 // Parameter:				-
    809 // Returns:					-
    810 // Changes Globals:		-
    811 //===========================================================================
    812 void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)
    813 {
    814 	int i;
    815 
    816 	if (scale < 0) scale = 0;
    817 	else if (scale > 100) scale = 100;
    818 	for (i = 0; i < config->numweights; i++)
    819 	{
    820 		ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);
    821 	} //end for
    822 } //end of the function ScaleFuzzyBalanceRange
    823 //===========================================================================
    824 //
    825 // Parameter:				-
    826 // Returns:					-
    827 // Changes Globals:		-
    828 //===========================================================================
    829 int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
    830 								fuzzyseperator_t *fsout)
    831 {
    832 	if (fs1->child)
    833 	{
    834 		if (!fs2->child || !fsout->child)
    835 		{
    836 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n");
    837 			return qfalse;
    838 		} //end if
    839 		if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))
    840 		{
    841 			return qfalse;
    842 		} //end if
    843 	} //end if
    844 	else if (fs1->type == WT_BALANCE)
    845 	{
    846 		if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)
    847 		{
    848 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n");
    849 			return qfalse;
    850 		} //end if
    851 		fsout->weight = (fs1->weight + fs2->weight) / 2;
    852 		if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight;
    853 		if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight;
    854 	} //end else if
    855 	if (fs1->next)
    856 	{
    857 		if (!fs2->next || !fsout->next)
    858 		{
    859 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n");
    860 			return qfalse;
    861 		} //end if
    862 		if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))
    863 		{
    864 			return qfalse;
    865 		} //end if
    866 	} //end if
    867 	return qtrue;
    868 } //end of the function InterbreedFuzzySeperator_r
    869 //===========================================================================
    870 // config1 and config2 are interbreeded and stored in configout
    871 //
    872 // Parameter:				-
    873 // Returns:					-
    874 // Changes Globals:		-
    875 //===========================================================================
    876 void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,
    877 								weightconfig_t *configout)
    878 {
    879 	int i;
    880 
    881 	if (config1->numweights != config2->numweights ||
    882 		config1->numweights != configout->numweights)
    883 	{
    884 		botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n");
    885 		return;
    886 	} //end if
    887 	for (i = 0; i < config1->numweights; i++)
    888 	{
    889 		InterbreedFuzzySeperator_r(config1->weights[i].firstseperator,
    890 									config2->weights[i].firstseperator,
    891 									configout->weights[i].firstseperator);
    892 	} //end for
    893 } //end of the function InterbreedWeightConfigs
    894 //===========================================================================
    895 //
    896 // Parameter:			-
    897 // Returns:				-
    898 // Changes Globals:		-
    899 //===========================================================================
    900 void BotShutdownWeights(void)
    901 {
    902 	int i;
    903 
    904 	for( i = 0; i < MAX_WEIGHT_FILES; i++ )
    905 	{
    906 		if (weightFileList[i])
    907 		{
    908 			FreeWeightConfig2(weightFileList[i]);
    909 			weightFileList[i] = NULL;
    910 		} //end if
    911 	} //end for
    912 } //end of the function BotShutdownWeights