Javascript: 随机绘制成团的形状(遗传算法)

Javascript: Randomly drawing shapes in clumps (Genetic Algorithm)

问题

我正在尝试制作模仿森林鸟瞰图的图形,因此我需要在 canvas 上随机生成绿色形状。 但是,我希望这些形状随机成团,更像真正的森林。有没有一个巧妙的方法来做到这一点?

我的代码在 objective 函数真正合并之前涉及遗传算法的基础知识,Javascript 和 p5.js 包

核心问题

var settings = {
  forestSize : 500,
  rows : 124,
  cols : 249,
};

function onCanvas(position){
  return position*4+2;
}

var forest = new Forest(settings.forestSize);

function Tree(){
  this.posx = Math.floor(Math.random()*settings.cols);
  this.posy = Math.floor(Math.random()*settings.rows);
}

function Forest(f){
  this.forSize = f;
  this.trees = [];

  for (var x = 0; x < this.forSize; x++) {
    this.trees[x] = new Tree();
    if(this.trees.length>1){
      for(var y=0; y<x; y++){
      while(this.trees[x].posx==this.trees[y].posx&&this.trees[x].posy==this.trees[y].posy){
        this.trees[x] = new Tree();
        }
      }
    }
  }
}

//create world
function createWorld(){
  background("lightblue");

  fill(0,255,0);
  for(var x=0; x<settings.forestSize; x++){
    rect(onCanvas(forest.trees[x].posx), onCanvas(forest.trees[x].posy), 4, 4);
  }
}

function setup() {
  createCanvas(1000, 500);
}

function draw() {
  createWorld();
} 

所有脚本

var settings = {
  populationSize : 25,
  geneLength : 8,
  mutationProbability : 0.05,
  forestSize : 500,
  rows : 124,
  cols : 249,
  year : 365,
};

function onCanvas(position){
  return position*4+2;
}

function randombetween(min, max){
  return Math.random()*max;
}

var population = new Population(settings.populationSize, settings.geneLength);

function Sheep(g, dna){
  this.genLen = g;
  this.fitness=0;
  this.xpos = Math.floor(Math.random()*settings.cols);
  this.ypos = Math.floor(Math.random()*settings.rows);

  this.chromosome = new Array(this.genLen);
  if (dna != null){
    this.chromosome = dna;
  } else{
    for(var x=0; x<this.genLen; x+=4){
      this.chromosome[x] = Math.random();
      this.chromosome[x+1] = randombetween(0, 1-this.chromosome[x]);
      this.chromosome[x+2] = randombetween(0, 1-this.chromosome[x]-this.chromosome[x+1]);
      this.chromosome[x+3] = 1-this.chromosome[x]-this.chromosome[x+1]-this.chromosome[x+2];
    }
  }
}

function Population(p, g){
  this.popSize = p;
  this.sheep = [];

  for (var x = 0; x < this.popSize; x++) {
    this.sheep[x] = new Sheep(g, null);
  }
}

var forest = new Forest(settings.forestSize);

function Tree(){
  this.posx = Math.floor(Math.random()*settings.cols);
  this.posy = Math.floor(Math.random()*settings.rows);
}

function Forest(f){
  this.forSize = f;
  this.trees = [];

  for (var x = 0; x < this.forSize; x++) {
    this.trees[x] = new Tree();
    if(this.trees.length>1){
      for(var y=0; y<x; y++){
      while(this.trees[x].posx==this.trees[y].posx&&this.trees[x].posy==this.trees[y].posy){
        this.trees[x] = new Tree();
        }
      }
    }
  }
}


//begin generation count
var generation=0;

//begin day count
var counter = 0;
function endRun(end){
  if(end>=settings.year){
    noLoop();
  }
}

//create world
function createWorld(){
  background("lightblue");

  fill(0,255,0);
  for(var x=0; x<settings.forestSize; x++){
    rect(onCanvas(forest.trees[x].posx), onCanvas(forest.trees[x].posy), 4, 4);
  }

  fill(255,0,0);
  for(var x=0; x<settings.populationSize; x++){
    rect(onCanvas(population.sheep[x].xpos), onCanvas(population.sheep[x].ypos), 4, 4);
  }

  //remove eaten trees
  for(var x=0; x<settings.populationSize; x++){
    for(var y=0; y<settings.forestSize; y++){
      if(population.sheep[x].xpos==forest.trees[y].posx && population.sheep[x].ypos==forest.trees[y].posy){
        forest.trees[y].posx=null;
        forest.trees[y].posy=null;
        population.sheep[x].fitness++;
      }
    }
  }

  //move eaters based on chromosome
  for(var x=0; x<settings.populationSize; x++){
    var move = Math.random();
    if(move<population.sheep[x].chromosome[0]){
      //up
      if(population.sheep[x].ypos>0){
        population.sheep[x].ypos-=1;
      }
    }
    else if(move-population.sheep[x].chromosome[0]<population.sheep[x].chromosome[1]){
      //down
      if(population.sheep[x].ypos<settings.rows-1){
        population.sheep[x].ypos+=1;
      }
    }
    else if(move-population.sheep[x].chromosome[0]-population.sheep[x].chromosome[1]<population.sheep[x].chromosome[2]){
      //right
      if(population.sheep[x].xpos<settings.cols-1){
        population.sheep[x].xpos+=1;
      }
    }
    else{
      //left
      if(population.sheep[x].xpos>0){
        population.sheep[x].xpos-=1;
      }
    }
  }
  counter++;
  endRun(counter);
}

function setup() {
  createCanvas(1000, 500);
}

function draw() {
  createWorld();
} 

我的想法是定义一个形状和该形状内的密度,然后为特定的随机坐标生成该形状。

例如:

    function radialTreePopulation(x,y,r,count){

          let trees = []
          for(let i = 0;i < count; i++){
            trees.push({
             posx : (x + (Math.random()* r * (Math.random() < 0.5 ? -1 : 1)))| 0,
             posy : (y + (Math.random()* r * (Math.random() < 0.5 ? -1 : 1)))| 0
            })
          }

      return trees
  }

比:

function Forest(f){
  let segmentCount = (Math.random() * 75)| 0;
  this.forSize = f ;
  this.trees = [];

  while(this.forSize){


    this.trees.push(...radialTreePopulation(
         (Math.random() *settings.cols)| 0,
         (Math.random() *settings.rows)| 0,
         (Math.random()*12) | 0 , // think squared > max segmentCount
         segmentCount)
         )

    segmentCount = (Math.random() * 100)| 0
    this.forSize -= this.forSize > segmentCount ?
                         segmentCount: this.forSize;

  }

}

片段:

var settings = {
      populationSize : 25,
      geneLength : 8,
      mutationProbability : 0.05,
      forestSize : 1500,
      rows : 124,
      cols : 249,
      year : 365,
    };

    function onCanvas(position){
      return position*4+2;
    }

    function randombetween(min, max){
      return Math.random()*max;
    }

    var population = new Population(settings.populationSize, settings.geneLength);

    function Sheep(g, dna){
      this.genLen = g;
      this.fitness=0;
      this.xpos = Math.floor(Math.random()*settings.cols);
      this.ypos = Math.floor(Math.random()*settings.rows);

      this.chromosome = new Array(this.genLen);
      if (dna != null){
        this.chromosome = dna;
      } else{
        for(var x=0; x<this.genLen; x+=4){
          this.chromosome[x] = Math.random();
          this.chromosome[x+1] = randombetween(0, 1-this.chromosome[x]);
          this.chromosome[x+2] = randombetween(0, 1-this.chromosome[x]-this.chromosome[x+1]);
          this.chromosome[x+3] = 1-this.chromosome[x]-this.chromosome[x+1]-this.chromosome[x+2];
        }
      }
    }

    function Population(p, g){
      this.popSize = p;
      this.sheep = [];

      for (var x = 0; x < this.popSize; x++) {
        this.sheep[x] = new Sheep(g, null);
      }
    }

    var forest = new Forest(settings.forestSize);
function radialTreePopulation(x,y,r,count){
    
      let trees = []
      for(let i = 0;i < count; i++){
        trees.push({
         posx : (x + (Math.random()* r * (Math.random() < 0.5 ? -1 : 1)))| 0,
         posy : (y + (Math.random()* r * (Math.random() < 0.5 ? -1 : 1)))| 0
        })
      }
      
      return trees
  }

    function Tree(){
      this.posx = Math.floor(Math.random()*settings.cols);
      this.posy = Math.floor(Math.random()*settings.rows);
    }

    
function Forest(f){
  let segmentCount = (Math.random() * 75)| 0;
  this.forSize = f ;
  this.trees = [];

  while(this.forSize){

    
    this.trees.push(...radialTreePopulation(
         (Math.random() *settings.cols)| 0,
         (Math.random() *settings.rows)| 0,
         (Math.random()*12) | 0 , // think squared > max segmentCount
         segmentCount)
         )
     
    segmentCount = (Math.random() * 75)| 0
    this.forSize -= this.forSize > segmentCount ?
                         segmentCount: this.forSize;
     
  }

}


    //begin generation count
    var generation=0;

    //begin day count
    var counter = 0;
    function endRun(end){
      if(end>=settings.year){
        noLoop();
      }
    }

    //create world
    function createWorld(){
      background("lightblue");

      fill(0,255,0);
      for(var x=0; x<settings.forestSize; x++){
        rect(onCanvas(forest.trees[x].posx), onCanvas(forest.trees[x].posy), 4, 4);
      }

      fill(255,0,0);
      for(var x=0; x<settings.populationSize; x++){
        rect(onCanvas(population.sheep[x].xpos), onCanvas(population.sheep[x].ypos), 4, 4);
      }

      //remove eaten trees
      for(var x=0; x<settings.populationSize; x++){
        for(var y=0; y<settings.forestSize; y++){
          if(population.sheep[x].xpos==forest.trees[y].posx && population.sheep[x].ypos==forest.trees[y].posy){
            forest.trees[y].posx=null;
            forest.trees[y].posy=null;
            population.sheep[x].fitness++;
          }
        }
      }

      //move eaters based on chromosome
      for(var x=0; x<settings.populationSize; x++){
        var move = Math.random();
        if(move<population.sheep[x].chromosome[0]){
          //up
          if(population.sheep[x].ypos>0){
            population.sheep[x].ypos-=1;
          }
        }
        else if(move-population.sheep[x].chromosome[0]<population.sheep[x].chromosome[1]){
          //down
          if(population.sheep[x].ypos<settings.rows-1){
            population.sheep[x].ypos+=1;
          }
        }
        else if(move-population.sheep[x].chromosome[0]-population.sheep[x].chromosome[1]<population.sheep[x].chromosome[2]){
          //right
          if(population.sheep[x].xpos<settings.cols-1){
            population.sheep[x].xpos+=1;
          }
        }
        else{
          //left
          if(population.sheep[x].xpos>0){
            population.sheep[x].xpos-=1;
          }
        }
      }
      counter++;
      endRun(counter);
    }

    function setup() {
      createCanvas(1000, 500);
    }

    function draw() {
      createWorld();
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>