Życie w RAM-ie
-
- Andrzej Stasiewicz,
- 01.12.2005
Po tych czynnościach wstępnych zabierajmy się do pracy. Zacznijmy od utworzenia mechanizmu mutacji, czyli przypadkowej zmienności formuły (genotypu) stworka (rysunek 2). Potrzebna będzie funkcja mogąca zmienić wartość dowolnego parametru struktury Stworek. Będzie nieskomplikowana, ale długa, bo każdy gen wymaga innego potraktowania algorytmem modyfikującym. Wprowadźmy także do gry parametr intensywnosc, który w jakiś sposób będzie regulował natężenie mutacji. Proponuję - ale się nie upieram - taki oto kształt algorytmu:
mutuj( int intensywnosc)
{
int co, il_przekszt, nr;
if( random( 101) < intensywnosc)
{
//która cecha (gen) mutuje?
co = random( 8);
il_przekszt = S.il_przekszt;
//które przekształcenie mutuje?
nr = random( il_przekszt);
switch( co)
{
case 0: //liczba przekształceń +/- 1
il_przekszt = il_przekszt + random(3)-1;
if( il_przekszt > MAX_IL_PRZEKSZT)
il_przekszt = MAX_IL_PRZEKSZT;
if( il_przekszt < 2)
il_przekszt = 2;
//przybyło przekształceń?
if( il_przekszt > S.il_przekszt)
{ //jeśli tak, zainicjuj nowe poprzednim
S.G[il_przekszt-1][0]=S.G[il_przekszt-2][0];
S.G[il_przekszt-1][1]=S.G[il_przekszt-2][1];
S.G[il_przekszt-1][2]=S.G[il_przekszt-2][2];
S.G[il_przekszt-1][3]=S.G[il_przekszt-2][3];
S.G[il_przekszt-1][4]=S.G[il_przekszt-2][4];
S.G[il_przekszt-1][5]=S.G[il_przekszt-2][5];
S.P[il_przekszt-1] = S.P[ il_przekszt - 2];
}
S.il_przekszt = il_przekszt;
brea
k;
case 1: //mutacja genu 0 na głębokość +/-0.1
S.G[ nr][ 0] = S.G[ nr][ 0] +
(random( 2001) - 1000) / 10000.;
break;
case 2: //mutacja genu 1 na głębokość +/-0.1
S.G[ nr][ 1] = S.G[ nr][ 1] +
(random( 2001) - 1000) / 10000.;
//... geny 2, 3, 4, 5 - analogicznie
case 7: //mutacja prawdopodobieństw
S.P[ nr] = S.P[ nr] + random( 21) - 10;
if( S.P[ nr] < 1)
S.P[ nr] = 1;
}
}
}
Funkcja ta przypadkowo uszkadza jakiś fragment łańcucha genetycznego. Parametr intensywnosc blokuje wejście do funkcji tym łatwiej, im jest mniejszy i jego działanie jest zgodne z intuicyjnym odczuciem intensywności mutacji. Parametr co kieruje uwagę funkcji na konkretny gen. Długa fraza switch() pracowicie opisuje mechanizm zmiany każdego genu. Trzeba być bardzo ostrożnym - zmiana nie może naruszyć reguł działania stworka, np. liczba przekształceń zawsze musi się zawierać w zakresie od 2 do MAX_IL_PRZEKSZT, a każdy współczynnik afiniczny powinien być niewielką liczbą rzeczywistą.
Genotyp stworka i przytoczony tutaj algorytm jest zresztą i tak za krótki - już widzimy, że brakuje np. implementacji i zmienności koloru afinicznego dywanu (kolorem zajmowaliśmy się miesiąc temu). Więcej cech stworka znajdziecie w załączonych materiałach programistycznych.
Teraz skoncentrujemy się na następnym etapie - ocenie bieżącego stanu stworka. Ocenę będziemy wystawiać osobiście - ot taki dziwny świat, w którym ktoś ocenia i szereguje obywateli. Oczywiście to zły, korupcjogenny pomysł na społeczeństwo, ale za to łatwy do implementacji. Chciałoby się tutaj dostrzec trochę szersze analogie ze światem za oknem, ale trzymajmy się tematu tego artykułu...
Aby oceniać stworki, musimy mieć nie jednego, a całe ich społeczeństwo oraz lepszy interfejs programu (porównaj rysunek 3). Musimy dokładnie widzieć stworki, mieć wywiad - wgląd w ich społeczność - i dopiero wtedy kliknięciami myszy możemy wskazywać te, które nagrodzimy i te, które skażemy na śmierć. Wprowadzamy do programu dwie zmienne - numery stworka najlepszego i najgorszego:
int najlepszy; //numer akurat najlepszego...
int najgorszy; //...i najgorszego stworka
W interfejsie programu z rysunku 3 zmienne te są ustawiane za pomocą kliknięć odpowiednio zatytułowanych przycisków.
Po zrealizowaniu przypadkowej zmienności genotypów oraz presji środowiska (czyli wydzielania najlepszego i najgorszego stworka) czeka nas etap ostatni - implementacja narodzin i śmierci w populacji stworków. Jak już wspominałem, najlepszy stworek oraz stworek wylosowany umieszczą swoje wymieszane geny w tablicy najgorszego stworka. Najgorszy stworek tym samym opuści populację. Najlepszy przekaże swoje cechy potomkowi.
Oto propozycja algorytmu rozmnażania:
rozmnazaj( int intensywnosc)
{
int i, j, partner, czyj_gen, il_przekszt;
if( random( 101) < intensywnosc)
{
partner = random( IL_
STWORKOW);
czyj_gen = random( 2);
//0 - gen najlepszego, 1 - partnera
if( czyj_gen == 0)
il_przekszt = S[ najlepszy].il_przekszt;
else
il_przekszt = S[ partner].il_przekszt;
S[ najgorszy].il_przekszt = il_przekszt;
//dziedziczenie współczynników
for( i = 0; i < il_przekszt; ++i)
{
czyj_gen = random( 2);
if( czyj_gen == 0)
{
for( j = 0; j < 6; ++j)
S[ najgorszy].G[ i][ j] =
S[ najlepszy].G[ i][ j];
}
else
{
for( j = 0; j < 6; ++j)
S[ najgorszy].G[ i][ j] =
S[ partner].G[ i][ j];
}
//dziedziczenie prawdopodobieństw
czyj_gen = random( 2);
if( czyj_gen == 0)
S[ najgorszy].P[ i] = S[ najlepszy].P[ i];
else
S[ najgorszy].P[ i] = S[ partner].P[ i];
//dziedziczenie kolorów
czyj_gen = random( 2);
if( czyj_gen == 0)
S[ najgorszy].K[ i] = S[ najlepszy].K[ i];
else
S[ najgorszy].K[ i] = S[ partner].K[ i];
}
}
}