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>
问题
我正在尝试制作模仿森林鸟瞰图的图形,因此我需要在 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>