Dazoot Catch That Fish!
 
   

Catch That Fish!

PDF
PDF

Prezentare generala

Aplicatia de fata este o implementare in Java a interactiunii intre mai multi agenti ce au capacitatea de a decide singuri asupra actiunilor ce vor fi efectuate.

Este simulat un colt de mare, vazut ca o matrice MxN. Acesta este cadrul in care se desfasoara interactiunea intre doua tipuri de agenti:de o parte barcile, si de cealalta pestii. Scopul barcilor este de a pescui toti pestii aflati in lac. Pentru aceasta barcile coopereaza intre ele, ajutandu-se in depistarea locurilor cu peste. Configuratia lacului este generata Random la inceputul programului. Default avem 2 barci si 25 de pesti, intr-un lac cu dimensiunea de 10x10.

Nota
Site-ul de prezentare al aplicatie se gaseste la adresa:http://opensource.dazoot.ro/member/dana/fish

Descrierea elementelor

Lacul

Lacul este reprezentat de o matrice NxM, matrice care contine elemente de tip celula. O celula poate sa aiba fie un peste, fie o barca fie doar apa. Lacul contine un sistem care elimina dupa o perioada de timp direle lasate de barci pentru a marca diverse celule care nu contin pesti.

Comportamentul agentilor

Cele doua tipuri de agenti sunt: barcile si pestii. Fiecare ocupa cate o celula din lac. Agentii actioneaza independent unul de celalalt, fiecare fiind autonom in ceea ce priveste luarea deciziilor. Unii dintre agenti coopereaza in scopul lor comun (barcile au ca scop prinderea tuturor pestilor care se afla in perimetrul lacului, si in acest scop se ajuta una pe cealalta.

Barca

O barca cauta mereu sa determine cea mai buna miscare urmatoare. Pentru aceasta ea dispune de instrumentele de bord, ce o ajuta sa detecteze daca in patratelele invecinate se afla sau nu peste. Initial ea dispune de o aparatura primitiva, fapt ca re ii restrictioneaza informatiile la doua patratele distanta. O data cu prinderea de pesti, isi perfectioneaza instrumentele. La fiecare cinci pesti prinsi va putea afla informatii cu o celula mai mult intr-o directie.

Dintr-o pozitie barca se poate misca in 4 directii: sus, jos, stanga, dreapta. La un moment dat barca va culege informatii despre fiecare celula in care se poate deplasa. Comparand informatiile primite, ea va alege mutarea cu ponderea cea mai mare. Ponderea mutarii se refera la cantitatea de peste ce va fi gasita si pescuita daca barca se deplaseaza in directia respectiva. Barca va colecta informatiile despre fiecare directie intr-un vector de miscari posibile, si apoi va decide in ce directie se va misca. O miscare poate sa aiba urmatoarele valori: -2 -> mutare imposibila (in aceea directie se ajunge pe malul lacului) -1 -> fara peste, care a fost insemnat de o alta barca ca fiind loc care nu conduce la peste 0 -> fara peste, nemarcat >1 -> in directia respectiva se gaseste peste Barca va verifica in fiecare directie atatea patratele cat este capabila. Valoarea intoarsa, daca exista peste, nu va fi numarul de patratele in care a fost identificat, ci cu cat sunt mai aproape acestea, cu atat au o pondere mai mare. Acest lucru este necesar pentru a evita situatiile in care o barca identifica in doua directii peste disponibil, intr-una imediat langa ea, iar in cealalta la 3 casute distanta, iar ea sa aleaga a doua varianta de mutare. Punand o pondere la suma calculata, directia in care pestele se afla la o patratica distanta va returna o valoare mai mare decat cazul al doilea. Daca sunt mai multe directii in care valoarea returnata este maxima, atunci barca va alege random una din ele. Daca nu identifica peste in nici una din cele patru directii, va prefera sa se deplaseze intr-o celula nemarcata de o alta barca. In acest caz va marca inainte sa plece celula in care se gasea, pentru a indruma celelalte barci de pe lac.

Intr-o celula a lacului se pot gasi in acelasi timp doua barci. Barcile pescuiesc intr-un timp fishTime si se deplaseaza intre doua celule intr-un timp moveTime.

Pestii

Un peste va incerca pe cat posibil sa fuga de barcile care vor sa il pescuiasca. Pestele este dotat cu o inteligenta redusa, in unele cazuri chiar daca simte apropierea barcii va alege sa nu se miste. Pestii sunt de doua feluri: cu experienta sau fara experienta.

Cei fara experienta isi vor alege de fiecare data cand vor sa se miste o miscare random, intr-una din directiile posibile. Faptul ca se decide sa se miste nu este influentat de apropierea unei barci. Decizia unui peste fara experienta de a se deplasa din celula in care se afla este random. Decizia de a efectua o miscare intr-o directie oarecare are o probabilitate de 1 la 10. Nu este posibil ca un peste sa se afle in aceeasi celula cu un altul, si nici sa depaseasca conturul lacului. Un peste fara experienta va putea chiar sa se mute intr-o patratica in care o barca si-a intins plasa de pescuit, caz in care va muri.

Pestii cu experienta in schimb, sunt capabili sa detecteze valurile facute de o barca de la doua casute distanta. Ei afla informatiile pentru fiecare directie, si daca observa ca sunt in pericol de a fi pescuiti, "decid" daca sa se miste sau sa ignore informatiile. O alta capacitate a pestilor cu "experienta" este de a simti panica transmisa de un peste invecinat in momentul in care acesta este prins de o barca. In mod normal, chiar daca detecteaza in jur barci decizia de a se misca are o probabilitatea de 1 la 10. Insa daca simte "panica" generata de un alt peste, decizia de a se misca intr-o directie are o pondere de 1 la 5.

Pestii se deplaseaza intre doua celule cu un timp moveTime.

Un banc de pesti este format din toti pestii uniti prin maxim o patratica goala. Doar pestii cu experienta din banc vor simti apropierea barcii si eventual vor lua decizia de a pleca intr-o directie "sigura".

Implementarea

Lumea in care are loc interactiunea dintre agenti

Aceasta lume este reprezentata de lac, obiect care contine atat o matrice a carui elemente corespund continutului lacului, dar si vectori ce retin pestii (si daca acestia mai sunt vii) si barcile de pe lac. Din punct de vedere grafic, lacul este un obiect ce extinde JPanel.

Unitatea de masura" a lacului este celula. Celula este un obiect care retine coordonatele sale, parintele sau(lacul din care face parte), continutul sau (o celula poate sa contina fie peste, fie barca, fie doar apa). Clasa Cell are o variabila interna de tip JLabel care va fi pozitionata in Lake (JPanel) la pozitia retinuta de celula, si careia ii este atasata o ImageIcon ce reprezinta continutul celulei. ImageIcon-ul ce va continut in variabila de tip JLabel are dimensiunea 30x20. Exista trei ImageIcon-uri diferite, reprezentand cele trei tipuri de continut. Contructorul unui obiect de tip Cell va primi coordonatele "virtuale" ale acesteia, raportate la unitatea de masura a lacului. Pentru a pozitiona JLabel-ul corect in JPanelul reprezentat de un obiect de tip Lake trebuie sa calculam pozitia reala a celulei. In secventa de cod urmatoare este prezentata functia care calculeaza pozitia pe x si pe y a celulei grafice.

				
public void setPosition()
{
	posx=(xx-1) * 30;
	posy=(yy-1) * 20;
}
				

Toate imaginile sunt incarcate o singura data in cadrul constructorului clasei Lake. La pornirea sa, lacul porneste sistemul de eliminare a direlor lasate de barci. Acesta, o data la 4*moveTime-ul unei barci, sterge toate marcajele. Acesta este implementat in clasa EcoSystem.java si momentan are ca unic rol stergerea marcajelor.

Agentii

Agentii se gasesc in package-ul ro.dazoot.fishermen.agents. Ei sunt implementati ca clase care extind Thread, putind astfel sa isi execute actiunile in paralel. Din aceasta cauza, metodele din Lake care ating resurse comune (cum ar fi matricea ce retine continutul lacului) sunt synchronized pentru a permite accesul doar a unui singur thread la un moment dat. Aceasta este necesara pentru a evita situatiile in care doua barci ar pescui acelasi peste etc.

O barca sau un peste pot efectua 4 tipuri de miscari: stanga, dreapta, sus si jos. Tipul acestor mutari, si directia imprimata de acestea sunt retinute in obiecte de tip Move. Algoritmul dupa care o barca va alege miscare urmatoare este prezentat intr-o sectiune anterioara, in descrierea comportamentului unui agent de tip Barca.

				
public Move(int direction)
{
	dy=0;dx=0;
	if(direction==0) dy=-1;
	if(direction==1) dx=1;
	if(direction==2) dy=1;
	if(direction==3) dx=-1;	
	this.direction=direction;
}
		
Barca

Barca este implementata in clasa Boat.java. Ea are o variabila interna de tip Cell, in care se retin coordonatele sale. Alte caracteristici ale unui obiect de tip Boat sunt: id, limit (limita superioara de casute pana la care poate sa detecteze existenta pestelui barca respectiva).

Barca, o data pornita ca thread, intra intr-un ciclu atata timp cat mai exista peste pe lac, in care la fiecare iteratie va decide care este cea mai buna miscare a sa urmatoare. In fragmentul de cod urmator este prezentata functia run() a unui obiect de tip Boat(). In cadrul acestei metode, o barca stabileste miscarea, si daca cumva prin aceasta miscare a prins un peste va stationa un timp fishTime pentru aceasta operatiune. Deasemenea isi incrementeaza numarul de pesti prinsi, si daca s-au mai adunat 5 isi incrementeaza limita. Daca a fost doar o mutare intr-o patratica in care se gasea apa, aceasta ii va lua o perioada moveTime.

				
public void run()
{
	int ret;
	int sleepTime;
	int moveTime=1000;
	int fishTime=2000;
	while(cell.getLake().getNrFishes()>0)
	{
		ret=move();
		if(ret==1)
		{
			this.caught++;
			if(this.caught%5==0) growLimit();
			try{
				sleep(fishTime+moveTime);
			}catch(Exception e){}
		}
		else
		{
			try{
				sleep(moveTime);
			}catch(Exception e){}		
		}
		if(ret!=-1) 
		cell.getLake().catchThatFish();	
	}	
}
			

Pentru a decide cea mai buna mutare urmatoare barca va scana aria inconjuratoare in cautare de informatii. Aceasta se face in functia scanArea() si se apeleaza functia getInfo din obiectul lake intern variabilei de tip Cell a barcii.

				
public synchronized int getInfo
(int x,int y,Move m,int about,int limit)
{
	int ret=0,i;	
	
	//verific intai daca mutarea este permisa
	//daca nu se poate efectua mutarea intorc -2
	if(((m.newX(x))>=h)||((m.newY(y))>=w)||
	((m.newX(x))<0)||((m.newY(y))<0))
		return -2;
	
	for(i=0;i<limit;i++)
	{
		//daca mai exista
		if(((m.newX(x)+i*m.getDX())<h)
		&&((m.newY(y)+i*m.getDY())<w)&&
		((m.newX(x)+i*m.getDX())>=0)&&
		((m.newY(y)+i*m.getDY())>=0))
		{
			if(getContent(m.newX(x)+i*m.getDX()
				,m.newY(y)+i*m.getDY())==about) 
				ret+=limit-i;
		}
		else break;
		
	}
	
	if((ret==0)&&(about==1))
		return sniffMarks(x,y,m);
	return ret;
}
		

Functia getInfo() de mai sus verifica continutul a limit celule din lac in directia imprimata de Move m. Valoarea cautata este data de variabila about, in cazul barcilor va avea valoarea 1(se cauta pesti). Cu cat gasim valoarea about mai aproape de pozitia indicata, cu atat are ponderea mai mare. Ponderea scade invers proportional cu departarea de pozitia indicata, adica pozitia vecina are ponderea limit, pozitia aflata la 2 casute distanta are ponderea limit-1 etc. Daca nu gasim peste in nici una din celulele investigate intoarcem daca celula imediat vecina in directia Move m este sau nu marcata de o alta barca. Marcajele se retin intern de Lake intr-o matrice, care daca are valoarea -1 in celula [i][j], inseamna ca aceasta a fost marcata recent de o alta barca ca nefiind o directie buna.

Dupa ce Boat isi strange informatiile referitoare la fiecare din celulele in care s-ar putea deplasa, incepe prelucrarea acestora in vederea obtinerii mutarii urmatoare. Prelucrarea se face in functia getNextMove() care returneaza codul miscarii ce va fi executata. Modalitatea dupa care se face este urmatoarea: -se parcurge vectorul possibleMoves, ignorandu-se mutarile care au ponderea -2 (mutare nepermisa) -se construieste un vector maxp, care contine mutarile cu pondere maxima, retinandu-se intr-o variabila si aceasta valoare a ponderii maxime -daca dam peste o mutare mai mare decat maximul actual se modifica valoarea acestuia, si se reincepe construirea vectorului maxp -daca la final vectorul maxp are mai mult de o valoare se alege random una din directiile care sunt memorate in el In secventa de cod urmatoare, luata din functia getNextMove() este prezentat acest ciclu de identificare a mutarii urmatoare:

				
for(i=0;i<4;i++)
{
	//aceasta mutare nu este permisa
	if(possibleMoves[i]==-2) continue;
	if(possibleMoves[i]>max)
	{
		//daca am intalnit o mutare 
		//cu ponderea mai mare decat maximul precedent
		max=possibleMoves[i];
		nrmax=1;
		maxp[0]=i;
	}	
	else if(possibleMoves[i]==max)
	{
		//daca am intalnit inca o mutare 
		//cu aceeasi pondere cu cea maxima existenta 
		//o adaugam in maxp
		maxp[nrmax]=i;
		nrmax++;	
	}
}
		

In cele doua figuri din continuare sunt prezentate doua cazuri, si decizia ce va fi luata de barca implicata.

Pesti in doua directii

In figura de mai sus, barca aflata la pozitia (5,7) are cate un peste atat in stanga, cat si in jos. Din aceste doua directii, ea va alege sa se miste in jos, deoarece pestele din aceasta directie este mai aproape si necesita un efort mai mic prinderea lui. Alegerea acestei directii este data tocmai de ponderea folosita la calcularea valorii returnate pentru fiecare directie de miscare de functie Lake.getInfo(..). Presupunand ca barca are limita de "vizibilitate" egala cu 2 valoarea pentru directia jos va fi limit*nrPesti=2, iar pentru directia stanga va fi (limit-1)*nrPesti=1. Cea de-a doua barca aflata in configuratie, daca are limita >=3 va alege random intre directiile stanga sau dreapta. Daca limita este mai mica decat 3, si nu simte cei 2 pesti aflati la o distanta de 3 casute de ea va alege random o directie de miscare.

Un singur peste ramas

In aceasta a doua configuratie, in lac mai exista un singur peste pe care il vaneaza cele doua barci. Insa din cauza distantei prea mari intre locul in care se afla pestele si pozitia barcilor acestea se vor misca random, pana cand pestele va intra in raza "vizuala" a uneia dintre barci.

O data decisa mutarea urmatoare se apeleaza functia din Lake moveBoat si se schimba si coordonatele din variabila cell interna. In functia moveBoat se stabileste daca patratica in care s-a mutat barca era un peste, si daca da i se transmite acelui Peste ca a fost capturat. Totodata se schimba imaginile aferente celor doua celule implicate in mutare: celula in care era barca devine apa, iar celula in care se misca barca capata boatIcon. Apoi se va apela metoda repaint() din Lake pentru a se redesena noua configuratie a lacului.

Pestii

Un agent de acest tip este definit de clasa Fish.java. Ca mod de abordare este foarte asemanator cu definirea unui agent Boat. La initializarea unui astfel de obiect, se decide daca pestele este cu experienta.

Threadul care va reprezenta un peste, o data pornit, va rula pana cand i se comunica de la Lake ca a fost prins. Acest lucru il afla prin apelul functiei Lake.isAlive(id) care daca returneaza valoarea 0 inseamna ca pestele a murit, si atunci threadul se opreste. In functia run() pestele decide daca si unde muta, in cazul in care nu a fost omorat de o barca

				
public void run()
{
	//timpul de deplasare in casuta respectiva
	int moveTime=1000;
	
	//while(alive==1)
	while(cell.getLake().isAlive(this.id)==1)
	{
		int ret=move();
		if(cell.getLake().isAlive(this.id)==1)
		try{
			sleep(moveTime);
		}catch(Exception e){}		
		if((ret!=-1)
			&&(cell.getLake().isAlive(this.id)==1))
				cell.getLake().catchThatFish();	
	}	
	System.out.println("Fish "+id+" ::am murit");
}				
		

Pestii fara experienta, la fiecare iteratie din run() vor decide cu o probabilitate de 1 la 10 daca se misca sau nu. O data decizia de a se misca luata, se vor misca intr-o directie random, respectand conditiile sa nu iasa din lac si sa nu intre in casuta altui peste.

				
if(experience==0)
{
	aux=r.nextInt(4);	
	while ((possibleMoves[aux]==-2)||
	(cell.getLake().getContentInDirection(cell.getX(),
		cell.getY(),
		new Move(possibleMoves[aux]))==1))
		aux=r.nextInt(4);
	//probabilitate de 1 la 10 ca 
	//pestele sa vrea sa se mute din loc
	if(r.nextInt(10)!=0) return -1;
	return aux;
}				
			

La pestii cu experienta, calculul mutarii urmatoare este similar cu cel al barcii (tot apeland getInfo(..) al obiectului de tip Lake intern, de data aceasta cu about=3 - barci).Insa la prelucrarea rezultaturilor obtinute se cauta celula cu pondere minima (cat mai putine barci in jur in directia in care se va deplasa). Fiind insa inferiori unei barci, chiar si pestii cu experienta au o sansa din 10 sa decida daca fug sau nu de pericolul reprezentat de barci, daca nu "simt" si panica unui peste invecinat tocmai capturat de o barca. In acest caz au o sansa de 1 la 5 de a se hotara sa fuga din calea pericolului. In secventa de cod din continuare este prezentat modul in care un peste decide daca sa se miste in directia aleasa ca optima sau nu.

				
//vedem daca nu cumva nu este amenintat 
//de nici o barca in acest caz nu se misca din loc
if(max==0) return -1;

//daca nu a simtit panica in jur
if(panic==0)
{
	//pestele are o probabilitate din 10,
	//chiar avand experienta, 
	//sa isi dea seama de pericolul reprezentat de barca
	if(r.nextInt(10)!=0) return -1;
}
//daca a simtit panica e mai ingrijorat, 
//si are o probabilitate de miscare de 1 la 5
else if(r.nextInt(5)!=0) return -1;

//daca e o singura mutare care are 
//ponderea mai mica decat a tututor celorlalte
if(nrmin==1) return minp[0];
//daca nu atunci alegem aleator 
//o mutare din vectorul de minp
return minp[r.nextInt(nrmin)];	
			

Credits

Nume Email
Indrumator stiintific: Cristian Stoica office.aquasoft.ro
Programator: Daniela Tiba dana.at.dazoot.ro

 

 

 

by TIBA Daniela
Valid HTML 4.01!Valid CSS!