カタベログ

IT技術に関するブログを書きたい.食べ物関連はInstagramをご参照の事.

ProcessingでLotka-Volterraの方程式の実験

学校の課題で作ってみました.クソグラムです.

//################################################
//Lotka-Volterra equation Program 2011/05/31
//Programmer : Takuma KATANOSAKA
//  Enviroment : 2D 100 x 100 cell (torus)
//################################################

import java.util.*;
import java.lang.*;

//################################################
//Global Variables
//  predator
//    true (If the predator exists in (X,Y))
//    false (otherwise)
//  
//  prey
//    true (If the prey exists in (X,Y))
//    false (otherwise)
//  
//  Vector<Predator> predator_vect  
//################################################

boolean[] predator = new boolean[10000];
boolean[] prey = new boolean[10000];
Vector<Predator> predator_vect = new Vector();

//################################################
//Global Methods
//  stocasticChoise(float [])
//  stocasticChoise(float [], Vector<Integer>)
//  renewCoord(int [], int)    
//################################################

int stocasticChoise(float [] reward_table){
  float sum_gi = 0;
  Vector oi = new Vector();
  
  for(int i = 0, imax = reward_table.length; i < imax; ++i){
    sum_gi += pow(2.0, reward_table[i]);
  }
  for(int i = 0, imax = reward_table.length; i < imax; ++i){
    oi.add(pow(2.0, reward_table[i]) / sum_gi);
  }

  return (int)oi.indexOf(Collections.max(oi)) + 1;
}
int stocasticChoise(float [] reward_table, Vector<Integer> removeElementsIndex){
  float sum_gi = 0;
  Vector oi = new Vector();
  
  for(int i = 0, imax = reward_table.length; i < imax; ++i){
    sum_gi += pow(2.0, reward_table[i]);
  }
  for(int i = 0, imax = reward_table.length; i < imax; ++i){
    oi.add(pow(2.0, reward_table[i]) / sum_gi);
  }
  for(int i : removeElementsIndex){
     oi.remove(i);
  }
  
  return (int)oi.indexOf(Collections.max(oi)) + 1;
}

void renewCoord(int [] coord, int mv){
  switch(mv){
    case 1:  coord[1] -= 1;  break;
    case 2:  coord[0] -= 1;  coord[1] += 1;  break;
    case 3:  coord[0] += 1;  coord[1] += 1;  break;
    case 4:  coord[0] -= 1;  coord[1] -= 1;  break;
    case 5:  coord[0] += 1;  coord[1] -= 1;  break;
    default: exit(); break;
  }
  
  //Enviroment is torus.
  if(coord[0] < 0)  coord[0] = 99;
  else if(coord[0] > 99)  coord[0] = 0;
  
  if(coord[1] < 0)  coord[1] = 99;
  else if(coord[1] > 99)  coord[1] = 0;
}

//################################################
//Predator
//  -Output : Front, Left, Right, Hardly Left, Hardly Right 
//  -Output Function : Stochastic Choice
//  -States : Ability Table 
//  -State Transition Function :  E = E + delta E (To move neighbor cell need 5 energy)
//                                delta E = 15, Ability(t + 1) = Ability(t) + 1 (when it collides with a prey)
//                                delta E = -5, Ability(t + 1) = Ability(t) - 1 (otherwise)
//                                If predotor's energy is bigger than 1000, its predator segments with mutation.
//################################################

class Predator{
  private int x, y;
  private float [] reward_table = {10,10,10,10,10};
  private int E;
  
  Predator(){
    x = (int)random(0, 99);
    y = (int)random(0, 99);
    E = 500;
  }
  Predator(Predator p){
    x = p.getX();
    y = p.getY();
    E = p.getE() / 2;
    
    //Mutation
    if(p.getE() >= 1000){
      float random_value = random(0, 1);
      //reward_table = Arrays.copyOf(p.getRewardTable(), p.getRewardTable().length);
      float [] temp = p.getRewardTable();
      for(int i = 0, imax = p.getRewardTable().length; i < imax; ++i){
        reward_table[i] = temp[i];
      }
      if(0 <= random_value && random_value < 0.2){
        reward_table[0] += reward_table[0] * random(-1, 1);
      }else if(0.2 <= random_value && random_value < 0.4){
        reward_table[1] += reward_table[1] * random(-1, 1);
      }else if(0.4 <= random_value && random_value < 0.6){
        reward_table[2] += reward_table[2] * random(-1, 1);
      }else if(0.6 <= random_value && random_value < 0.8){
        reward_table[3] += reward_table[3] * random(-1, 1);
      }else{
        reward_table[4] += reward_table[4] * random(-1, 1);
      }
    }
  }
  
  public int getX(){
    return x;
  }
  public int getY(){
    return y;
  }
  public int getE(){
    return E;
  }
  public float [] getRewardTable(){
    return reward_table;
  }
  
  public void move(){
    int mv = stocasticChoise(reward_table);
    int [] temp_coord = {x, y};
    Vector<Integer> removeElementsIndex = new Vector();
    /**
    Forward : 1
    Left : 2
    Right : 3
    Hardly Left : 4
    Hardly Right : 5 
    */
    
    predator[100*x + y] = false;
    
    renewCoord(temp_coord, mv);
    while(predator[100*temp_coord[0] + temp_coord[1]]){
      removeElementsIndex.add(mv - 1);
      if(removeElementsIndex.size() == 5){
        temp_coord[0] = x;
        temp_coord[1] = y;
        break;
      }
      mv = stocasticChoise(reward_table, removeElementsIndex);
      renewCoord(temp_coord, mv);
    }
    x = temp_coord[0];
    y = temp_coord[1];
    predator[100*x + y] = true;
    
    //Eval Energy
    energy(mv);
  }
  
  private void energy(int action){
    if(prey[x*100 + y]){
      reward_table[action - 1] += 0.5;
      prey[x*100 + y] = false;
      E += 15;
    }else{
      reward_table[action - 1] -= 3.0;
      E -= 5;
    }
  }
  
  public Vector segmentation(){
    Vector son_vect = new Vector();

    if(E >= 1000){
      for(int i = 0; i < 2; ++i){
        Predator son = new Predator(this);
        son_vect.add(son);
      }
    }
    
    return son_vect;
  }
  public boolean isDead(){
    if(E <= 0){
      return true;
    }else{
      return false;
    }
  }
}

//################################################
//
//Main Method Part (Set up & Draw)
//
//################################################

void setup(){
  frameRate(120);
  
  size(600, 600);
  background(0,0,128);
  smooth();
  
  //PREY
  fill(255,255,255);
  for(int i = 0; i < 100; ++i){
    for(int j = 0; j < 100; ++j){
      if(random(0, 1) < 0.15){
        prey[i*100 + j] = true;
        //predator[i*100 + j] = true;
        ellipse(6*i+3, 6*j+3 ,6,6);
      }else{
        prey[i*100 + j] = false;
      }
    }
  }

  //PREDATOR
  fill(255,0,0);
  for(int i = 0; i < 5; ++i){
    Predator p = new Predator();

    if(predator_vect.add(p) == false){
      exit();
    }

    predator[p.getX()*100 + p.getY()] = true;
    prey[p.getX()*100 + p.getY()] = false;
    
    ellipse(6*p.getX() + 3, 6*p.getY() + 3, 6, 6);
  }
}

//++++++++++++++++++++++++++++++++++++++++
void draw(){
  int prey_num = 0;
  
  background(0,0,128);
  
  //PREDATOR
  for(int i = 0; i < predator_vect.size(); ++i){
    Predator p = predator_vect.get(i);
    Vector son_vect;
    p.move();
    
    if(p.isDead()){
      predator[p.getX()*100 + p.getY()] = false;
      predator_vect.remove(i);
    }else{
      //If a predator is at the point of death, change the color of it.  
      if(p.getE() <= 100){
        fill(128, 255, 0);
      }else{
        fill(255, 0, 0);
      }
      
      son_vect = p.segmentation();
      if(son_vect.isEmpty()){
        ellipse(6*p.getX() + 3, 6*p.getY() + 3,6,6);
      }else{
        predator_vect.remove(i);
        if(!predator_vect.addAll(son_vect)){
          exit();
        }
        fill(255, 255, 0);
        ellipse(6*p.getX() + 3, 6*p.getY() + 3,6,6);
      }
    }
  }
  //PREY
  fill(255,255,255);
  for(int i = 0; i < 100; ++i){
    for(int j = 0; j < 100; ++j){
      if(prey[i*100 + j] == true){
        prey_num++;
        ellipse(6*i+3, 6*j+3,6,6);
        if(random(0, 1) < 0.15){
          if((j > 0) && prey[i*100 + (j - 1)] == false && predator[i*100 + (j - 1)] == false){
            prey[i*100 + (j - 1)] = true;
            prey_num++;
            ellipse(6*i+3, 6*(j - 1)+3,6,6);
          }else if((j < 99) && prey[i*100 + (j + 1)] == false && predator[i*100 + (j + 1)] == false){
            prey[i*100 + (j + 1)] = true;
            prey_num++;
            ellipse(6*i+3, 6*(j + 1)+3,6,6);
          }else if((i > 0) && prey[(i - 1)*100 + j] == false && predator[(i - 1)*100 + j] == false){
            prey[(i - 1)*100 + j] = true;
            prey_num++;
            ellipse(6*(i - 1)+3, 6*(j + 1)+3,6,6);
          }else if((i < 99) && prey[(i + 1)*100 + j] == false && predator[(i + 1)*100 + j] == false){
            prey[(i + 1)*100 + j] = true;
            prey_num++;
            ellipse(6*(i + 1)+3, 6*j+3,6,6);
          } 
        }
      }
    }
  }
  println(predator_vect.size() + "\t" + prey_num);
}

Processing(Proce55ing : P5)はJavaで簡単にアニメーションを作ることのできる面白い代物.
自分の環境(Mac OSX 10.6)では動作が遅く不安定だけど,Windowsで使うとなかなかサクサク動く.
Javaを知ってて,アニメーションを作りたい人は知ってて損はないものだと思います.
本当に簡単に作れるのでプログラミング教育用にも向いていると思います.

・リンク

公式には英語だけど本体とリファレンスが置いてある.