2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
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.
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.
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
===========================================================================
23
/*****************************************************************************
24
* name: be_ai_weight.c
28
* $Archive: /MissionPack/code/botlib/be_ai_weight.c $
30
*****************************************************************************/
32
#include "../qcommon/q_shared.h"
37
#include "l_precomp.h"
43
#include "be_aas_funcs.h"
44
#include "be_interface.h"
45
#include "be_ai_weight.h"
47
#define MAX_INVENTORYVALUE 999999
48
#define EVALUATERECURSIVELY
50
#define MAX_WEIGHT_FILES 128
51
weightconfig_t *weightFileList[MAX_WEIGHT_FILES];
53
//===========================================================================
58
//===========================================================================
59
int ReadValue(source_t *source, float *value)
63
if (!PC_ExpectAnyToken(source, &token)) return qfalse;
64
if (!strcmp(token.string, "-"))
66
SourceWarning(source, "negative value set to zero\n");
67
if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse;
69
if (token.type != TT_NUMBER)
71
SourceError(source, "invalid return value %s\n", token.string);
74
*value = token.floatvalue;
76
} //end of the function ReadValue
77
//===========================================================================
82
//===========================================================================
83
int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs)
85
if (PC_CheckTokenString(source, "balance"))
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;
99
if (!ReadValue(source, &fs->weight)) return qfalse;
100
fs->minweight = fs->weight;
101
fs->maxweight = fs->weight;
103
if (!PC_ExpectTokenString(source, ";")) return qfalse;
105
} //end of the function ReadFuzzyWeight
106
//===========================================================================
110
// Changes Globals: -
111
//===========================================================================
112
void FreeFuzzySeperators_r(fuzzyseperator_t *fs)
115
if (fs->child) FreeFuzzySeperators_r(fs->child);
116
if (fs->next) FreeFuzzySeperators_r(fs->next);
118
} //end of the function FreeFuzzySeperators
119
//===========================================================================
123
// Changes Globals: -
124
//===========================================================================
125
void FreeWeightConfig2(weightconfig_t *config)
129
for (i = 0; i < config->numweights; i++)
131
FreeFuzzySeperators_r(config->weights[i].firstseperator);
132
if (config->weights[i].name) FreeMemory(config->weights[i].name);
135
} //end of the function FreeWeightConfig2
136
//===========================================================================
140
// Changes Globals: -
141
//===========================================================================
142
void FreeWeightConfig(weightconfig_t *config)
144
if (!LibVarGetValue("bot_reloadcharacters")) return;
145
FreeWeightConfig2(config);
146
} //end of the function FreeWeightConfig
147
//===========================================================================
151
// Changes Globals: -
152
//===========================================================================
153
fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source)
155
int newindent, index, def, founddefault;
157
fuzzyseperator_t *fs, *lastfs, *firstfs;
159
founddefault = qfalse;
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;
170
def = !strcmp(token.string, "default");
171
if (def || !strcmp(token.string, "case"))
173
fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
175
if (lastfs) lastfs->next = fs;
182
SourceError(source, "switch already has a default\n");
183
FreeFuzzySeperators_r(firstfs);
186
fs->value = MAX_INVENTORYVALUE;
187
founddefault = qtrue;
191
if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
193
FreeFuzzySeperators_r(firstfs);
196
fs->value = token.intvalue;
198
if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token))
200
FreeFuzzySeperators_r(firstfs);
204
if (!strcmp(token.string, "{"))
207
if (!PC_ExpectAnyToken(source, &token))
209
FreeFuzzySeperators_r(firstfs);
213
if (!strcmp(token.string, "return"))
215
if (!ReadFuzzyWeight(source, fs))
217
FreeFuzzySeperators_r(firstfs);
221
else if (!strcmp(token.string, "switch"))
223
fs->child = ReadFuzzySeperators_r(source);
226
FreeFuzzySeperators_r(firstfs);
232
SourceError(source, "invalid name %s\n", token.string);
237
if (!PC_ExpectTokenString(source, "}"))
239
FreeFuzzySeperators_r(firstfs);
246
FreeFuzzySeperators_r(firstfs);
247
SourceError(source, "invalid name %s\n", token.string);
250
if (!PC_ExpectAnyToken(source, &token))
252
FreeFuzzySeperators_r(firstfs);
255
} while(strcmp(token.string, "}"));
259
SourceWarning(source, "switch without default\n");
260
fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
262
fs->value = MAX_INVENTORYVALUE;
266
if (lastfs) lastfs->next = fs;
272
} //end of the function ReadFuzzySeperators_r
273
//===========================================================================
277
// Changes Globals: -
278
//===========================================================================
279
weightconfig_t *ReadWeightConfig(char *filename)
281
int newindent, avail = 0, n;
284
fuzzyseperator_t *fs;
285
weightconfig_t *config = NULL;
289
starttime = Sys_MilliSeconds();
292
if (!LibVarGetValue("bot_reloadcharacters"))
295
for( n = 0; n < MAX_WEIGHT_FILES; n++ )
297
config = weightFileList[n];
306
if( strcmp( filename, config->filename ) == 0 )
308
//botimport.Print( PRT_MESSAGE, "retained %s\n", filename );
315
botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename );
320
PC_SetBaseFolder(BOTFILESBASEFOLDER);
321
source = LoadSourceFile(filename);
324
botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
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))
334
if (!strcmp(token.string, "weight"))
336
if (config->numweights >= MAX_WEIGHTS)
338
SourceWarning(source, "too many fuzzy weights\n");
341
if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
343
FreeWeightConfig(config);
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))
352
FreeWeightConfig(config);
357
if (!strcmp(token.string, "{"))
360
if (!PC_ExpectAnyToken(source, &token))
362
FreeWeightConfig(config);
367
if (!strcmp(token.string, "switch"))
369
fs = ReadFuzzySeperators_r(source);
372
FreeWeightConfig(config);
376
config->weights[config->numweights].firstseperator = fs;
378
else if (!strcmp(token.string, "return"))
380
fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
382
fs->value = MAX_INVENTORYVALUE;
385
if (!ReadFuzzyWeight(source, fs))
388
FreeWeightConfig(config);
392
config->weights[config->numweights].firstseperator = fs;
396
SourceError(source, "invalid name %s\n", token.string);
397
FreeWeightConfig(config);
403
if (!PC_ExpectTokenString(source, "}"))
405
FreeWeightConfig(config);
410
config->numweights++;
414
SourceError(source, "invalid name %s\n", token.string);
415
FreeWeightConfig(config);
420
//free the source at the end of a pass
422
//if the file was located in a pak file
423
botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
427
botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime);
431
if (!LibVarGetValue("bot_reloadcharacters"))
433
weightFileList[avail] = config;
437
} //end of the function ReadWeightConfig
439
//===========================================================================
443
// Changes Globals: -
444
//===========================================================================
445
qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs)
447
if (fs->type == WT_BALANCE)
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;
459
if (fprintf(fp, " return ") < 0) return qfalse;
460
if (!WriteFloat(fp, fs->weight)) return qfalse;
461
if (fprintf(fp, ";\n") < 0) return qfalse;
464
} //end of the function WriteFuzzyWeight
465
//===========================================================================
469
// Changes Globals: -
470
//===========================================================================
471
qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent)
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;
480
if (!WriteIndent(fp, indent)) return qfalse;
483
if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse;
487
if (fprintf(fp, "default:") < 0) return qfalse;
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;
498
if (fprintf(fp, "} //end case\n") < 0) return qfalse;
502
if (fprintf(fp, "} //end default\n") < 0) return qfalse;
507
if (!WriteFuzzyWeight(fp, fs)) return qfalse;
512
if (!WriteIndent(fp, indent)) return qfalse;
513
if (fprintf(fp, "} //end switch\n") < 0) return qfalse;
515
} //end of the function WriteItemFuzzyWeights_r
516
//===========================================================================
520
// Changes Globals: -
521
//===========================================================================
522
qboolean WriteWeightConfig(char *filename, weightconfig_t *config)
528
fp = fopen(filename, "wb");
529
if (!fp) return qfalse;
531
for (i = 0; i < config->numweights; i++)
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)
538
if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse;
542
if (!WriteIndent(fp, 1)) return qfalse;
543
if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse;
545
if (fprintf(fp, "} //end weight\n") < 0) return qfalse;
549
} //end of the function WriteWeightConfig
551
//===========================================================================
555
// Changes Globals: -
556
//===========================================================================
557
int FindFuzzyWeight(weightconfig_t *wc, char *name)
561
for (i = 0; i < wc->numweights; i++)
563
if (!strcmp(wc->weights[i].name, name))
569
} //end of the function FindFuzzyWeight
570
//===========================================================================
574
// Changes Globals: -
575
//===========================================================================
576
float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs)
580
if (inventory[fs->index] < fs->value)
582
if (fs->child) return FuzzyWeight_r(inventory, fs->child);
583
else return fs->weight;
587
if (inventory[fs->index] < fs->next->value)
590
if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child);
591
else w1 = fs->weight;
593
if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
594
else w2 = fs->next->weight;
596
if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
597
return w2; // can't interpolate, return default weight
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;
603
return FuzzyWeight_r(inventory, fs->next);
606
} //end of the function FuzzyWeight_r
607
//===========================================================================
611
// Changes Globals: -
612
//===========================================================================
613
float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)
617
if (inventory[fs->index] < fs->value)
619
if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);
620
else return fs->minweight + random() * (fs->maxweight - fs->minweight);
624
if (inventory[fs->index] < fs->next->value)
627
if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);
628
else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);
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);
633
if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
634
return w2; // can't interpolate, return default weight
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;
640
return FuzzyWeightUndecided_r(inventory, fs->next);
643
} //end of the function FuzzyWeightUndecided_r
644
//===========================================================================
648
// Changes Globals: -
649
//===========================================================================
650
float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)
652
#ifdef EVALUATERECURSIVELY
653
return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);
657
s = wc->weights[weightnum].firstseperator;
661
if (inventory[s->index] < s->value)
663
if (s->child) s = s->child;
664
else return s->weight;
668
if (s->next) s = s->next;
669
else return s->weight;
674
} //end of the function FuzzyWeight
675
//===========================================================================
679
// Changes Globals: -
680
//===========================================================================
681
float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)
683
#ifdef EVALUATERECURSIVELY
684
return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);
688
s = wc->weights[weightnum].firstseperator;
692
if (inventory[s->index] < s->value)
694
if (s->child) s = s->child;
695
else return s->minweight + random() * (s->maxweight - s->minweight);
699
if (s->next) s = s->next;
700
else return s->minweight + random() * (s->maxweight - s->minweight);
705
} //end of the function FuzzyWeightUndecided
706
//===========================================================================
710
// Changes Globals: -
711
//===========================================================================
712
void EvolveFuzzySeperator_r(fuzzyseperator_t *fs)
716
EvolveFuzzySeperator_r(fs->child);
718
else if (fs->type == WT_BALANCE)
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;
727
if (fs->next) EvolveFuzzySeperator_r(fs->next);
728
} //end of the function EvolveFuzzySeperator_r
729
//===========================================================================
733
// Changes Globals: -
734
//===========================================================================
735
void EvolveWeightConfig(weightconfig_t *config)
739
for (i = 0; i < config->numweights; i++)
741
EvolveFuzzySeperator_r(config->weights[i].firstseperator);
743
} //end of the function EvolveWeightConfig
744
//===========================================================================
748
// Changes Globals: -
749
//===========================================================================
750
void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)
754
ScaleFuzzySeperator_r(fs->child, scale);
756
else if (fs->type == WT_BALANCE)
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;
764
if (fs->next) ScaleFuzzySeperator_r(fs->next, scale);
765
} //end of the function ScaleFuzzySeperator_r
766
//===========================================================================
770
// Changes Globals: -
771
//===========================================================================
772
void ScaleWeight(weightconfig_t *config, char *name, float scale)
776
if (scale < 0) scale = 0;
777
else if (scale > 1) scale = 1;
778
for (i = 0; i < config->numweights; i++)
780
if (!strcmp(name, config->weights[i].name))
782
ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);
786
} //end of the function ScaleWeight
787
//===========================================================================
791
// Changes Globals: -
792
//===========================================================================
793
void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)
797
ScaleFuzzySeperatorBalanceRange_r(fs->child, scale);
799
else if (fs->type == WT_BALANCE)
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)
807
fs->maxweight = fs->minweight;
810
if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);
811
} //end of the function ScaleFuzzySeperatorBalanceRange_r
812
//===========================================================================
816
// Changes Globals: -
817
//===========================================================================
818
void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)
822
if (scale < 0) scale = 0;
823
else if (scale > 100) scale = 100;
824
for (i = 0; i < config->numweights; i++)
826
ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);
828
} //end of the function ScaleFuzzyBalanceRange
829
//===========================================================================
833
// Changes Globals: -
834
//===========================================================================
835
int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
836
fuzzyseperator_t *fsout)
840
if (!fs2->child || !fsout->child)
842
botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n");
845
if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))
850
else if (fs1->type == WT_BALANCE)
852
if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)
854
botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n");
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;
863
if (!fs2->next || !fsout->next)
865
botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n");
868
if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))
874
} //end of the function InterbreedFuzzySeperator_r
875
//===========================================================================
876
// config1 and config2 are interbreeded and stored in configout
880
// Changes Globals: -
881
//===========================================================================
882
void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,
883
weightconfig_t *configout)
887
if (config1->numweights != config2->numweights ||
888
config1->numweights != configout->numweights)
890
botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n");
893
for (i = 0; i < config1->numweights; i++)
895
InterbreedFuzzySeperator_r(config1->weights[i].firstseperator,
896
config2->weights[i].firstseperator,
897
configout->weights[i].firstseperator);
899
} //end of the function InterbreedWeightConfigs
900
//===========================================================================
904
// Changes Globals: -
905
//===========================================================================
906
void BotShutdownWeights(void)
910
for( i = 0; i < MAX_WEIGHT_FILES; i++ )
912
if (weightFileList[i])
914
FreeWeightConfig2(weightFileList[i]);
915
weightFileList[i] = NULL;
918
} //end of the function BotShutdownWeights