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 		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 //===========================================================================
ReadFuzzyWeight(source_t * source,fuzzyseperator_t * fs)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 //===========================================================================
FreeFuzzySeperators_r(fuzzyseperator_t * fs)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 //===========================================================================
FreeWeightConfig2(weightconfig_t * config)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 //===========================================================================
FreeWeightConfig(weightconfig_t * config)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 //===========================================================================
ReadFuzzySeperators_r(source_t * source)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 //===========================================================================
ReadWeightConfig(char * filename)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 //===========================================================================
FindFuzzyWeight(weightconfig_t * wc,char * name)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 //===========================================================================
FuzzyWeight_r(int * inventory,fuzzyseperator_t * fs)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 			if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
597         		return w2;      // can't interpolate, return default weight
598 			else
599 				scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
600 			//scale between the two weights
601 			return (1 - scale) * w1 + scale * w2;
602 		} //end if
603 		return FuzzyWeight_r(inventory, fs->next);
604 	} //end else if
605 	return fs->weight;
606 } //end of the function FuzzyWeight_r
607 //===========================================================================
608 //
609 // Parameter:				-
610 // Returns:					-
611 // Changes Globals:		-
612 //===========================================================================
FuzzyWeightUndecided_r(int * inventory,fuzzyseperator_t * fs)613 float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)
614 {
615 	float scale, w1, w2;
616 
617 	if (inventory[fs->index] < fs->value)
618 	{
619 		if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);
620 		else return fs->minweight + random() * (fs->maxweight - fs->minweight);
621 	} //end if
622 	else if (fs->next)
623 	{
624 		if (inventory[fs->index] < fs->next->value)
625 		{
626 			//first weight
627 			if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);
628 			else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);
629 			//second weight
630 			if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
631 			else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight);
632 			//the scale factor
633 			if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
634         		return w2;      // can't interpolate, return default weight
635 			else
636 				scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
637 			//scale between the two weights
638 			return (1 - scale) * w1 + scale * w2;
639 		} //end if
640 		return FuzzyWeightUndecided_r(inventory, fs->next);
641 	} //end else if
642 	return fs->weight;
643 } //end of the function FuzzyWeightUndecided_r
644 //===========================================================================
645 //
646 // Parameter:				-
647 // Returns:					-
648 // Changes Globals:		-
649 //===========================================================================
FuzzyWeight(int * inventory,weightconfig_t * wc,int weightnum)650 float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)
651 {
652 #ifdef EVALUATERECURSIVELY
653 	return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);
654 #else
655 	fuzzyseperator_t *s;
656 
657 	s = wc->weights[weightnum].firstseperator;
658 	if (!s) return 0;
659 	while(1)
660 	{
661 		if (inventory[s->index] < s->value)
662 		{
663 			if (s->child) s = s->child;
664 			else return s->weight;
665 		} //end if
666 		else
667 		{
668 			if (s->next) s = s->next;
669 			else return s->weight;
670 		} //end else
671 	} //end if
672 	return 0;
673 #endif
674 } //end of the function FuzzyWeight
675 //===========================================================================
676 //
677 // Parameter:				-
678 // Returns:					-
679 // Changes Globals:		-
680 //===========================================================================
FuzzyWeightUndecided(int * inventory,weightconfig_t * wc,int weightnum)681 float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)
682 {
683 #ifdef EVALUATERECURSIVELY
684 	return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);
685 #else
686 	fuzzyseperator_t *s;
687 
688 	s = wc->weights[weightnum].firstseperator;
689 	if (!s) return 0;
690 	while(1)
691 	{
692 		if (inventory[s->index] < s->value)
693 		{
694 			if (s->child) s = s->child;
695 			else return s->minweight + random() * (s->maxweight - s->minweight);
696 		} //end if
697 		else
698 		{
699 			if (s->next) s = s->next;
700 			else return s->minweight + random() * (s->maxweight - s->minweight);
701 		} //end else
702 	} //end if
703 	return 0;
704 #endif
705 } //end of the function FuzzyWeightUndecided
706 //===========================================================================
707 //
708 // Parameter:				-
709 // Returns:					-
710 // Changes Globals:		-
711 //===========================================================================
EvolveFuzzySeperator_r(fuzzyseperator_t * fs)712 void EvolveFuzzySeperator_r(fuzzyseperator_t *fs)
713 {
714 	if (fs->child)
715 	{
716 		EvolveFuzzySeperator_r(fs->child);
717 	} //end if
718 	else if (fs->type == WT_BALANCE)
719 	{
720 		//every once in a while an evolution leap occurs, mutation
721 		if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight);
722 		else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5;
723 		//modify bounds if necesary because of mutation
724 		if (fs->weight < fs->minweight) fs->minweight = fs->weight;
725 		else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight;
726 	} //end else if
727 	if (fs->next) EvolveFuzzySeperator_r(fs->next);
728 } //end of the function EvolveFuzzySeperator_r
729 //===========================================================================
730 //
731 // Parameter:				-
732 // Returns:					-
733 // Changes Globals:		-
734 //===========================================================================
EvolveWeightConfig(weightconfig_t * config)735 void EvolveWeightConfig(weightconfig_t *config)
736 {
737 	int i;
738 
739 	for (i = 0; i < config->numweights; i++)
740 	{
741 		EvolveFuzzySeperator_r(config->weights[i].firstseperator);
742 	} //end for
743 } //end of the function EvolveWeightConfig
744 //===========================================================================
745 //
746 // Parameter:				-
747 // Returns:					-
748 // Changes Globals:		-
749 //===========================================================================
ScaleFuzzySeperator_r(fuzzyseperator_t * fs,float scale)750 void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)
751 {
752 	if (fs->child)
753 	{
754 		ScaleFuzzySeperator_r(fs->child, scale);
755 	} //end if
756 	else if (fs->type == WT_BALANCE)
757 	{
758 		//
759 		fs->weight = (float) (fs->maxweight + fs->minweight) * scale;
760 		//get the weight between bounds
761 		if (fs->weight < fs->minweight) fs->weight = fs->minweight;
762 		else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight;
763 	} //end else if
764 	if (fs->next) ScaleFuzzySeperator_r(fs->next, scale);
765 } //end of the function ScaleFuzzySeperator_r
766 //===========================================================================
767 //
768 // Parameter:				-
769 // Returns:					-
770 // Changes Globals:		-
771 //===========================================================================
ScaleWeight(weightconfig_t * config,char * name,float scale)772 void ScaleWeight(weightconfig_t *config, char *name, float scale)
773 {
774 	int i;
775 
776 	if (scale < 0) scale = 0;
777 	else if (scale > 1) scale = 1;
778 	for (i = 0; i < config->numweights; i++)
779 	{
780 		if (!strcmp(name, config->weights[i].name))
781 		{
782 			ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);
783 			break;
784 		} //end if
785 	} //end for
786 } //end of the function ScaleWeight
787 //===========================================================================
788 //
789 // Parameter:				-
790 // Returns:					-
791 // Changes Globals:		-
792 //===========================================================================
ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t * fs,float scale)793 void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)
794 {
795 	if (fs->child)
796 	{
797 		ScaleFuzzySeperatorBalanceRange_r(fs->child, scale);
798 	} //end if
799 	else if (fs->type == WT_BALANCE)
800 	{
801 		float mid = (fs->minweight + fs->maxweight) * 0.5;
802 		//get the weight between bounds
803 		fs->maxweight = mid + (fs->maxweight - mid) * scale;
804 		fs->minweight = mid + (fs->minweight - mid) * scale;
805 		if (fs->maxweight < fs->minweight)
806 		{
807 			fs->maxweight = fs->minweight;
808 		} //end if
809 	} //end else if
810 	if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);
811 } //end of the function ScaleFuzzySeperatorBalanceRange_r
812 //===========================================================================
813 //
814 // Parameter:				-
815 // Returns:					-
816 // Changes Globals:		-
817 //===========================================================================
ScaleFuzzyBalanceRange(weightconfig_t * config,float scale)818 void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)
819 {
820 	int i;
821 
822 	if (scale < 0) scale = 0;
823 	else if (scale > 100) scale = 100;
824 	for (i = 0; i < config->numweights; i++)
825 	{
826 		ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);
827 	} //end for
828 } //end of the function ScaleFuzzyBalanceRange
829 //===========================================================================
830 //
831 // Parameter:				-
832 // Returns:					-
833 // Changes Globals:		-
834 //===========================================================================
InterbreedFuzzySeperator_r(fuzzyseperator_t * fs1,fuzzyseperator_t * fs2,fuzzyseperator_t * fsout)835 int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
836 								fuzzyseperator_t *fsout)
837 {
838 	if (fs1->child)
839 	{
840 		if (!fs2->child || !fsout->child)
841 		{
842 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n");
843 			return qfalse;
844 		} //end if
845 		if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))
846 		{
847 			return qfalse;
848 		} //end if
849 	} //end if
850 	else if (fs1->type == WT_BALANCE)
851 	{
852 		if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)
853 		{
854 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n");
855 			return qfalse;
856 		} //end if
857 		fsout->weight = (fs1->weight + fs2->weight) / 2;
858 		if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight;
859 		if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight;
860 	} //end else if
861 	if (fs1->next)
862 	{
863 		if (!fs2->next || !fsout->next)
864 		{
865 			botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n");
866 			return qfalse;
867 		} //end if
868 		if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))
869 		{
870 			return qfalse;
871 		} //end if
872 	} //end if
873 	return qtrue;
874 } //end of the function InterbreedFuzzySeperator_r
875 //===========================================================================
876 // config1 and config2 are interbreeded and stored in configout
877 //
878 // Parameter:				-
879 // Returns:					-
880 // Changes Globals:		-
881 //===========================================================================
InterbreedWeightConfigs(weightconfig_t * config1,weightconfig_t * config2,weightconfig_t * configout)882 void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,
883 								weightconfig_t *configout)
884 {
885 	int i;
886 
887 	if (config1->numweights != config2->numweights ||
888 		config1->numweights != configout->numweights)
889 	{
890 		botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n");
891 		return;
892 	} //end if
893 	for (i = 0; i < config1->numweights; i++)
894 	{
895 		InterbreedFuzzySeperator_r(config1->weights[i].firstseperator,
896 									config2->weights[i].firstseperator,
897 									configout->weights[i].firstseperator);
898 	} //end for
899 } //end of the function InterbreedWeightConfigs
900 //===========================================================================
901 //
902 // Parameter:			-
903 // Returns:				-
904 // Changes Globals:		-
905 //===========================================================================
BotShutdownWeights(void)906 void BotShutdownWeights(void)
907 {
908 	int i;
909 
910 	for( i = 0; i < MAX_WEIGHT_FILES; i++ )
911 	{
912 		if (weightFileList[i])
913 		{
914 			FreeWeightConfig2(weightFileList[i]);
915 			weightFileList[i] = NULL;
916 		} //end if
917 	} //end for
918 } //end of the function BotShutdownWeights
919