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