.:: Bots United ::.

.:: Bots United ::. (http://forums.bots-united.com/index.php)
-   United Bot (http://forums.bots-united.com/forumdisplay.php?f=46)
-   -   Independed Thinking Machine (http://forums.bots-united.com/showthread.php?t=3244)

stefanhendriks 21-12-2004 10:53

Independed Thinking Machine
I got the basic principles working, and therefor i find it appropiate to introduce you to my version of a game-independant bot template.

The ITM-Bot, The "Independed Thinking Machine". The architecture is easy, neat, and it works for HL1 (CSTRIKE) atm. There is still work todo, but the bots spawn, choose a team, etc. So the basics work: Bot can recieve messages, its able to update its data, it can send messages (data) back to the server so it 'interacts'.

The interface has only a few 'most needed' functions that the 'empty' interface also offers.

Is there any 'advantage' over other bots, no. Why did i do it? Why not? I looked into PMB's code, and i understand the principle, but that does not make me fully understand the code. I wanted to do it myself, just to create an as naked as possible code.

For instance, the entities list is not updated yet. And , i want to do this for most important entities only. I am not sure yet what is handy to do, update the entire long list of entities, or just the 'important to bots' one.

Anyhow, just was anxious to tell you i got my bot working too. And i hope i can create a HL2 interface soon, so the bot thinking is there, and i only need a few functions to work for the bot. But thats a long way to go ;)

@$3.1415rin 21-12-2004 13:10

Re: Independed Thinking Machine
isnt this just engine indep layer augmented with some thinking-functions, like calling behaviours or whatever ...

maybe you also wanna take a look at the joebot xp perception and "thinking" code ... I guess you have the code already, pm me if you don't know where to look despite the lots of comments :P

but nice to hear that there are still people out there, who don't have to spend all their free time for university ... looking forward to see some code ( snippets )

stefanhendriks 21-12-2004 15:21

Re: Independed Thinking Machine
yes, its basicly




so, BOT & INFO is my code, GAME is game dependant code (aka like PMB with interface) , the interface feeds the info 'layer' and the bot uses that layer.

botmeister 21-12-2004 21:01

Re: Independed Thinking Machine
Well as we discussed in the United Bot thread, the big deal will be in comming up with a common interface from which any bot can be built from replaceable components. We're a long way from realizing this, but what PMB and Stefan have done is a big step forward. The hard part is just getting started with something that can be tinkered with, which was a point made by botman a while long ago.

I wish I had more time for bot programming, but at least the project I'm now working on at my job will be in developing a similar interface, so I should be able to share my experience in here at some point.

Pierre-Marie Baty 21-12-2004 22:17

Re: Independed Thinking Machine
lol, engine-independent thingies seem to be quite fashionable these days :D

I wish I had more time here too (...and the HL2 SDK).

you got me interested, botmeister, could you tell us more about that job ?

stefanhendriks 21-12-2004 22:38

Re: Independed Thinking Machine
well aside from the hl1 interface i try to write an empty interface with only comments. So you only have to 'insert' code there to feed the information layer so bots actually work.

I try to keep as much as possible out of the bot code. Ie, for joining a team in a game, the bot simply does not know what game it is. All it should do is

(snippet from itm)

void c_ITM_Bot::BOT_Think()
        if (bJoinedTeam(this) == false)
                return; // do nothing yet
        // We joined the game, think!

The join function differs from game to game, so what i have done is:


// PURPOSE: Return TRUE when joined a team
bool bJoinedTeam(c_ITM_Bot *pBot)
        // we already joined the action
        if (pBot->bJoinedAction)
                return true;
        // In order to keep this part 'engine independant' we run
        // a function shared in the entire framework called "IF_JOINTEAM"
        // which should return TRUE, when its done joining the game, which is
        // game and mod depended.
        pBot->bJoinedAction = IF_JOINTEAM(pBot);
        return pBot->bJoinedAction; // return this

where IF_JOINTEAM() is an InterFace function (IF_...) with a pointer of this bot. The IF_JOINTEAM function is a 'standard shared' function in the interface. Which is EMPTY in the 'empty/example' interface.

The contents look similiar to the join function in Real/HPB/Racc/whatever bot.

// The only BOT / GAME SPECIFIC "join the game" FUNCTION
bool IF_JOINTEAM(c_ITM_Bot *pBot)
        // We handle CS joining here, its very basic, as we are lazy
        // and let CS decide what team we are ;) We later on check
        // what team we are with the UTIL_ function at the 'sense'
        // part. Which is, even not needed, because the Update Sequence
        // keeps our bot data up-to-date!
        edict_t *pEdict = UTIL_GetBotEdict(pBot);
        if (pEdict == NULL)
                IF_PRINT("ERROR: Could not get pEdict from pBot\n");
                return false;
        // handle Counter-Strike stuff here...
        if (pBot->iStartAction == ITM_MSG_SELECT_A_TEAM)
                pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
                // Select 'auto'
                FakeClientCommand(pEdict, "menuselect", "5", NULL);               
                return false; // we did not complete joining yet
        if (pBot->iStartAction == ITM_MSG_SELECT_CLASS_ALPHA) // counter terrorist
                pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
                // Select 'auto'
                FakeClientCommand(pEdict, "menuselect", "5", NULL);
                // bot has now joined the game (doesn't need to be started)               
                return true;
        if (pBot->iStartAction == ITM_MSG_SELECT_CLASS_BETA) // terrorist select
                pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
                // Select 'auto'               
                FakeClientCommand(pEdict, "menuselect", "5", NULL);
// bot has now joined the game (doesn't need to be started)
                return true;
return false;

The reason for this way is simple: keep everything game specific out of the bot code.

This is the only example code i have so far that directly calls an interface function from the BOT. Apart frmo the runplayermove logic in the HL1 engine, which i call with "MakeItHappen". This is a more general way of saying to do what we thought about (the bot) and get things in motion. Every game has a different way of doing this, so MakeItHappen was a nice name i thought.

For the moment things are very, very basic and very naked coded. Only the barebones are there, but thats the point. It should not be bigger then nescesary.

Just FYI here the contents of the itm_interface_hl1.h file:

// Shared interface functions
#define MAX_HL1_CLIENTS        32                        // Max 32 clients in HL1
struct t_timeBot
int msecnum;
float msecdel;
int msecval;
// The function names should not change
// The params may change though
void IF_Init();                                                        // Init any (global) vars we use in the game specific interface
void IF_Spawn(edict_t *pent);                                // Spawns something
void IF_UpdateSequence();                                        // aka Startframe()
int IF_CreateBot (edict_t * pPlayer, const char *arg1, const char *arg2, const char *arg3, const char *arg4);
void IF_PRINT(char *msg);                                        // print a message on the server
void IF_ClientCommand(edict_t * pBot, char *arg1, char *arg2, char *arg3); // command from a bot to the server
int IF_MakeItHappen(c_ITM_Bot *pBot);        // Aka RunPlayerMove()
// "Converts" any game vector of this game into the vector of our own format!
c_ITM_Vector IF_ConvertVector(Vector vec);
Vector IF_ConvertVector(c_ITM_Vector vec);
t_timeBot TimeBot[MAX_HL1_CLIENTS];
// Newly added functions:
c_ITM_Bot * UTIL_GetBotPointer (edict_t * pEdict);
int                UTIL_GetBotIndex (edict_t * pEdict);
void HUD_DrawString (int r, int g, int b, char *msg, edict_t * edict);
void BotClient_CS_VGUI (void *p, int bot_index);
void BotClient_CS_ShowMenu (void *p, int bot_index);
void BotClient_CS_WeaponList (void *p, int bot_index);
void BotClient_CS_CurrentWeapon (void *p, int bot_index);
void BotClient_CS_AmmoX (void *p, int bot_index);
void BotClient_CS_AmmoPickup (void *p, int bot_index);
void BotClient_CS_WeaponPickup (void *p, int bot_index);
void BotClient_CS_ItemPickup (void *p, int bot_index);
void BotClient_CS_Health (void *p, int bot_index);
void BotClient_CS_Battery (void *p, int bot_index);
void BotClient_CS_Damage (void *p, int bot_index);
void BotClient_CS_Money (void *p, int bot_index);
void BotClient_CS_DeathMsg (void *p, int bot_index);
void BotClient_CS_ScreenFade (void *p, int bot_index);
void BotClient_CS_HLTV(void *p, int bot_index);
void BotClient_CS_SayText(void *p, int bot_index);
// StatusIcon
void BotClient_CS_StatusIcon (void *p, int bot_index);
void BotClient_Valve_WeaponList (void *p, int bot_index);
void BotClient_Valve_CurrentWeapon (void *p, int bot_index);
void BotClient_Valve_AmmoX (void *p, int bot_index);
void BotClient_Valve_AmmoPickup (void *p, int bot_index);
void BotClient_Valve_WeaponPickup (void *p, int bot_index);
void BotClient_Valve_ItemPickup (void *p, int bot_index);
void BotClient_Valve_Health (void *p, int bot_index);
void BotClient_Valve_Battery (void *p, int bot_index);
void BotClient_Valve_Damage (void *p, int bot_index);
void BotClient_Valve_DeathMsg (void *p, int bot_index);
void BotClient_Valve_ScreenFade (void *p, int bot_index);

as another note, i do use the clients[] var , but in a different way. As for in HPB you see they are not linked to the bots in any way. The clients[] var is handling ALL clients joining the game, and i share the indexes with my bots. So i can simply get a pEdict WITHOUT even using that in my c_ITM_Bot class. All i need is an index...

@$3.1415rin 21-12-2004 23:14

Re: Independed Thinking Machine
about how I solved this join stuff : In JoeBOT XP most is inside behaviours, but the joining is not. well, I wanted to write it is, but I looked it up and it isnt ... well, so it's basically the same you are doing stefan, except that I have a general function handling all sort of menues, which is a virtual function and thus is automatically used if a derived class has another handling than the normal HL bot. Maybe putting this into the structure of the behaviuors would be a fine way since that doesnt imply the need of another function ... nice idea, if I had only time.

About the network messages : ( havnt yet implemented all of them, just those I need at the moment ) They produce perceipts, like those perceipts when a bot sees another player, therefore they are processed by behaviours like almost everything else. this is something that's so to say part of the layer "converting" the information which the engine provides.


CBaseBot *pBot = g_pGame->getBot(bot_index);
if ((damage_armor > 0) || (damage_taken > 0)){
/*if ((damage_bits & (DMG_FALL | DMG_CRUSH)){
// bot received damage by falling down
// ignore certain types of damage...
if (damage_bits & IGNORE_DAMAGE){
// let the bot 'feel' something strange is going on ... health is going down, m8 !
CPerceipt *pNewPerceipt = pBot->m_pPerception->addPerceipt();
pNewPerceipt->m_VOrigin = damage_origin;
pNewPerceipt->m_lType = CPerceipt::PT_DAMAGE;
pNewPerceipt->m_lTypeSpec |= CPerceipt::PTX_DAMAGE_STRANGE;
pNewPerceipt->m_fDistance = (pBot->getOrigin()-damage_origin).length();
pNewPerceipt->m_iAddInfo = damage_bits;
// let the bot 'feel' that he's attacked :)
CPerceipt *pNewPerceipt = pBot->m_pPerception->addPerceipt();
pNewPerceipt->m_VOrigin = damage_origin;
pNewPerceipt->m_lType = CPerceipt::PT_DAMAGE;
pNewPerceipt->m_fDistance = (pBot->getOrigin()-damage_origin).length();
pNewPerceipt->m_iAddInfo = damage_bits;
g_Map.m_Waypoints.addDamage(pBot->getOrigin(),damage_origin,damage_armor + damage_taken);

( bah, again that distance code I wanted to change :D )

and then those information is used fully engine indep in a behaviour


// watch Damage
void CBV_HLDM_WatchDamage::evaluate(list<CGoal> *pGoals,CPerceipt* p){
if(p->m_lType == CPerceipt::PT_DAMAGE
&&!(p->m_lTypeSpec & CPerceipt::PTX_DAMAGE_STRANGE)){
(15)*((p->m_fLifetime - g_pGame->getTime() + p->m_fLUpdate)/p->m_fLifetime),
void CBV_HLDM_WatchDamage::execute(CGoal *pGoal){
m_fLastExecution = g_pGame->getTime();

stefanhendriks 21-12-2004 23:26

Re: Independed Thinking Machine
i understood the first code snippet perfectly. Thats also a way of doing it. Although i would just set a flag or something, or even better. Just set the health and let the bot think handle the question 'how did my health drop' and determine that from an origin if it was inflicted or not.

THe second piece was just a to big IF statement for me to handle at once heh :)

@$3.1415rin 21-12-2004 23:38

Re: Independed Thinking Machine
I have to store more data in the perceptions since the perceptions can exist longer, and an entity inflicting damage might be deleted. the perception might even go to the long term memory, so that's why i'm storing almost everything important I can get in the perceipt.

and always checking if the health might have dropped is called "polling" and not very loved noadays, since you need processing time for it. In my solution only then the code is executed if something happened

stefanhendriks 21-12-2004 23:44

Re: Independed Thinking Machine
actually i don't care what is loved or not ;) i go for security, and portability :) And yet easy-to-read code. With machines nowadays, i don't think bots can take up to much cpu power.

All times are GMT +2. The time now is 11:32.

Powered by vBulletin® Version 3.8.2
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.