Bobot_look.c

#include "../game/g_local.h"
#include "bobot.h"
#include "bobot_look.h"
#include "bobot_utils.h"

vec3_t            sonarsStart[BOBOT_LOOK_BRAIN__NUM_SONARS];
vec3_t            sonarsEnd[BOBOT_LOOK_BRAIN__NUM_SONARS];
vec3_t            sonarsOffset[BOBOT_LOOK_BRAIN__NUM_SONARS];
trace_t            tr[BOBOT_LOOK_BRAIN__NUM_SONARS];
double            lookInputs[BOBOT_LOOK_BRAIN__NUM_INPUTS];
double            lookOutputs[BOBOT_LOOK_BRAIN__NUM_OUTPUTS];
vec_t            lookUp,lookRight;
vec3_t            rotation,anglesTeammates;

/*    déduit la position moyenne de quelques équipiers proches */
void SetAverageAnglesTeammates(Sbobot *bobotAI)
{    int i;
    vec3_t    teammatesAverageVector;
    qboolean notNull = qfalse;

    VectorClear(teammatesAverageVector);
    for(i=0;i<BOBOT_LOOK_BRAIN__MAX_TEAMMATES;i++)
    {    if(bobotAI->teammatesVector[i][0] != 0 && bobotAI->teammatesVector[i][1] != 0)
        {    VectorNormalize(bobotAI->teammatesVector[i]);
            teammatesAverageVector[0] += bobotAI->teammatesVector[i][0];
            teammatesAverageVector[1] += bobotAI->teammatesVector[i][1];
            notNull = qtrue;
        }
    }
    VectorNormalize(teammatesAverageVector);
    if(notNull)
    { vectoangles(teammatesAverageVector, anglesTeammates); }
    else
    { VectorClear(anglesTeammates); }
}

void BOBOT_AI_setLookInputs(gentity_t *bobot, vec3_t anglesNode)
{    int        i;
    vec3_t    dir,up,forward,right,mins,maxs;
    //    vec3_t    testAngles,testVector;
    //    gentity_t    *tent; // pour l'affichage des sonars (debug)
    Sbobot        *bobotAI;

    bobotAI = &bobots[bobot->s.clientNum];

    /*    Get current direction */
    VectorCopy(bobot->client->ps.viewangles,dir);
    AngleVectors (dir, forward, right, up);

    SetAverageAnglesTeammates(bobotAI);
   
    /*    différence entre la visée actuelle du bot et la position moyenne des équipiers */
    //    VectorSubtract(bobot->s.angles,anglesTeammates, anglesTeammates);
    anglesTeammates[0] = AngleNormalize180(anglesTeammates[0]);
    anglesTeammates[1] = AngleNormalize180(anglesTeammates[1]);

    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
        VectorSet(sonarsOffset[i], 0, 0, 0);
    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
        G_ProjectSource(bobot->client->ps.origin, sonarsOffset[i], forward, right, sonarsStart[i]);

    /*    réglage du PITCH de l'offset des rayons */
   
    /*    un sonar dans la ligne de mire du bot */
    sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_CENTER][0]    = BOBOT_LOOK_BRAIN__SONARS_LENGTH;

    /*    un autre à l'extrémité droite de son champ de vision */
    sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_RIGHT][0]    = cos(BOBOT_ANGLE_OF_LOOKING/2) * BOBOT_LOOK_BRAIN__SONARS_LENGTH;
   
    /*    un autre à l'extrémité gauche de son champ de vision */
    sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_LEFT][0] = sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_RIGHT][0];

    /*    réglage du YAW de l'offset des rayons*/
    sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_RIGHT][1] = sin(BOBOT_ANGLE_OF_LOOKING/2) * BOBOT_LOOK_BRAIN__SONARS_LENGTH;
    sonarsOffset[BOBOT_LOOK_BRAIN__SONAR_LEFT][1] = -sin(BOBOT_ANGLE_OF_LOOKING/2) * BOBOT_LOOK_BRAIN__SONARS_LENGTH;

    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
    { G_ProjectSource(bobot->client->ps.origin, sonarsOffset[i], forward, right, sonarsEnd[i]); }
   
    /*    hauteurs des rayons réglées en fonction de la position des yeux du bot*/
    sonarsStart[BOBOT_LOOK_BRAIN__SONAR_RIGHT][2] += bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_LOOK_BRAIN__SONAR_RIGHT][2]   += bobot->client->ps.viewheight;
    sonarsStart[BOBOT_LOOK_BRAIN__SONAR_CENTER][2]+= bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_LOOK_BRAIN__SONAR_CENTER][2]  += bobot->client->ps.viewheight;
    sonarsStart[BOBOT_LOOK_BRAIN__SONAR_LEFT][2]  += bobot->client->ps.viewheight;
    sonarsEnd[BOBOT_LOOK_BRAIN__SONAR_LEFT][2]    += bobot->client->ps.viewheight;
   
    /*    donnons un peu d'épaisseur aux sonars */
    VectorSet( mins, -4, -4, -4 );
    VectorSet( maxs,  4,  4,  4 );

    /*    on active les sonars */
    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
    { trap_Trace(&tr[i], sonarsStart[i], mins, maxs, sonarsEnd[i], bobot->s.number, bobot->clipmask); }

    /*    ATTRIBUE LES PERCEPTIONS DU BOT AUX ENTREES DU NN */

    /*    les entrées du réseau de neurones sont normalisées */
    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
    {    /*    une notion d'urgence aide le bot : les distances sont au carré, 1 indique un obstacle très proche */
        lookInputs[i] = (1.0 - tr[i].fraction) * (1.0 - tr[i].fraction);
    }

    /*    l'output est injecté dans les inputs. le sonar 3 (arrière)
        n'est pas encore utilisé (et ne le sera peut être jamais) */
    lookInputs[BOBOT_LOOK_BRAIN__LAST_ROTATION] = bobotAI->last_lookOutputs[BOBOT_LOOK_BRAIN__ROTATION];
    lookInputs[BOBOT_LOOK_BRAIN__LAST_VERTICAL_ROTATION] = bobotAI->last_lookOutputs[BOBOT_LOOK_BRAIN__VERTICAL_ROTATION];
   
    /*    le bot est renseigné sur la position du prochain node */
   
    /*    le yaw est converti en valeur entre -1 et 1 */
    lookInputs[BOBOT_LOOK_BRAIN__NODE_YAW] = anglesNode[1]/180;
   
    /*    le pitch aussi, dans le but de le faire monter ou descendre les pentes ou escaliers */
    lookInputs[BOBOT_LOOK_BRAIN__NODE_PITCH] = anglesNode[0]/180;

    /*    le bot est renseigné sur la position de ses proches équipiers */
   
    /*    le yaw est converti en valeur entre -1 et 1 */
    lookInputs[BOBOT_LOOK_BRAIN__TEAMMATES_YAW] = anglesTeammates[1]/180;

    /*    le pitch aussi, dans le but de le faire monter ou descendre les pentes ou escaliers */
    lookInputs[BOBOT_LOOK_BRAIN__TEAMMATES_PITCH] = anglesTeammates[0]/180;

    /*// affichage des sonars (debug)
    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_SONARS;i++)
    {    tent = G_TempEntity( sonarsStart[i], EV_RAILTRAIL );
        VectorCopy(tr[i].endpos,tent->s.origin2);
    }*/
}

void BOBOT_AI_getLookOutputs(gentity_t *bobot)
{    vec3_t        angle;
    vec3_t        vue;
    Sbobot        *bobotAI;    

    bobotAI = &bobots[bobot->s.clientNum];

    /*    regarder en haut ou en bas. transforme l'output (entre 0 et 1) en valeur entre -1 et 1 */
    lookUp = 2*lookOutputs[BOBOT_LOOK_BRAIN__VERTICAL_ROTATION] - 1;

    /*    se tourner à droite ou à gauche. */
    lookRight = 2*lookOutputs[BOBOT_LOOK_BRAIN__ROTATION] - 1;

    VectorSet(rotation,lookUp/2,lookRight,0);

    /*    look */
    vue[PITCH] = bobotAI->move_angles[PITCH]/3;
    vue[YAW] = bobotAI->move_angles[YAW];
    vue[ROLL] = 0;
    AnglesSubtract (vue , bobot->s.angles , vue);
    VectorSet(angle,lookRight*BOBOT_LOOK_BRAIN__MAX_ROTATION_SPEED,lookUp*BOBOT_LOOK_BRAIN__MAX_ROTATION_SPEED/2,0);
    if (VectorLength(vue) > BOBOT_VIEW_TAILLE_CENTRE)
    {    VectorScale(vue,BOBOT_VIEW_RECENTRE,vue);
        VectorAdd(angle , vue , angle);   
    }
    BOBOT_MOVEMENT_ChangeBotAngle(angle,bobot);
    //-joc look
    //    VectorCopy(anglesTeammates,bobot->s.angles); // test de debug
}

void BOBOT_AI_setLookFitness(gentity_t *bobot,vec3_t anglesNode, usercmd_t *ucmd)
{    //    vec3_t        mins,maxs;
    double        tmp;
    //    trace_t        collisionTrace;
    Sbobot        *bobotAI;

    bobotAI = &bobots[bobot->s.clientNum];

    /*    les comportements oscillatoires sont punis */
    if(lookRight * bobotAI->last_lookRight > 0)
    { bobotAI->lookFitness -= 1; }
    if(lookUp * bobotAI->last_lookUp > 0)
    { bobotAI->lookFitness -= 1; }

    /*    pénalise les grosses variations de rotation */
    tmp = fabs(VectorLength(rotation)) + 1;
    bobotAI->lookFitness -= tmp * tmp - 1;

    /*    pénalise le bot s'il ne regarde pas dans la direction du prochain node du point de vue de la rotation verticale */
    tmp = fabs(anglesNode[0])/180 + 1;
    bobotAI->lookFitness -= tmp * tmp - 1;

    /*    pénalise le bot s'il ne regarde pas dans la direction du prochain node du point de vue de la rotation horizontale */
    tmp = fabs(anglesNode[1])/180 + 1;
    bobotAI->lookFitness -= tmp * tmp - 1;

    /*    pénalise le bot s'il ne couvre pas ses équipiers */
    tmp = fabs(anglesTeammates[1])/180 + 1;
    bobotAI->lookFitness -= tmp * tmp - 1;

    /*    s'il a tué quelqu'un c'est qu'il a regardé au bon endroit. il est donc récompensé */
    if(bobotAI->frag == qtrue)
    {    bobotAI->lookFitness += BOBOT_FRAG_IMPORTANCE;
        bobotAI->frag = qfalse;
    }

    /*    s'il s'est fait tuer c'est qu'il n'a pas regardé au bon endroit,
        ou qu'il n'a pas couvert ses équipiers et s'est donc retrouvé seul */
    if(bobotAI->killed == qtrue)
    {    //bobotAI->lookFitness -= BOBOT_FRAG_IMPORTANCE;
        bobotAI->killed = qfalse;
    }

    /*    pas bien de trop reculer */
    if(ucmd->forwardmove < -10)
    { bobotAI->lookFitness -= 1; }
    //G_Printf("Bot : %d | lookFitness : %f\n",bobot->s.clientNum,bobotAI->lookFitness);
}

/***************************************************************************
3emeType
fait en sorte que le bot regarde le node.
Renvoie la différence de visée entre le node et la visée actuelle du bot
PITCH = up / down
YAW   = left / right
ROLL  = fall over
***************************************************************************/
void BOBOT_lookNode(gentity_t *bobot,int node, vec3_t anglesToLookNode)
{    vec3_t        dir,lookAngles,nodeNoise;
    vec_t        pitchNoise,yawNoise;
   
    //G_Say(bobot, NULL, SAY_ALL, va("je regarde le node %i",node));
    /*    bruit dans la localisation du node (pour qu'il ne vise pas le centre tout le temps) */
    pitchNoise = RandomClamped()*nodes[node].radius;
    yawNoise   = RandomClamped()*nodes[node].radius;
    VectorSet(nodeNoise,pitchNoise,yawNoise,0);
    VectorSubtract (nodes[node].origin, bobot->client->ps.origin, dir);
    VectorAdd(dir,nodeNoise,dir); // ajout du bruit
    vectoangles (dir, anglesToLookNode);
    AnglesSubtract (anglesToLookNode , bobot->s.angles , lookAngles);
    BOBOT_MOVEMENT_ChangeBotAngle(lookAngles,bobot);
}

void BOBOT_LOOK_initBot(Sbobot* bobotAI)
{    int i;
    for(i=0; i<MAX_NODES; i++)
    { bobotAI->myNodesToLook[i] = 0; }
}

/***************************************************************************
3emeType
Remplacement du réseau de neurones par cette fonction.
Le bot regarde dans la direction d'un waypoint proche de lui,
et change régulièrement ce waypoint.
C'est moins "IA" mais ça devrait être plus performant. We'll see...
En tout cas ça peut sûrement être améliorer.

pav: DIST_MAX_NODE fixé à 3000 par 3emetype
Fonction pas très efficace, ça fait aller les bots en marche arrière
quand la valeur de DIST_MAX_NODE >500 et si < 500 les bot ne savent
plus monter à l'échelle. Quel rapport il y a-t-il ?
***************************************************************************/
void BOBOT_Look(gentity_t *bobot, usercmd_t *ucmd)
{    vec3_t        anglesToLookNode,diffMoveLook;
    int            i,n;
    vec_t        dist;
    int            closeNodes[MAX_CLOSE_NODES];
    Sbobot        *bobotAI;
    int            inmyteam;
       
    bobotAI = &bobots[bobot->s.clientNum];
    inmyteam = (bobot->client->sess.sessionTeam == TEAM_ALLIES ? NODE_TEAM_ALLIES : NODE_TEAM_AXIS);
     // est-ce qu'il est temps de changer d'orientation de visée ?
    if(level.time > bobotAI->nextChangeNodeToLook)
    {    // initialisation
               
        for(n=0;n<MAX_CLOSE_NODES;n++)
            closeNodes[n] = INVALID;
       
        // repérons MAX_NEAR_NODES (au plus) nodes proches
        for (i=0,n=0; i < number_of_nodes && n < MAX_CLOSE_NODES; i++)
        {    if(g_SniperWar.integer)
            {    if(bobotAI->Bloked == qtrue)
                {    if((nodes[i].type & NODE_PROTECTION) && !(nodes[i].type & inmyteam))
                    {    closeNodes[n] = i;
                        n++;
                    }
                }
            }
            else
            {    dist = Distance(nodes[i].origin,bobot->client->ps.origin);
                if (dist < DIST_MAX_NODE && dist > DIST_MIN_NODE &&
                    bobotAI->myNodesToLook[i] < level.time - TIME_DONT_LOOK_NODE_ALREADY_LOOKED)
                {    closeNodes[n] = i;
                    n++;
                }
            }
        }
       
        // choisissons l'un de ces nodes comme cible pour orienter la vision du bot
        bobotAI->chosenNodeToLook = INVALID;
        for(i=0; i<MAX_CLOSE_NODES && bobotAI->chosenNodeToLook == INVALID; i++)
            bobotAI->chosenNodeToLook = closeNodes[RandInt(0,MAX_CLOSE_NODES-1)];

        if(bobotAI->chosenNodeToLook == INVALID) // si aucun node n'est valide
        {    BOBOT_Look_With_NN(bobot, ucmd); // on utilise ses neurones
            return;
        }
        //G_Say( bobot, NULL, SAY_ALL, va("je regarde le node %d!",bobotAI->chosenNodeToLook));
        bobotAI->myNodesToLook[bobotAI->chosenNodeToLook] = level.time;
       
        // on prévoit la prochaine fois où le bot changera de waypoint à regarder
        bobotAI->nextChangeNodeToLook=RandInt(MIN_TIME_LOOKING_A_NODE,MAX_TIME_LOOKING_A_NODE)+ level.time;
        //bobotAI->nextChangeNodeToLook=RandInt(2000,5000)+ level.time;
    }
    BOBOT_lookNode(bobot,bobotAI->chosenNodeToLook, anglesToLookNode); // on vise le centre du node
    AnglesSubtract (bobotAI->move_angles, anglesToLookNode, diffMoveLook);
    //G_Printf("BOBOT_Look : lookangle apres : %i \n",diffMoveLook[YAW]);
    // si le node n'est plus dans la direction du bot, ou qu'il est trop proche
    if(fabs(diffMoveLook[YAW]) > BOBOT_ENEMIES_DETECTION_ANGLE ||
       Distance(nodes[bobotAI->chosenNodeToLook].origin,bobot->client->ps.origin) < DIST_MIN_NODE)
    {    if(bobotAI->Bloked == qfalse)
            bobotAI->nextChangeNodeToLook = 0; // alors au prochain coup on choisit un nouveau node
        else
        {    if(bobot->client->sess.sessionTeam == TEAM_ALLIES)
            {    if(!(nodes[bobotAI->chosenNodeToLook].type & NODE_TEAM_AXIS))
                bobotAI->nextChangeNodeToLook = 0;
            }
            else if(bobot->client->sess.sessionTeam == TEAM_AXIS)
            {    if(!(nodes[bobotAI->chosenNodeToLook].type & NODE_TEAM_ALLIES))
                bobotAI->nextChangeNodeToLook = 0;
            }
        }
    }
}

void BOBOT_Look_With_NN(gentity_t *bobot, usercmd_t *ucmd)
//void BOBOT_Look(gentity_t *bobot, usercmd_t *ucmd)
{    int        i;
    vec3_t        anglesNode;
    Sbobot        *bobotAI;
   
    bobotAI = &bobots[bobot->s.clientNum];
    vectoangles( bobotAI->move_vector, anglesNode );

    /*    on construit un vecteur représentant
        la différence entre la visée actuelle du bot et la position du prochain node */
    VectorSubtract(bobot->s.angles,anglesNode, anglesNode);
    anglesNode[0] = AngleNormalize180(anglesNode[0]);
    anglesNode[1] = AngleNormalize180(anglesNode[1]);

    /*    on prépare les entrées du réseau de neurones */
    BOBOT_AI_setLookInputs(bobot,anglesNode);

    /*    Travail du réseau de neurones. calcul des sorties et gestion de la symétrie dans les mouvements */

    /*    si l'obstacle le plus proche est à droite */
    if(lookInputs[BOBOT_LOOK_BRAIN__SONAR_LEFT] < lookInputs[BOBOT_LOOK_BRAIN__SONAR_RIGHT])
    {
        /*    alors on fait comme si l'obstacle était à gauche */
        swap(&lookInputs[BOBOT_LOOK_BRAIN__SONAR_LEFT],&lookInputs[BOBOT_LOOK_BRAIN__SONAR_RIGHT]);
        /*    l'ancienne valeur de la rotation est inversée */
        lookInputs[BOBOT_LOOK_BRAIN__LAST_ROTATION] = 1 - lookInputs[BOBOT_LOOK_BRAIN__LAST_ROTATION];
        /*    le yaw du node aussi */
        lookInputs[BOBOT_LOOK_BRAIN__NODE_YAW] = - lookInputs[BOBOT_LOOK_BRAIN__NODE_YAW];
        /*    le yaw des équipiers aussi */
        lookInputs[BOBOT_LOOK_BRAIN__TEAMMATES_YAW] = - lookInputs[BOBOT_LOOK_BRAIN__TEAMMATES_YAW];
        /*    et le NN décide de tourner à droite (s'il est performant) */
       
        /*    calcul des sorties du NN */
        UpdateNN(&(bobotAI->itsLookBrain),lookInputs,lookOutputs);
        /*    donc on inverse la rotation choisie par le NN, et le bot tourne alors à gauche */
        lookOutputs[BOBOT_LOOK_BRAIN__ROTATION] = 1 - lookOutputs[BOBOT_LOOK_BRAIN__ROTATION];
    }
    else
    { UpdateNN(&(bobotAI->itsLookBrain),lookInputs,lookOutputs); }
    /*    Attribue au bot les sorties du NN */
    BOBOT_AI_getLookOutputs(bobot);

    /*    Calcul du fitness */
    if(BOBOT_LOOK_BRAIN__IS_LEARNING)
    { BOBOT_AI_setLookFitness(bobot,anglesNode,ucmd); }
   
    /*préparation du prochain think */
    for(i=0;i<BOBOT_LOOK_BRAIN__NUM_OUTPUTS;i++)
    { bobotAI->last_lookOutputs[i] = lookOutputs[i]; }
    bobotAI->last_lookRight = lookRight;
    bobotAI->last_lookUp = lookUp;
    //VectorCopy(bobotAI->move_angles,bobot->s.angles); // pour l'instant le bot regarde là où il va
}

/*********************************************************************************
Joc
regarder bien devant luit mais en restant "fluide"
***********************************************************************************/
void BOBOT_AI_SpecialLook(gentity_t *bobot, usercmd_t *ucmd)
{    vec3_t    vue;
    Sbobot    *bobotAI;    

    bobotAI = &bobots[bobot->s.clientNum];
    AnglesSubtract (bobotAI->move_angles , bobot->s.angles , vue);
    BOBOT_MOVEMENT_ChangeBotAngle(vue,bobot);
}

Créer un site gratuit avec e-monsite - Signaler un contenu illicite sur ce site