Silverback9

#야생으로

Creative Coding 독학 제287일 2024년01월08일(수)

오늘은 화면 테두리에 닿는 로켓들을 정리하는 날이예요~^^*

공연 1작품 감상하고 계셔요~^^* 코딩 공부 정리해서 돌아올게요~^^* 쓩~^^*

화면 테두리를 정리하는 것은~^^* 장애물에 닿았을 때와 같은 방법으로 해 낼 수 있을 것 같아요~^^*

화면 테두리에 닿는 로켓의 this.crashed를 true로 바꾸고, 그러면 이 로켓의 fitness 적합성 수치가 1/10으로 줄어들게 되겠죠~^^*

그러면 다음 세대부터 점점 테두리에 닿는 로켓의 수도 줄어들게 될 것 같아요~^^*

function Rocket(dna) {
    .
    .
    .
  this.update = function() {
    if(this.pos.x > width || this.pos.x < 0) {
    this.crashed = true;
    }

    if(this.pos.y > height || this.pos.y < 0) {
    this.crashed = true;
    }
  }
   //화면 테두리에 부딪혔을 때도 this.crashed 값을 true로 설정하겠습니다.
    .
    .
    .
}

그럼 우리 테두리에도 닿지 않고 장애물도 잘 돌아서 목표물에 안착하는 로켓군단을 만나러 가 볼까요~~^^*

그럼 이제 우리는 Smart Rocket 똑똑한 로켓 프로그램을 완성한 것 같은데요^^* 전체 코드 한 번 다시 살펴 볼까요~~^^*

var lifespan = 200; //200 프레임동안 살아보겠습니다. 
var population;
var lifeP; //살아온 날 count 수치를 pharagraph 문단으로 보여줍니다. 
var count = 0; //살아온 날
var target; //드디어 우리에게 추구하고픈 목표가 생겼어요!!
var mutateR = 0.01; //돌연변이 발생 확률입니다. 
var maxforce = 0.2; //힘의 크기입니다. 

var rx = 150;
var ry = 300;
var rw = 100;
var rh = 10;
//장애물의 시작위치(150, 300), 너비 100, 높이 10입니다. 

function setup(){
  createCanvas(400, 400); 
  population = new Population();
  lifeP = createP();
  target = createVector(width/2, 100);
}

function draw(){
  background(0);
  population.run();
  lifeP.html(count); 
  
  count++;
  if (count == lifespan) {
    population.evaluate();
    population.selection();
    count = 0;
  }
  //200 frame이 되면 로켓들의 적합성을 기반으로 부모를 선정하여 유전자 교차 작업을 통해 다음세대 로켓을 생성합니다. 
  
  fill(255);
  rect(rx, ry, rw, rh);
  //흰색의 장애물을 만들어 보겠습니다. 
  
  ellipse(target.x, target.y, 16, 16);
  //우리의 target 목표물입니다. 
}


function DNA(genes) {

   if(genes) {
     this.genes = genes;
   } else {
     this.genes = [];
     for(var i = 0; i < lifespan; i++) {
       this.genes[i] = p5.Vector.random2D();
       this.genes[i].setMag(maxforce);
     }
   }
   
  this.crossover = function(partner) {
    var newgenes = [];
    var mid = floor(random(this.genes.length));
    for(i = 0; i < this.genes.length; i++) {
      if(i > mid) {
        newgenes[i] = this.genes[i];
      } else {
        newgenes[i] = partner.genes[i]
      }
    }
    return new DNA(newgenes);
  }
  
  this.mutation = function() {
    for(i = 0; i < this.genes.length; i++) {
      if(random(1) < mutateR) {
        this.genes[i] = p5.Vector.random2D();
        this.genes[i].setMag(maxforce);
      }
    }
  }
  //무작위 수를 뽑아 mutateR보다 작은 경우, 무작위 방향과 0.02 크기의 힘을 가진 벡터를 this.genes[i]에 저장하겠습니다. 
  
} 
//맨처음 세대는 무작위로 genes 데이터를 만들어 DNA를 생성하고, 다음 세대 DNA 생성을 할 때는 genes 정보를 전달 받아 생성합니다. 

  
function Rocket(dna) {
  this.pos = createVector(width/2, height);
  this.vel = createVector(); 
  this.acc = createVector();
  this.completed = false;
  this.crashed = false;
  //부딪혔나에 대한 상태 진리 변수입니다. 
  
  if (dna) {
    this.dna = dna;
  } else {
    this.dna = new DNA();
  }
  this.fitness = 0;

  this.count = 0; 
  //this.dna.genes[this.count]에 쓰이며, 배열의 순서를 나타내게 될 것입니다. 
  
  this.calcFitness = function() {
    var d = dist(this.pos.x, this.pos.y, target.x, target.y);
    this.fitness = map( d, 0, width*2, width*2, 0);
    if(this.completed) {
      this.fitness *= 10;
    }
    
    if(this.crashed) {
      this.fitness /= 10;
    } 
    //장애물에 부딪히면 로켓의 fitness 적합성 값을 10분의 1로 줄이겠습니다.    
  }
  
  this.applyForce = function(force) {
    this.acc.add(force);
  }
  
  this.update = function() {
    
    if(this.pos.x > rx && this.pos.x < rx + rw && this.pos.y > ry && this.pos.y < ry + rh) {
      this.crashed = true; 
    }
    //장애물에 부딪히면 this.crashed 값을 true로 설정하겠습니다. 
    
    if(this.pos.x > width || this.pos.x < 0) {
      this.crashed = true;
    }

    if(this.pos.y > height || this.pos.y < 0) {
      this.crashed = true;
      
    }    
    //화면 테두리에 부딪혔을 때도 this.crashed  값을 true로 설정하겠습니다. 
  
    var d = dist(this.pos.x, this.pos.y, target.x, target.y);
      if (d < 10) {
        this.completed = true;
        this.pos = target.copy();
      }
      //로켓과 목표물과의 거리가 <<10 이하>>가 되면 this.complete 진리값이 "true"가 되고, 로켓의 위치에 목표물의 위치값을 저장합니다. 
 
      this.applyForce(this.dna.genes[this.count]);
      //this.dna.genes[this.count]에 서로 다른 속도가 저장되어 있으므로
      //프레임마다 서로 다른 속도값이 힘에 적용되어 가속도에 더해집니다.  
      this.count++;    
 
      if(!this.completed && !this.crashed) {
      this.vel.add(this.acc);
      this.pos.add(this.vel);
      this.acc.mult(0);
      }
      //로켓과 목표물 사이 거리가 <<10보다 큰>> 경우에만 로켓은 계속 움직입니다. 
      //그리고, 로켓이 장애물에 닿지 않은 경우에만 로켓은 계속 움직입니다. 
    }

  this.show = function() {
    push();
    noStroke();
    fill(255, 150); 
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    rectMode(CENTER);
    rect(0, 0, 25, 5); 
    pop();
  } 
}
//첫 세대 로켓은 무작위 DNA로 다음 세대 로켓부터는 교차작업을 통해 만들어진 DNA 정보로 생성합니다. 
//목표물에 가장 가까운 로켓이 가장 큰 fitness값을 가질 수 있도록, 로켓과 목표물 사이의 거리 <0 - width*2>를 <width*2 - 0>으로 mapping합니다. 


function Population () {
  
  this.rockets = [];
  this.popsize = 200;
  this.matingpool = [];

  for (var i = 0; i < this.popsize; i++) {
    this.rockets[i] = new Rocket();
  }

  this.evaluate = function() {

    var maxfit = 0;
    for (var i = 0; i < this.popsize; i++) {
      this.rockets[i].calcFitness();
      if (this.rockets[i].fitness > maxfit) {
        maxfit = this.rockets[i].fitness;
      }
    }
    
    createP(maxfit);
    console.log(this.rockets);
    
    for (var i = 0; i < this.popsize; i++) {
      this.rockets[i].fitness /= maxfit;
    }

    this.matingpool = [];
    for (var i = 0; i < this.popsize; i++) {
      var n = this.rockets[i].fitness * 100;
      for (var j = 0; j < n; j++) {
        this.matingpool.push(this.rockets[i]);
      }
    }
  }
  //로켓들의 최고 fitness 값을 기반으로 각 로켓의 fitness 값을 <0-1>로 정규화하고, 이를 바탕으로 높은 fitness 값을 가진 로켓들을 후보자군에 더 많이 넣습니다. 
  
  this.selection = function() {
    var newRockets = [];
    for (var i = 0; i < this.rockets.length; i++) {
      var parentA = random(this.matingpool).dna;
      var parentB = random(this.matingpool).dna;
      var child = parentA.crossover(parentB);
      child.mutation();
      newRockets[i] = new Rocket(child);
    }
    this.rockets = newRockets;
  }  
  //후보자군에서 무작위로 부모를 선택하여 교차작업을 합니다.
  //교차작업을 통해 만들어진 child에 돌연변이생성 작업을 더한 후 자녀 로켓을 생성합니다. 로켓집합의 세대교체를 합니다. 
  
    
  this.run = function() {
    for (var i = 0; i < this.popsize; i++) {
      this.rockets[i].update();
      this.rockets[i].show();
    }
  }
}  
//로켓의 움직임 변화를 반영하여 시각화합니다. 

와우!!! 우리가 genetic algorithm 공부를 위해, 이미 완성된 프로그램들을 분석하는 활동에 이어, 이번에 처음으로 프로그램 하나를 처음부터 끝까지 코드 작업을 직접 해보는 활동을 해내었네요~~^^*

코드 분석 공부를 통해 genetic algorithm의 원리와 절차에 익숙해 지고 나서 도전한 코딩 작업이라서 복습의 느낌도 좀 있었던 것 같아요~~^^*

어제보다 나은 오늘의 나를 향해 가는, 새로운 부분이 많아서 도전적이긴한데 익숙한 부분도 많아서 편안하기도 한 첫 코딩작업 활동인 것 같기도 해요~~^^*

몰입의 물결 위를 편안한 긴장감으로 서핑하는 느낌~~^^*

genetic algorithm 첫 코딩 작업 완성~~~^^* Smart Rocket~~^^*

34:50 ~ 35:20 사이의 강의 내용을 보시면, fitness 적합성 수치가 마이너스 값을 가지는 로켓이 나오는 것을 발견할 수 있는데요. 이 강의는 width를 기준으로 mapping을 해서 그런 것 같아요. 우리는, 이미 width*2를 기준으로 mapping을 해서, fitness 적합성 수치가 마이너스 값이 되는 것을 예방할 수 있었던 것 같습니다.

우리가 고민했던 부분이 고민해 볼 만한 부분이었다는 것이 느껴져서 보람있기도 하네요^^*

오늘 저와 함께 Smart Rocket 똑똑한 로켓을 완성해 주셔서 감사합니다~~^^*

우리 내일도 만나서~~^^* 또다른 코딩 도전을 이어나가 볼까요~~^^*

도레미를 탄탄히 다지면 아름답고 재미있는 노래도 만들어 부를 수 있는 것처럼~^^*

기초탄탄 계속 가 볼까요~~^^*

공기의 색깔을 눈으로 귀로 피부로 느낄 수 있는 프로그램을 만들어 낼 수 있을 때까지 계속 우리 함께 가보는 것인가요~~^^* 그러면 참 좋겠어요~~^^*

실패할지라도 포기하지는 않는 도전정신으로 계속 가 보면 좋겠어요~~^^*

오늘도 점심 맛있게 드시고요!

보람찬 하루를 보내시고요!

편안한 저녁 따뜻한 밤 누리시고요!

우리 내일 다시 또 만나는 거예요~~^^* YEAH~~^^*

네~~^^* 꿈은 이루어 집니다~~^^*

댓글 남기기