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