ENUNCIAT
Realitzar un equip de futbol de robots
utilitzant el JavaSoccer. La tècnica d’intel·ligència
artificial utilitzada són els agents. Cada jugador serà una
agent amb la seva base de coneixement i un conjunt de regles, amb aquestes
regles haurà de prendre una decisió de quina és l’acció
més convenient a realitzar. Com que el futbol és un joc d’equip
els jugadors hauran de consultar la seva decisió amb els companys,
la tècnica utilitzada seran els consensos. Les necessitats de cada
jugador i el seu prestigi dependrà de com evolucioni el partit.
DESCRIPCIÓ
GENERAL
Un Agent-Jugador consta de 4 parts
completament diferenciades:
-
Inicialitzacions.
-
Presa de decisions.
-
Comunicació entre agents.
-
Realitzar una acció.
La part d’inicialitzacions s’encarrega
de inicialitzar totes les variables sobre la posicions absolutes (les posicions
dels companys, dels oponents, de la pilota i de les porteries). També
són necessari conèixer la posició relativa de la pilota
respecte al nostre agent per saber si la tenim a prop o lluny. També
s’han d’inicialitzat tota una sèrie de variables utilitzades per
prendre decisions, per realitzar la comunicació amb els companys,
per realitzar consensos, …
Per prendre decisions cada jugador
té definit un conjunt de regles. Aquestes regles està realitzades
amb lògica difusa (Fuzzy). Aquestes regles representen les possibles
accions que pot prendre un jugador. La regla amb un valor d’activació
més elevat serà la decisió presa pel jugador.
Quan cada jugador ha decidit quina
és l’acció a realitzar segons la seva base de coneixement
i de les seves regles llavors s’ha de comunicar amb els companys. Cada
jugador envia per un canal de comunicació la seva decisió
i llegeix les decisions preses pels companys. Si existeix algun conflicte
entre la decisió presa per dos agents s’aplica la tècnica
de consensos per veure quin dels dos agents té més possibilitats
de dur a terme una determinada acció, possiblement un dels dos agents
haurà canviar de decisió. La necessitat de cada jugador i
el prestigi que poden tenir els companys d’un jugador dependrà de
l’evolució del partit.
Un cop els jugadors han consensuat
la seva decisió amb els companys el jugador ha de realitzar l’acció
corresponent a la decisió presa.
INICIALITZACIONS
Existeixen dos tipus de inicialitzacions,
les que s’han de realitzar quan s’instancia la classe del jugador i les
que s’han de realitzar a cada pas d’execució. Les primeres són
variables que no varien en el transcurs del partit o que només s’han
d’inicialitzar la primera vegada, s’han de col·locar en un mètode
anomenat Configure(), les segones s’han d’executar cada vegada que
s’ha de realitzar un pas d’execució i les col·locarem al
mètode iniVars().
El mètode Configure()
mètode és executat pel JavaBotSim quan comença l’execució
del partit. S’ha d’inicialitzar:
-
l’identificador del jugador (RobotID)
-
la posició de les porteries (portaprop)
-
obrir el canal de comunicació (messagesin)
-
la decisió per defecte de cada
jugador (Segona_decisio)
-
la taula de consensos que conte la necessitat
del propi jugador i el prestigi que li tenen els seus companys (consensos).
Codi del mètode Configure()
/* Metode que configura la classe
*/
public void Configure()
{
int i;
Vec2 temp,jo;
RobotID =
abstract_robot.getPlayerNumber(temps_act);
// Identificador
del robot
jo = abstract_robot.getPosition(temps_act);
// Posicio del robot
temp = abstract_robot.getOurGoal(temps_act);
// Vector jo-porta pròpia
portaprop
= new Vec2( temp.x, temp.y );
portaprop.add(jo);
// Posició porta pròpia
temp = abstract_robot.getOpponentsGoal(temps_act);
// Vector jo-porta contrària
portacont
= new Vec2( temp.x, temp.y );
portacont.add(jo);
// Posició de la porta contrària
if (portaprop.x<0)
// Si som de l'equip de l'Oest
{
if (RobotID==2) RobotID=4;
// Canviem els ids per a tenir els robots ben posicionats
else if (RobotID==4) RobotID=2;
}
// Definició
de les segones decisions: si no poden fer una acció
// el robot
farà la segona_decisió
switch (RobotID)
{
case 0:
Segona_decisio=COBRIR;
// Porter: anar a cobrir la porteria
break;
case 1:
Segona_decisio=ZONA_DEFENSA;
// Defensa:zona de defensa a l'alçada de la pilota
break;
case 2:
Segona_decisio=OBERT_DRETA;
// Mig dret: Zona lateral dreta
break;
case 4:
Segona_decisio=OBERT_ESQUERRA;
// Mig esquerra: Zona lateral esquerra
break;
case 3:
Segona_decisio=ZONA_ATAC;
// Davanter: Zona d'Atac
break;
}
messagesin
= abstract_robot.getReceiveChannel();// messagein: missatges rebuts
for (i=0;i<JUGADORS;i++)consensos[i]=0.5;
// Valor inicial de necessitat i prestigis
}
El mètode iniVars()
s’encarrega d’inicialitzar les variables que van canviant de valor en el
transcurs del partit o que s’han d’inicialitzar abans de prendre una decisió.
Aquest mètode es cridat al principi del TakeStep() i inicialitza
les següents variables:
-
el temps actual del partit (temps_act)
-
la posició del jugador (jo)
-
la posició de la pilota (pilota)
-
la posició relativa de la pilota
respecte el jugador (jo_pilota)
-
la posició relativa de la porteria
pròpia respecte al jugador (jo_portaprop)
-
la taula de decisions on es guardarà
el valor d’activació de cada decisió (decisions)
Codi
del mètode iniVars()
void iniVars()
{
int i;
temps_act
= abstract_robot.getTime();
for
(i=0;i<MAX_DEC;i++) decisions[i]=0;
jo = abstract_robot.getPosition(temps_act);
// Posició del jugador
jo_pilota
= abstract_robot.getBall(temps_act); // Vector jo-pilota
pilota =
new Vec2( jo_pilota.x, jo_pilota.y ); // Posició de la pilota
pilota.add(
jo );
jo_portaprop
= abstract_robot.getOurGoal(temps_act); // Vector jo-porta pròpia
}
PRESA
DE DECISIONS
Un cop tenim totes les variables inicialitzades
l’agent, en funció de les dades disponibles ha de prendre una decisió
utilitzant regles amb lògica difusa. El conjunt de regles utilitzades
per cada agent és diferent en funció de la seva funcionalitat,
és a dir, si el jugador ha d’actuar com a porter tindrà una
regles completament diferents que el jugador que ha d’actuar com a atacant.
Al final, de totes les regles aplicades s’ha de decidir quina és
l’acció a prendre agafant la que tingui un valor d’activació
més alt.
Possibles accions
a realitzar per un jugador
Les regles han de permetre decidir
quina és l’acció a realitzar, per tant, cal definir quines
són les accions realitzables per un jugador. Les accions possibles
són les següents:
-
Xutar la pilota, el jugador ha de xutar
la pilota
-
Anar a la pilota: el jugador es dirigeix
a la posició de la pilota
-
Aturar-se, el jugador es para a la posició
actual.
-
Defensar porteria, el jugador es situa
a prop de la porteria per defensar-la
-
Cobrir la porteria, el jugador es dirigeix
a la porteria pròpia per cobrir-la
-
Recuperar la posició, el jugador
ha de recuperar la seva posició per intentar aconseguir la pilota
-
Obert a la dreta, el jugador es situa
a la banda dreta del camp
-
Obert a l’esquerra, el jugador es situa
a la banda esquerra del camp
-
Atacar, el jugador ha d’agafar la pilota
i conduir-la a la zona d’atac
-
Zona d’atac, el jugador es situa a la
zona d’atac esperant la pilota
-
Zona de defensa, el jugador es situa a
la zona de defensa esperant la pilota
Tipus de jugadors
Les regles aplicades per cada jugador
són diferents en funció de la seva funcionalitat. Hi ha un
perfil de jugador per a cada agent:
-
Porter: s’encarrega de defensar la porteria,
no ha de sortir de la zona de la porteria pròpia i ha de refusar
la pilota.
-
Defensa: és l’encarregat de defensar
mentre la pilota estigui al camp propi i distribuir el joc cap al mig camp
o al camp contrari.
-
Mig dret: la funció d’aquest jugador
és cobrir la banda dreta del camp, si cal defensa o si cal ataca
en funció de del circumstàncies del joc.
-
Mig esquerra: realitza la mateixa funció
que el mig dret però a la part esquerra del camp.
-
Davanter: Aquest jugador ha de tenir una
vocació clarament atacant esperant la pilota o anant-la a buscar
per marcar gol.
Conjunts fuzzy
Per poder utilitzar regles amb lògica
difusa cal tenir un conjunt de variables fuzzy, a continuació descriurem
els conjunts fuzzy que s’han utilitzat. Tots aquest mètodes estan
a la classe Fuzzy.
-
Estar a prop de la pilota. El mètode
ApropPilota(Vec2 jo_pilota) en funció de la distància del
jugador a la pilota ens retorna un valor de certesa.
-
Estar lluny de la pilota. El mètode
LlunyPilota (Vec2 jo_pilota) en funció de la distància del
jugador a la pilota ens retorna un valor de certesa.
-
Tenir la pilota. El mètode
TenirPilota (Vec2 jo_pilota) en funció de la distància del
jugador a la pilota ens retorna un valor de certesa.
-
Xut clar. El mètode XutClar(Vec2
jo,Vec2 pilota,Vec2 portaprop, Vec2 palsup, Vec2 palinf) ens indica si
temin el jugador correctament encarat cap a la porteria contrària
i que la pilota queda alineada amb el jugador per poder xutar. Si observem
la Figura 1 tenim un jugador de l’equip de l’oest que vol marcar gol a
la porteria de l’est. Si la pilota està situada en la zona pintada
de color blanc voldrà dir que si pot xutar la pilota anirà
entra a dins de la porteria. En canvi, si la pilota està a la zona
negra si xuta la pilota no entrarà directament a la porteria però
es pot produir un rebot i entrar, per tant es pot realitzar el xut però
amb un valor de certesa inferior. Si la pilota està a qualsevol
altre zona del camp llavors el mètode XutClar retornarà un
valor de certesa igual a 0.
Figura
1. Zones del camp amb un xut clar
-
Una posició esta situada
a la dreta del camp. El mètode ObertDreta(Vec2 pos,Vec2 portaprop)
passant-li la posició d’algun jugador o de la pilota ens indica
si està a la banda dreta del camp. Cal diferenciar si s’ataca cap
a l’est o cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:
Si l’equip és el de l’oest
llavors el conjunt fuzzy és:
-
Una posició esta situada
a la esquerra del camp. El mètode ObertEsquerra(Vec2 pos,Vec2
portaprop) passant-li la posició d’algun jugador o de la pilota
ens indica si està a la banda esquerra del camp. Cal diferenciar
si s’ataca cap a l’est o cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:

Si l’equip és el de
l’oest llavors el conjunt fuzzy és:

-
Una posició esta situada
a la zona central del camp. El mètode Centrat(Vec2 pos) cal
passant-li la posició d’algun jugador o de la pilota ens indica
si està a la zona central del camp, ni a la banda dreta ni a l’esquerra.
-
Una posició esta situada
a la mig camp. El mètode MigCamp(Vec2 pos) cal passant-li la
posició d’algun jugador o de la pilota ens indica si està
al mig camp, ni a la zona d’atac ni a la de defensa.
-
Una posició esta situada
al camp propi. El mètode CampPropi(Vec2 pos,Vec2 portaprop)
passant-li la posició d’algun jugador o de la pilota ens indica
si està al propi camp. Cal diferenciar si s’ataca cap a l’est o
cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:

Si l’equip és el de
l’oest llavors el conjunt fuzzy és:

-
Una posició esta situada
al camp contrari. El mètode CampContrari(Vec2 pos,Vec2 portaprop)
passant-li la posició d’algun jugador o de la pilota ens indica
si està al camp contrari. Cal diferenciar si s’ataca cap a l’est
o cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:
Si l’equip és el de l’oest
llavors el conjunt fuzzy és:
-
Una posició esta situada
a la zona de gol. El mètode ZonaGol(Vec2 pos,Vec2 portaprop)
passant-li la posició d’algun jugador o de la pilota ens indica
si està a la zona de gol contrària. Cal diferenciar si s’ataca
cap a l’est o cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:

Si l’equip és el de
l’oest llavors el conjunt fuzzy és:

-
Una posició esta situada
a la zona de la porteria. El mètode ZonaGol(Vec2 pos,Vec2 portaprop)
passant-li la posició d’algun jugador o de la pilota ens indica
si està a la zona de la porteria pròpia. Cal diferenciar
si s’ataca cap a l’est o cap a l’oest.
Si l’equip que fa la consulta
és l’equip de l’est el conjunt fuzzy és el següent:
Si l’equip és el de l’oest
llavors el conjunt fuzzy és:
-
Una posició esta situada
davant d’una altre. El mètode Davant(Vec2 pos1,Vec2 pos2, Vec2
portaprop) passant-li dos posicions d’algun jugador o de la pilota ens
indica si la primera posició està davant de la segona.
Regles de decisió
Les regles utilitzaren lògica
difusa, cal implementar els operadors and i or. L’operador lògic
and és el producte i l’operador or és la suma menys el producte
de dos valors.
Mètode que implementa la or
lògica està a la classe Utils.
public static double
Or(double d1, double d2)
{
return d1+d2-(d1*d2);
}
Com que tenim 5 perfils de jugador
llavors s’han de definir 5 conjunts de regles.
switch (RobotID)
{
case 0:/*
Regles de decisió del Porter */
break;
case 1:/*
Regles de decisió del Defensa */
break;
case 2:/*
Regles de decisió del Mig dret */
break;
case 4: /*
Regles de decisió del Mig esquerra */
break;
case 3: /*
Regles de decisió del Davanter */
break;
}
El valor de cada decisió es
guarda a una taula anomenada decisions[]
que servirà per escollir l’acció a realitzar per a cada jugador.
-
Regles de decisió del porter
R1: TenirPilora and XutClar Þ
Xutar
R2: JoCampContrari or JoMigCamp or
(JoCampPropi and JoObertDreta) or (JoCampPropi and JoObertEsquerra) Þ
Cobrir
R3: JoZonaPorteria and JoCentrat Þ
Defensar
R4: JoDavantPilota Þ
AnarPilota
Codi de les regles
decisions[XUTAR]=f.TenirPilota(jo_pilota)*f.XutClar(jo,pilota,portaprop,top_goalpost(),bottom_goalpost());
decisions[COBRIR]=f.CampContrari(jo,portaprop);
decisions[COBRIR]=u.Or(decisions[COBRIR],f.MigCamp(jo));
decisions[COBRIR]=u.Or(decisions[COBRIR],f.CampPropi(jo,portaprop)*f.ObertDreta(jo,portaprop));
decisions[COBRIR]=u.Or(decisions[COBRIR],f.CampPropi(jo,portaprop)*f.ObertEsquerra(jo,portaprop));
decisions[DEFENSAR]=f.ZonaPorteria(jo,portaprop)*f.Centrat(jo);
decisions[ANAR_PILOTA]=f.Davant(jo,pilota,portaprop);
-
Regles de decisió del defensa
R1: TenirPilora and XutClar Þ
Xutar
R2: JoZonaGol or PilotaZonaGol or
(JoCampContrari and LlunyPilota) Þ
Defensar
R3: (PilotaDavantJo and JoCampPropi
and PilotaCampPropi) or (PilotaDavantJo and JoCampPropi and PilotaMigCamp)
Þ AnarPilota
R4: JoDavantPilota Þ
Recuperar
Codi de les regles
decisions[XUTAR]=f.TenirPilota(jo_pilota)*f.XutClar(jo,pilota,portaprop,top_goalpost(),bottom_goalpost());
decisions[ZONA_DEFENSA]=f.ZonaGol(jo,portaprop);
decisions[ZONA_DEFENSA]=u.Or(decisions[ZONA_DEFENSA],f.ZonaGol(pilota,portaprop));
decisions[ZONA_DEFENSA]=u.Or(decisions[ZONA_DEFENSA],f.CampContrari(jo,portaprop)*f.LlunyPilota(jo_pilota));
decisions[ANAR_PILOTA]=f.Davant(pilota,jo,portaprop)*f.CampPropi(jo,portaprop)*f.CampPropi(pilota,portaprop);
decisions[ANAR_PILOTA]=u.Or(decisions[ANAR_PILOTA],f.Davant(pilota,jo,portaprop)*f.CampPropi(jo,portaprop)*f.MigCamp(pilota));
decisions[RECUPERAR]=f.Davant(jo,pilota,portaprop);
-
Regles de decisió del mig dret
R1: TenirPilora and XutClar Þ
Xutar
R2: (PilotaCampContrari and PilotaObertDreta)
or (PilotaCampContrari and PilotaCentrat) Þ
Atacar
R3: (PilotaObertEsquerra and JoCampContrari)
or (PilotaObertEsquerra and JoMigCamp) Þ
ObertDreta
R4: PilotaObertEsquerra and JoZonaPorteria
and JoObertDreta Þ
Aturar
R5: (PilotaCampPropi and JoDavantPilota
and PilotaObertDreta) or (PilotaCampPropi and JoDavantPilota and PilotaCentrat)
Þ Recuperar
R6: (PilotaCampPropi and PilotaDavantJo
and PilotaObertDreta) or (PilotaCampPropi and PilotaDavantJo and PilotaCentrat)Þ
Recuperar
Codi de les regles
decisions[XUTAR]=f.TenirPilota(jo_pilota)*f.XutClar(jo,pilota,portaprop,top_goalpost(),bottom_goalpost());
decisions[ATACAR]=f.CampContrari(pilota,portaprop)*f.ObertDreta(pilota,portaprop);
decisions[ATACAR]=u.Or(decisions[ATACAR],f.CampContrari(pilota,portaprop)*f.Centrat(pilota));
decisions[OBERT_DRETA]=f.ObertEsquerra(pilota,portaprop)*f.CampContrari(jo,portaprop);
decisions[OBERT_DRETA]=u.Or(decisions[OBERT_DRETA],f.ObertEsquerra(pilota,portaprop)*f.MigCamp(jo));
decisions[ATURAR]=f.ObertEsquerra(pilota,portaprop)*f.ZonaPorteria(jo,portaprop)*f.ObertDreta(jo,portaprop);
decisions[RECUPERAR]=f.CampPropi(pilota,portaprop)*f.Davant(jo,pilota,portaprop)*f.ObertDreta(pilota,portaprop);
decisions[RECUPERAR]=u.Or(decisions[RECUPERAR],f.CampPropi(pilota,portaprop)*f.Davant(jo,pilota,portaprop)*f.Centrat(pilota));
decisions[ANAR_PILOTA]=f.CampPropi(pilota,portaprop)*f.Davant(pilota,jo,portaprop)*f.ObertDreta(pilota,portaprop);
decisions[ANAR_PILOTA]=u.Or(decisions[ANAR_PILOTA],f.CampPropi(pilota,portaprop)*f.Davant(pilota,jo,portaprop)*f.Centrat(pilota));
-
Regles de decisió del mig esquerra
R1: TenirPilora and XutClar Þ
Xutar
R2: (PilotaCampContrari and PilotaObertEsquerra)
or (PilotaCampContrari and PilotaCentrat) Þ
Atacar
R3: (PilotaObertDreta and JoCampContrari)
or (PilotaObertDreta and JoMigCamp) Þ
ObertEsquerra
R4: PilotaObertDreta and JoZonaPorteria
and JoObertEsquerra Þ
Aturar
R5: (PilotaCampPropi and JoDavantPilota
and PilotaObertEsquerra) or (PilotaCampPropi and JoDavantPilota and PilotaCentrat)
Þ Recuperar
R6: (PilotaCampPropi and PilotaDavantJo
and PilotaObertEsquerra) or (PilotaCampPropi and PilotaDavantJo and PilotaCentrat)
Þ Recuperar
Codi de les regles
decisions[XUTAR]=f.TenirPilota(jo_pilota)*f.XutClar(jo,pilota,portaprop,top_goalpost(),bottom_goalpost());
decisions[ATACAR]=f.CampContrari(pilota,portaprop)*f.ObertEsquerra(pilota,portaprop);
decisions[ATACAR]=u.Or(decisions[ATACAR],f.CampContrari(pilota,portaprop)*f.Centrat(pilota));
decisions[OBERT_ESQUERRA]=f.ObertDreta(pilota,portaprop)*f.CampContrari(jo,portaprop);
decisions[OBERT_ESQUERRA]=u.Or(decisions[OBERT_ESQUERRA],f.ObertDreta(pilota,portaprop)*f.MigCamp(jo));
decisions[ATURAR]=f.ObertDreta(pilota,portaprop)*f.ZonaPorteria(jo,portaprop)*f.ObertEsquerra(jo,portaprop);
decisions[RECUPERAR]=f.CampPropi(pilota,portaprop)*f.Davant(jo,pilota,portaprop)*f.ObertEsquerra(pilota,portaprop);
decisions[RECUPERAR]=u.Or(decisions[RECUPERAR],f.CampPropi(pilota,portaprop)*f.Davant(jo,pilota,portaprop)*f.Centrat(pilota));
decisions[ANAR_PILOTA]=f.CampPropi(pilota,portaprop)*f.Davant(pilota,jo,portaprop)*f.ObertEsquerra(pilota,portaprop);
decisions[ANAR_PILOTA]=u.Or(decisions[ANAR_PILOTA],f.CampPropi(pilota,portaprop)*f.Davant(pilota,jo,portaprop)*f.Centrat(pilota));
-
Regles de decisió del davanter
R1: TenirPilora and XutClar Þ
Xutar
R2: JoDavantPilota and PilotaCampContrari
Þ Recuperar
R3: (PilotaDavantJo and PilotaMigCamp)
or (PilotaDavantJo and PilotaCampContrari)Þ
Atacar
R4: PilotaCampPropi Þ
ZonaAtac
Codi de les regles
decisions[XUTAR]=f.TenirPilota(jo_pilota)*f.XutClar(jo,pilota,portaprop,top_goalpost(),bottom_goalpost());
decisions[RECUPERAR]=f.Davant(jo,pilota,portaprop)*f.CampContrari(pilota,portaprop);
decisions[ATACAR]=u.Or(f.Davant(pilota,jo,portaprop)*f.MigCamp(pilota),f.Davant(pilota,jo,portaprop)*f.CampContrari(pilota,portaprop));
decisions[ZONA_ATAC]=f.CampPropi(pilota,portaprop);
Decisió de
l’acció a realitzar
Quan el jugador a aplicat les regles
llavors s’ha de decidir per quina és l’acció més convenient
a realitzar, s’ha de trobar el màxim de la taula de decisions. El
mètode Decisio() de la classe Utils, donada la taula de decisions
retorna la decisió que ha de prendre el jugador.
Codi del mètode Desicio()
public static int Decisio(double
vec[])
{
int i,pos=0;
double maxim=vec[0];
for (i=1;i<vec.length;i++)
{
if (vec[i] > maxim)
{
maxim=vec[i];
pos=i;
}
}
return pos;
}
COMUNICACIÓ
ENTRE AGENTS
Quan el jugadors ja han decidit quina
és l’acció a realitzar es passa a la fase de comunicació
Cal enviar al decisió als companys. El jugador ha de rebre les diferents
decisions que ha pres i si cal consensuar la decisió. Com que els
jugadores són adaptatius cal anar modificant la necessitat i els
prestigis de tots els jugadors.
Enviar als companys
de decisió presa
A la fase d’inicialització s’ha
obert un canal de comunicació. Per enviar la decisió cal
omplir una objecte de tipus Enumeration, aquest objecte té dos atributs
un serveix per posar-hi l’identificador de jugador que envia el missatge
i el segon atribut s’hi ha de posar el missatge a enviar. El missatge que
ha d’enviar és la decisió que a pres el jugador i la certesa
d’aquesta decisió. Quan l’estructura del missatge ja està
completada s’envia utilitzant un broadcast el missatge a tots els companys.
Codi per enviar un missatge
/* Comunicació amb els
companys */
m.sender=RobotID;
m.val=(new Integer(decidit)).toString();
m.val=m.val.concat(" ");
m.val=m.val.concat((new Double(decisions[u.Decisio(decisions)])).toString());
abstract_robot.broadcast(m);
// Enviar missatge a tots el companys
Rebre les decisions
dels companys
El jugador rep el missatge de tots
els seus companys. Del missatge s’ha d’extreure l’identificador de qui
envia el missatge, la decisió que ha pres i la certesa de la decisió
presa. Per obtenir aquest valors s’han creat els següents mètodes
de la classe Utils:
-
Mètode ID_company(String m) donat
el missatge d’un company ens retorna el identificador del remitent.
public static int
ID_company(String m)
{
Integer v=new
Integer(m.substring(8,9));
return v.intValue();
}
-
Mètode decisio_company(String m)
donat el missatge d’un company ens retorna la decisió que ha pres.
public static int
decisio_company(String m)
{
Integer v=new
Integer(m.substring(15,17));
return v.intValue();
}
-
Mètode certesa_company(String m)
donat el missatge d’un company ens retorna la certesa de la decisió
que ha pres.
public static double
certesa_company(String m)
{
Double v=new
Double(m.substring(17));
return v.doubleValue();
}
Utilitzant aquests mètodes i realitzant
un recorregut per la cua de missatges que hem rebut anem recollint les
dades de les decisions preses pels companys i es van aplicant els consensos.
double certesa_company;
int id_company,decisio_company;
while (messagesin.hasMoreElements())
// Rebre tots els missatges
{
StringMessage
recvd =(StringMessage)messagesin.nextElement(); // rebre missatge
String missatge=recvd.toString();
id_company=u.ID_company(missatge);
// id del company que ha enviat el missatge
decisio_company=u.decisio_company(missatge);
// decisió del company
certesa_company=u.certesa_company(missatge);
// certesa de la decisió del company
// Algorisme
per detectar decisions incompatibles
}
Detectar la decisions
entre companys incompatibles
Quan coneixem la decisió d’un
company i la certesa amb que ha pres aquesta decisió llavors és
quan es mira si calen aplicar els consensos. Moltes vegades es possible
que un jugador decideix anar a la pilota i un dels seus companys decideix
posar-se a la seva zona de defensa, en aquests cas cada jugador a pres
una decisió completament independent. Quan les decisions preses
per dos jugadors no s’interfereixen no calen aplicar els consensos. El
problema sorgeix quan dos jugadors realitzen decisions que entren amb conflicte,
com per exemple, que dos jugadors decideixen atacar. Les decisions que
són conflictives són les següents:
Jugador \ Company
|
XUTAR
|
ATACAR
|
ANAR_PILOTA
|
RECUPERAR
|
XUTAR |
Conflicte |
Conflicte |
Conflicte |
|
ATACAR |
Conflicte |
Conflicte |
Conflicte |
|
ANAR_PILOTA |
Conflicte |
Conflicte |
Conflicte |
Conflicte |
RECUPERAR |
|
|
Conflicte |
Conflicte |
La resta de possibles accions a realitzar
(ATURAR, DEFENSAR, COBRIR, … ) no són conflictives amb cap altre
acció.
Si no és produeix cap tipus
de conflicte el jugador ha de realitzar l’acció de a decidit fer,
però si existeix un conflicte amb un altre jugador llavors s’han
d’aplicar consensos.
El codi per detectar les incompatibilitats
és el següent:
switch(decidit) // decisió
presa per jugador
{
case XUTAR:
/* Mirar si hi ha incompatibilitats entre les decisions del company */
switch(decisio_company)
{
case XUTAR:
case ANAR_PILOTA:
case ATACAR:
// Algorisme de consensos
break;
}
break;
case ANAR_PILOTA:
/* Mirar si hi ha incompatibilitats entre les decisions del company */
switch(decisio_company)
{
case XUTAR:
case RECUPERAR:
case ANAR_PILOTA:
case ATACAR:
// Algorisme de consensos
break;
}
break;
case RECUPERAR:
/* Mirar si hi ha incompatibilitats entre les decisions del company */
switch(decisio_company)
{
case RECUPERAR:
case ANAR_PILOTA:
// Algorisme de consensos
break;
}
break;
case ATACAR:
/* Mirar si hi ha incompatibilitats entre les decisions del company */
switch(decisio_company)
{
case XUTAR:
case ANAR_PILOTA:
case ATACAR:
// Algorisme de consensos
break;
}
break;
}
Consensuar la decisió
amb els companys
L’aplicació de consensos consisteix
en comparar la certesa de les decisions de dos jugadors tenint en compte
el prestigi i la necessitat dels jugadors. Per fer aquesta comparació
realitza el producte de la certesa de la decisió presa per el jugador
amb la seva necessitat i es compara amb el producte de la certesa de la
decisió del company amb el prestigi que el jugador dona al seu company.
Si el jugador que realitza la comparació obté un valor de
certesa més alt que el seu company llavors no cal canviar la decisió
del jugador. En canvi si la certesa obtinguda per el jugador és
inferior a la certesa del seu company llavors el jugador ha de canviar
de decisió. Cada tipus de jugador té definida una segona
opció per defecte que està guardada a la variable Segona_decisio
i que ha estat assignada a la fase d’inicialitzacions, per exemple, la
opció per defecte del davanter és anar a la zona d’atac.
El codi següent és l’encarregat
de realitzar els consensos. Les variables canvi_decisio, ID_canvi_decisio
i decisio_canvi_decisio són utilitzades les l’algorisme de consensos
adaptatius.
if (decisions[decidit]*consensos[RobotID]<certesa_company*consensos[id_company])
{ // canviem la nostra decisió
decidit=Segona_decisio;
canvi_decisio=true;
ID_canvi_decisio=id_company;
decisio_canvi_decisio=decisio_company;
}
Algorisme adaptatiu
de Prestigi i necessitat
Cada jugador a mesura que es desenvolupa
el partit va variant la seva necessitat i el prestigi que té dels
altres companys.
Necessitat
El concepte de necessitat fa referència
a la necessitat que té el robot per anar a la pilota. Per exemple,
si es tracta d’un defensor i la pilota està en la zona d’atac del
camp contrari, la necessitat d’aquest jugador és molt baixa ja que
no li correspon a ell anar a buscar la pilota.
La necessitat anirà variant
a cada execució depenent de cada tipus de jugador la necessitat
serà diferent.
S’han definit unes funcions de necessitat
per a cada tipus de jugador:
-
Porter i Defensa: La necessitat d’anar
a la pilota del porter i el defensa és màxima (1) si la pilota
es troba en el camp propi des de la porteria fins a una distància
d i aleshores va disminuint per tota la resta del camp fins a arribar
a la porteria contrària on la necessitat és 0.
El codi d’aquesta funció és
el següent:
if (portacont.x<0)
{
if (pos.x>f.DIST_ZO_MIN)
res=1;
else res=(1/(f.DIST_ZO_MIN-portacont.x))*pos.x+(1-f.DIST_ZO_MIN/(f.DIST_ZO_MIN-portacont.x));
}
else
{
if (pos.x<-f.DIST_ZO_MIN)
res=1;
else res=(-1/(f.DIST_ZO_MIN-portacont.x))*pos.x+(1-f.DIST_ZO_MIN/(f.DIST_ZO_MIN
-portacont.x));
}
-
Mitjos: La necessitat d’anar a la pilota
dels mitjos és màxima (1) si la pilota es troba a la seva
banda (dreta o esquerra). La banda es defineix des del lateral del camp
fins a una distància determinada. A partir d’aquesta distància
la necessitat va disminuint fins a arribar al lateral del camp contrari,
on la necessitat és 0.
El codi de la necessitat del mig
dret és el següent:
double res;
double inc=0.15;
if (portaprop.x>0)
{
if (pos.y>f.DIST_OB_MAX-inc)
res=1;
else res=(1/(f.DIST_OB_MAX+0.76-inc))*pos.y+(1-(f.DIST_OB_MAX-inc)/(f.DIST_OB_MAX+0.76-inc));
}
else
{
if (pos.y<-f.DIST_OB_MAX+inc)
res=1;
else res=(-1/(f.DIST_OB_MAX-inc+0.76))*pos.y+(1-(f.DIST_OB_MAX-inc)/(f.DIST_OB_MAX+0.76-inc));
}
return res;
El codi de la necessitat del mig esquerra
és el següent:
double res;
double inc=0.15;
if (portaprop.x<0)
{
if (pos.y>f.DIST_OB_MAX-inc)
res=1;
else res=(1/(f.DIST_OB_MAX-inc+0.76))*pos.y+(1-(f.DIST_OB_MAX-inc)/(f.DIST_OB_MAX+0.76-inc));
}
else
{
if (pos.y<-f.DIST_OB_MAX+inc)
res=1;
else res=(-1/(f.DIST_OB_MAX-inc+0.76))*pos.y+(1-(f.DIST_OB_MAX-inc)/(f.DIST_OB_MAX+0.76-inc));
}
return res;
-
Davanter: la necessitat del davanter és
similar a la del defensa i el porter però es té en compte
el camp contrari en comptes del camp d’atac. Així doncs la necessitat
del davanter serà màxima (1) quan la pilota es troba al camp
d’atac, des de la porteria contrària fins a una distancia determinada.
A partir d’aquesta distància s’anirà reduint la necessitat
fins a arribar a la porteria pròpia on la necessitat serà
0.
El codi de la funció necessitat
pel davanter és el següent:
double res;
if (portaprop.x>0)
{
if (pos.x<-f.DIST_ZO_MIN)
res=1;
else res=(-1/(f.DIST_ZO_MIN+portaprop.x))*pos.x+(1-f.DIST_ZO_MIN/(f.DIST_ZO_MIN+portaprop.x));
}
else
{
if (pos.x>f.DIST_ZO_MIN)
res=1;
else res=(1/(f.DIST_ZO_MIN+portaprop.x))*pos.x+(1-f.DIST_ZO_MIN/(f.DIST_ZO_MIN+portaprop.x));
}
return res;
Prestigi
El prestigi que un jugador i
té sobre un company j es basa en calcular la necessitat que
té el jugador j per anar a la pilota en la situació
de joc corresponent. Aquest prestigi, que s’inicialitza amb un valor arbitrari
(0.5), anirà canviant a mesura que es vagi desenvolupant el joc
i sempre que es produeixin interferències entre les accions que
vol fer el jugador i i el j, les interferències possibles
s’han explicat anteriorment. L’algorisme de canvi de prestigi per un agent
funciona de la següent manera:
-
L’agent ha de canviar la seva acció
quan es produeix incompatibilitat entre les accions decidides i la certesa
del company multiplicat per el seu prestigi és major que la necessitat
de l’agent per la certesa de la seva decisió. Aleshores es canvia
la decisió i es guarda una variable booleana que vol dir que hi
hagut canvi de decisió de l’agent. També ens guardem el ID
i la decisió del company que ha provocat el canvi de decisió.
if (decisions[decidit]*consensos[RobotID]<certesa_company*consensos[id_company])
{ // canviem la nostra decisió
decidit=Segona_decisio;
canvi_decisio=true;
ID_canvi_decisio=id_company;
decisio_canvi_decisio=decisio_company;
}
-
En el següent cicle d’execució
es comprova si en el temps anterior s’ha produït un canvi en la decisió
de l’agent. En cas afirmatiu i si la decisió actual del company
que ha provocat el canvi de decisió és la mateixa que en
temps anterior aleshores es canvia el prestigi d’aquell company assignant-li
la funció de necessitat per anar a la pilota analitzada en l’apartat
anterior. D’aquesta manera estem premiant al company: si es tracta d’un
atacant el premi serà un augment del prestigi ja que tindrà
més necessitat d’anar a la pilota. Si es vol premiar a un defensor
es disminuirà el prestigi per tal de que aquest jugador tingui menys
necessitat d’atacar. El codi de l’algorisme és el següent:
if ((canvi_decisio)&&(id_company==ID_canvi_decisio))
{
if (decisio_company==decisio_canvi_decisio)
{
switch(id_company)
{
case 0: consensos[id_company]=necessitat_porter(pilota,portacont);break;
case 1: consensos[id_company]=necessitat_defensa(pilota,portacont);break;
case 2: consensos[id_company]=necessitat_mig_dret(pilota,portaprop);break;
case 3: consensos[id_company]=necessitat_davanter(pilota,portaprop);break;
case 4: consensos[id_company]=necessitat_mig_esquerra(pilota,portaprop);break;
}
}
if (consensos[id_company]>1)
consensos[id_company]=1;
if (consensos[id_company]<0)
consensos[id_company]=0;
canvi_decisio=false;
}
ACCIONS
Un cop decidida l’acció a desenvolupar
i consensuada amb els diferents agents cal realitzar l’acció que
farà la decisió resultant. Hi ha un total d’onze accions
possibles, com ja s’ha comentat anteriorment. A continuació s’analitzarà
cada acció, amb la qual s’obtindrà el vector resultat, i
la velocitat. El vector resultat serà el vector a la posició
on ha d’anar el jugador i la velocitat és amb quina velocitat s’ha
de moure.
Tots els exemples d’accions que es
mostraran a continuació estan definits com a equip propi l’equip
de l’oest.
Xutar
El vector resultant serà el
vector del jugador a la pilota. Com que cal xutar s’ha d’anar sempre a
la velocitat màxima (1.0). La Figura 2 mostra el vector de l’acció
xutar.
Figura
2. Acció xutar
El codi d’aquesta acció
és el següent:
resultat=jo_pilota; // anar
a la pilota
velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
Anar a Pilota
Anar a pilota és una acció
on el jugador anirà a buscar la pilota. El jugador es situarà
en una posició darrera la pilota per a després poder atacar
o xutar. El jugador es situarà en la direcció del vector
porteria pròpia – pilota a una distància 0.05 de la pilota
i a velocitat màxima. El vector resultant és el que es mostra
en la Figura 3 en color negre.
El codi d’aquesta acció és
el següent:
pilota_porta=new Vec2(portaprop);
pilota_porta.sub(pilota);
pilota_porta.setr(0.05);
posicio=new Vec2(pilota);
posicio.add(pilota_porta);
resultat=new Vec2(posicio);
// Anar a poca distancia darrera la pilota
resultat.sub(jo);
velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
Figura
3. Acció anar a pilota
Recuperar
Recuperar és una acció
de defensa, molt semblant a l’anterior, però augmentant la distància
a la pilota per tal de poder posar-se per davant i poder xutar-la. En aquest
cas el vector resultant es troba a 0.2 de la pilota. La Figura 4 mostra
el vector resultat.
Figura
4. Acció Recuperar
El codi de l’acció
recuperar és el següent:
pilota_porta=new Vec2(portaprop);
pilota_porta.sub(pilota);
pilota_porta.setr(0.2);
posicio=new Vec2(pilota);
posicio.add(pilota_porta);
resultat=new Vec2(posicio);
// anar darrera la pilota.
resultat.sub(jo);
velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
Defensar
L’acció de defensa la realitza
únicament el porter. El vector resultant té el sentit del
vector porteria – pilota però amb el mòdul limitat a 0.35.
La Figura 5 mostra la posició del porter i l’àrea grisa mostra
la zona de moviment d’aquest. El porter ha d’anar a velocitat màxima
fins que no arribi a la posició final que marca el vector resultat.
Figura
5. Acció defensar
El codi de l’acció
és el següent:
pilota_porta=new Vec2(portaprop);
pilota_porta.sub(pilota);
pilota_porta.setr(-0.35); //
vector pilota-porta amb un mòdul de 0.35
posicio=new Vec2(portaprop);
posicio.add(pilota_porta);
resultat=new Vec2(posicio);
resultat.sub(jo); // Posició
defensa: entre la pilota i la porteria
if (resultat.r<0.05) velocitat=0.0;
// Aturar
else velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
Cobrir
Aquesta acció també la
realitza únicament el porter. Quan el porter es troba a fora de
la seva àrea aquesta acció el situa dins de l’àrea.
El vector resultant, tal i com mostra la Figura 6, serà el vector
jo – porteria pròpia i el robot anirà a velocitat màxima.
El codi de l’acció cobrir és
el següent:
resultat=jo_portaprop;
velocitat=1.0; // cobrir la
porteria
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
Figura
6. Acció Cobrir
Atacar
Aquesta acció situarà
el robot en el sentit del vector pilota – porteria contrària a una
distància 0.05 més enrera de la pilota per tal de poder-la
xutar. Com que es tracta d’una acció d’atac cal fer-la a velocitat
màxima (1.0). La Figura 7 mostra un exemple d’aquest tipus d’acció.
Figura
7. Acció Atacar
El codi d’aquesta acció
és:
pilota_porta=new Vec2(portacont);
pilota_porta.sub(pilota);
pilota_porta.setr(-0.05);
posicio=new Vec2(pilota);
posicio.add(pilota_porta);
resultat=new Vec2(posicio);
//anar darrera la pilota, encarar-se i xutar
resultat.sub(jo);
velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
Obert Dreta / Obert
Esquerra
Aquestes dues accions són anàlogues
i situaran el jugador a la banda dreta o esquerra del camp a la mateixa
alçada, en coordenades x, que la pilota. En la Figura 8 veiem els
dos laterals que es troben oberts a la dreta i a l’esquerra que s’estan
situant a l’alçada de la pilota en x. Aquesta acció es realitzarà
a velocitat màxima fins que no arribi a la posició desitjada.
El codi per l’acció Obert Dreta
és el següent:
posicio=new Vec2(pilota.x,0.4*portaprop.x/Math.abs(portaprop.x));
resultat=posicio;
resultat.sub(jo); // anar al
lateral dret, a l'alçada x de la pilota
if (resultat.r<0.05) velocitat=0.0;
else velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
El codi per l’acció Obert Esquerra
és el següent:
posicio=new Vec2(pilota.x,-0.4*portaprop.x/Math.abs(portaprop.x));
resultat=posicio;
resultat.sub(jo); // anar al
lateral esquerra, a l'alçada x de la pilota
if (resultat.r<0.05) velocitat=0.0;
else velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
Figura
8. Accions Obert Dreta i Obert Esquerra
Zona Atac
Com mostra la Figura 9 , aquesta acció
situarà el jugador al camp contrari, a una distància determinada
del mig camp en coordenades x, i a l’alçada, en coordenades y, de
la pilota. L’acció es realitzarà a velocitat màxima
fins que no s’arribi al punt desitjat.
Figura
9. Acció Zona Atac
El codi d’aquesta acció
és el següent:
posicio=new Vec2(-0.4*portaprop.x/Math.abs(portaprop.x),pilota.y);
resultat=posicio;
resultat.sub(jo); // anar a
zona d'atac, a l'alçada y de la pilota
if (resultat.r<0.05) velocitat=0.0;
else velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
Zona Defensa
Aquesta acció segueix la mateixa
filosofia que l’anterior però en aquest cas no es va al camp contrari
si no que el jugador es situa al camp propi a una distància determinada
en x i a l’alçada de la pilota en y. La Figura 10 mostra un exemple
d’aquesta acció.
El codi d’aquesta acció és
el següent:
posicio=new Vec2(0.4*portaprop.x/Math.abs(portaprop.x),pilota.y);
resultat=posicio;
resultat.sub(jo); // anar a
zona de defensa
if (resultat.r<0.05) velocitat=0.0;
else velocitat=1.0;
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
if (abstract_robot.canKick(temps_act))
abstract_robot.kick(temps_act);
Figura
10. Acció Zona Defensa
Aturar
Aquesta acció és la més
simple de totes i es tracta de parar el jugador, és a dir, donar-li
velocitat 0. Cal tenir en compte, però, que encara que el robot
està parat, s’anirà encarant a la pilota a mesura que aquesta
es vagi movent.
El codi d’aquesta acció és
el següent:
resultat=jo_pilota;
velocitat=0.0; // para el robot
abstract_robot.setSteerHeading(temps_act,
resultat.t);
abstract_robot.setSpeed(temps_act,
velocitat);
JOCS
DE PROVES
S’han desenvolupat tres jocs de proves
diferents (porter, defensa i mig) per tal d’analitzar millor els moviments
dels jugadors porter, defensa i mig, independentment. Un quart fitxer de
proves (o7kou) serà el que jugarà amb tots els cinc
jugadors (porter, defensa, mitjos i davanter). En aquest exemple es podrà
apreciar el funcionament dels diversos agents, la presa de decisions utilitzant
consensos i el canvi de prestigis dels agents mitjançant l’algoritme
adaptatiu.
Tots els jocs de proves s’ha realitzat
contra l’equip SchemaDemo. Segons les proves realitzades aquest
és un dels millors equips dels que hi ha en els d’exemple.
Per a observar l’acció que està
realitzant cada jugador cal activar l’opció robot state del
menú view.
Porter
El fitxer de proves porter.bat mostrarà
el funcionament del porter. Tal i com mostra la Figura 11 es jugarà
u contra cinc contraris.
El porter realitzarà l’acció
defensar, cobrir i xutar tal i com s’ha explicat anteriorment.
Figura
11. Exemple de proves porter.bat
Defensa
El fitxer defensa.bat mostrarà
el funcionament del defensa i el porter jugant contra cinc contraris. Les
accions del defensa són treu la pilota cap al camp contrari i defensar
l’atac dels contraris. La Figura 12 mostra un instant del joc.
Figura
12. Exemple de proves defensa.bat
Mig
En aquest exemple (mig.bat)
es veurà el funcionament del mig esquerra juntament amb el defensa
i el porter. La Figura 13 mostra un exemple d’execució.
Figura
13. Exemple de proves mig.bat
Ou7kou
En aquest exemple (fitxer ou7kou.bat)
podem observar el joc de tots cinc components de l’equip. Aquesta execució
és on s’observa millor el consens de les decisions entre els jugadors
i la variació dels prestigis de cada agent en funció del
desenvolupament del joc. La Figura 14 mostra un instant del joc. El davanter
està en posició de xutar, els dos mitjos es troben oberts
a la seva banda corresponent i el defensor es troba a la zona de defensa.
En aquesta jugada es produeix un conflicte entre el davanter i el mig esquerra
ja que inicialment tots dos volen anar a la pilota. El davanter té
major necessitat d’anar a la pilota que el mig esquerra. donat que el valor
de la decisió multiplicat per la necessitat del mig esquerra és
menor que el prestigi que té el mig esquerra del davanter, aleshores
el mig canviarà la seva decisió deixant que sigui el davanter
el que vagi a la pilota.
Figura
14. Exemple de proves ou7kou.bat