๐ŸŽ“10์ฃผ์ฐจ: ์ง„ํ™”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜ (์œ ์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜)

BamgasiJMยท2025๋…„ 11์›” 6์ผ

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
21/67
post-thumbnail

"์ƒ๋ฌผ์˜ ์ง„ํ™” ์›๋ฆฌ(๋ณ€์ด, ์„ ํƒ, ๊ต๋ฐฐ)๋ฅผ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด์„œ, ์ปดํ“จํ„ฐ๊ฐ€ ์Šค์Šค๋กœ '์ข‹์€ ํ•ด๋‹ต'์„ ์ฐพ์•„๊ฐ€๊ฒŒ ๋งŒ๋“œ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜"์ด์—์š”.
์ด๊ฒƒ์€ ์ตœ์ ํ™” ๋ฌธ์ œ, ์ƒ์„ฑ ๋””์ž์ธ, ์ธ๊ณต์ƒ๋ช… ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋“ฑ์—์„œ ์ž์—ฐ์˜ ์ง„ํ™”๋ฅผ ๋ชจ๋ฐฉํ•˜๋Š” ํ•ต์‹ฌ ์›๋ฆฌ์ž…๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ๋‚ด์šฉ
์ด๋ฆ„Genetic Algorithm (GA)
์˜๋ฏธ์ƒ๋ฌผํ•™์  ์ง„ํ™”(์œ ์ „, ๋Œ์—ฐ๋ณ€์ด, ์ž์—ฐ์„ ํƒ)๋ฅผ ๋ชจ๋ฐฉํ•œ ์ตœ์ ํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜
ํ•ต์‹ฌ ์•„์ด๋””์–ด์—ฌ๋Ÿฌ ๊ฐœ์ฒด๊ฐ€ ๊ฒฝ์Ÿํ•˜๊ณ , ์šฐ์ˆ˜ํ•œ ๊ฐœ์ฒด๋ผ๋ฆฌ ๊ต๋ฐฐ(crossover)ํ•˜๊ณ  ๋ณ€์ด(mutation)๋ฅผ ๊ฑฐ์ณ ์„ธ๋Œ€๋ฅผ ๊ฑฐ๋“ญํ•˜๋ฉฐ ์ง„ํ™”
๊ฒฐ๊ณผ ํŒจํ„ด์„ธ๋Œ€๊ฐ€ ์ง€๋‚ ์ˆ˜๋ก ๋ชฉํ‘œ์— ๊ฐ€๊นŒ์›Œ์ง€๋Š” ๊ฐœ์ฒด ์ง‘๋‹จ์˜ '์ ์‘'
๊ด€๋ จ ๊ฐœ๋…์ž์—ฐ์„ ํƒ(natural selection), ์ ํ•ฉ๋„(fitness), ๊ต๋ฐฐ(crossover), ๋Œ์—ฐ๋ณ€์ด(mutation)

๐Ÿ“– ์œ ์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ธฐ๋ณธ ์›๋ฆฌ

1๏ธโƒฃ ์ดˆ๊ธฐ ์ง‘๋‹จ(Population) ์ƒ์„ฑ

โ†’ ๋ฌด์ž‘์œ„๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์ฒด(individual)๋ฅผ ๋งŒ๋“ฆ
(์˜ˆ: ๋ชฉํ‘œ ๋ฌธ์žฅ์„ ์ฐพ๋Š” ๊ฒฝ์šฐ, ๋žœ๋คํ•œ ๋ฌธ์ž์—ด์„ ์—ฌ๋Ÿฌ ๊ฐœ ์ƒ์„ฑ)

2๏ธโƒฃ ์ ํ•ฉ๋„(Fitness) ํ‰๊ฐ€

โ†’ ๊ฐ ๊ฐœ์ฒด๊ฐ€ "๋ชฉํ‘œ์— ์–ผ๋งˆ๋‚˜ ๊ฐ€๊นŒ์šด์ง€" ์ ์ˆ˜๋ฅผ ๋งค๊น€
(์˜ˆ: ๋ชฉํ‘œ ๋ฌธ์žฅ๊ณผ ๋ช‡ ๊ธ€์ž๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€)

3๏ธโƒฃ ์„ ํƒ(Selection)

โ†’ ์ ํ•ฉ๋„๊ฐ€ ๋†’์€ ๊ฐœ์ฒด๋ฅผ ๋” ๋งŽ์ด ์„ ํƒํ•จ
โ†’ ์ž์—ฐ์„ ํƒ(survival of the fittest)์„ ๋ชจ๋ฐฉ

4๏ธโƒฃ ๊ต๋ฐฐ(Crossover)

โ†’ ์„ ํƒ๋œ ๋‘ ๋ถ€๋ชจ์˜ ์œ ์ „์ž๋ฅผ ์„ž์–ด ์ž์‹์„ ๋งŒ๋“ฆ
(์˜ˆ: ๋ถ€๋ชจ A์˜ ์•ž๋ถ€๋ถ„ + ๋ถ€๋ชจ B์˜ ๋’ท๋ถ€๋ถ„)

5๏ธโƒฃ ๋Œ์—ฐ๋ณ€์ด(Mutation)

โ†’ ์ผ์ • ํ™•๋ฅ ๋กœ ์ž์‹์˜ ์œ ์ „์ž ์ผ๋ถ€๋ฅผ ๋žœ๋คํ•˜๊ฒŒ ๋ฐ”๊ฟˆ
โ†’ ๋‹ค์–‘์„ฑ์„ ์œ ์ง€ํ•˜๊ณ  ์ง€์—ญ ์ตœ์ ํ•ด(local optimum)๋ฅผ ํƒˆ์ถœ

6๏ธโƒฃ ๋‹ค์Œ ์„ธ๋Œ€๋กœ ๋Œ€์ฒด

โ†’ ์ƒˆ๋กœ์šด ์ง‘๋‹จ์œผ๋กœ ๊ต์ฒดํ•˜๊ณ  2๏ธโƒฃ๋กœ ๋Œ์•„๊ฐ
โ†’ ๋ชฉํ‘œ์— ๋„๋‹ฌํ•˜๊ฑฐ๋‚˜ ์ผ์ • ์„ธ๋Œ€์ˆ˜๊นŒ์ง€ ๋ฐ˜๋ณต


๐Ÿ”— ์ž์—ฐ ํ˜„์ƒ๊ณผ์˜ ์—ฐ๊ฒฐ

๐Ÿงฌ ์ƒ๋ฌผ ์ง„ํ™”๊ฐˆ๋ผํŒŒ๊ณ ์Šค ํ•€์น˜์ƒˆ๊ฐ€ ๋ถ€๋ฆฌ ๋ชจ์–‘์„ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์ง„ํ™”
๐ŸŒฑ ์‹๋ฌผ ์ ์‘์„ ์ธ์žฅ์ด ๊ฑด์กฐํ•œ ํ™˜๊ฒฝ์— ์ ์‘ํ•œ ํ˜•ํƒœ๋กœ ์ง„ํ™”
๐Ÿฆ‹ ๊ณค์ถฉ์˜ ์˜ํƒœ๋‚˜๋ญ‡์žŽ์ฒ˜๋Ÿผ ๋ณด์ด๋„๋ก ์ง„ํ™”ํ•œ ๋‚˜๋ฐฉ
๐Ÿ† ํฌ์‹์ž-ํ”ผ์‹์ž์น˜ํƒ€์˜ ์†๋„์™€ ๊ฐ€์ ค์˜ ๋ฏผ์ฒฉ์„ฑ์ด ์„œ๋กœ ๊ฒฝ์Ÿํ•˜๋ฉฐ ์ง„ํ™”

๐Ÿ“Š ๋‹ค๋ฅธ ์ฃผ์ œ์™€์˜ ์—ฐ๊ฒฐ

์ฃผ์ œ๊ด€๊ณ„
๋žœ๋ค(Randomness)๋Œ์—ฐ๋ณ€์ด์™€ ์ดˆ๊ธฐ ์ง‘๋‹จ ์ƒ์„ฑ์˜ ๊ธฐ๋ฐ˜
์‹œ์Šคํ…œ(Systems)๊ฐœ์ฒด๋“ค์˜ ์ง‘๋‹จ์  ์ƒํ˜ธ์ž‘์šฉ์œผ๋กœ ๋ณต์žก์„ฑ ์ƒ์„ฑ
ํ˜•ํƒœ & ๊ตฌ์กฐ(Form & Structure)์ง„ํ™”๋ฅผ ํ†ตํ•ด ํ˜•ํƒœ๊ฐ€ ์ตœ์ ํ™”๋จ
๋ฐ์ดํ„ฐ(Data)์œ ์ „์ž๋ฅผ ๋ฐ์ดํ„ฐ๋กœ ํ‘œํ˜„ํ•˜๊ณ  ์กฐ์ž‘

๐Ÿ’ป ์˜ˆ์ œ ์ฝ”๋“œ

๐Ÿ”น ์˜ˆ์ œ 1: ๊ธฐ๋ณธ ์œ ์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (๋ฌธ์ž์—ด ์ง„ํ™”)

let population = [];
let target = "BamgasiJM";
let mutationRate = 0.01;
let popSize = 200;

function setup() {
  createCanvas(600, 250);
  // ์ดˆ๊ธฐ ์ง‘๋‹จ ์ƒ์„ฑ
  for (let i = 0; i < popSize; i++) {
    population.push(new DNA(target.length));
  }
}

function draw() {
  background(15);

  // ์ ํ•ฉ๋„ ๊ณ„์‚ฐ
  for (let dna of population) {
    dna.calcFitness(target);
  }

  // ๋‹ค์Œ ์„ธ๋Œ€ ์ƒ์„ฑ
  let matingPool = [];
  for (let dna of population) {
    let n = floor(dna.fitness * 100);
    for (let i = 0; i < n; i++) {
      matingPool.push(dna);
    }
  }

  // ๊ต๋ฐฐ ๋ฐ ๋Œ์—ฐ๋ณ€์ด
  for (let i = 0; i < population.length; i++) {
    let parentA = random(matingPool);
    let parentB = random(matingPool);
    let child = parentA.crossover(parentB);
    child.mutate(mutationRate);
    population[i] = child;
  }

  // ์ตœ๊ณ  ๊ฐœ์ฒด ํ‘œ์‹œ
  let best = population[0];
  for (let dna of population) {
    if (dna.fitness > best.fitness) {
      best = dna;
    }
  }

  fill(210, 180, 0);
  textSize(32);
  textAlign(CENTER);
  text("Target: " + target, width / 2, 70);
  fill(30, 200, 190);
  textSize(32);
  textAlign(CENTER);
  text("Best: " + best.genes, width / 2, 120);
  fill(200);
  textSize(18);
  textAlign(CENTER);
  text("Generation: " + frameCount, width / 2, 180);
  text("Fitness: " + nf(best.fitness, 0, 4), width / 2, 210);

  // ๋ชฉํ‘œ ๋‹ฌ์„ฑ ์‹œ ์ •์ง€
  if (best.genes === target) {
    noLoop();
    console.log("Completed! Number of Generations: " + frameCount);
  }
}

class DNA {
  constructor(len) {
    this.genes = "";
    this.fitness = 0;
    for (let i = 0; i < len; i++) {
      this.genes += this.randomChar();
    }
  }

  randomChar() {
    let c = floor(random(32, 128));
    return String.fromCharCode(c);
  }

  calcFitness(target) {
    let score = 0;
    for (let i = 0; i < this.genes.length; i++) {
      if (this.genes[i] === target[i]) {
        score++;
      }
    }
    this.fitness = score / target.length;
  }

  crossover(partner) {
    let child = new DNA(this.genes.length);
    let midpoint = floor(random(this.genes.length));
    child.genes =
      this.genes.substring(0, midpoint) + partner.genes.substring(midpoint);
    return child;
  }

  mutate(rate) {
    let genes = "";
    for (let i = 0; i < this.genes.length; i++) {
      if (random(1) < rate) {
        genes += this.randomChar();
      } else {
        genes += this.genes[i];
      }
    }
    this.genes = genes;
  }
}

๐Ÿ”น ์˜ˆ์ œ 2: ํƒ€๊ฒŸ์„ ํ–ฅํ•œ ๋กœ์ผ“ ์ง„ํ™”

let population = [];
let popSize = 50;
let target;
let lifespan = 300;
let lifeCounter = 0;
let generation = 1;

function setup() {
  createCanvas(600, 600);
  target = createVector(width / 2, 50);
  
  for (let i = 0; i < popSize; i++) {
    population.push(new Rocket());
  }
}

function draw() {
  background(0);
  
  // ๋ชฉํ‘œ ์ง€์ 
  fill(255, 0, 0);
  circle(target.x, target.y, 20);
  
  // ๋กœ์ผ“ ์—…๋ฐ์ดํŠธ
  for (let rocket of population) {
    rocket.update();
    rocket.show();
  }
  
  lifeCounter++;
  
  // ์„ธ๋Œ€ ๊ต์ฒด
  if (lifeCounter >= lifespan) {
    lifeCounter = 0;
    evaluate();
    selection();
    generation++;
  }
  
  // ์ •๋ณด ํ‘œ์‹œ
  fill(255);
  textAlign(LEFT);
  text("Generation: " + generation, 10, 20);
  text("Lifespan: " + lifeCounter + "/" + lifespan, 10, 40);
}

function evaluate() {
  let maxFit = 0;
  for (let rocket of population) {
    rocket.calcFitness(target);
    if (rocket.fitness > maxFit) {
      maxFit = rocket.fitness;
    }
  }
  
  // ์ •๊ทœํ™”
  for (let rocket of population) {
    rocket.fitness /= maxFit;
  }
}

function selection() {
  let matingPool = [];
  
  for (let rocket of population) {
    let n = floor(rocket.fitness * 100);
    for (let i = 0; i < n; i++) {
      matingPool.push(rocket);
    }
  }
  
  let newPopulation = [];
  for (let i = 0; i < popSize; i++) {
    let parentA = random(matingPool);
    let parentB = random(matingPool);
    let child = parentA.crossover(parentB);
    child.mutate();
    newPopulation.push(child);
  }
  
  population = newPopulation;
  lifeCounter = 0;
}

class Rocket {
  constructor(dna) {
    this.pos = createVector(width / 2, height - 50);
    this.vel = createVector();
    this.acc = createVector();
    this.completed = false;
    this.crashed = false;
    this.fitness = 0;
    
    if (dna) {
      this.dna = dna;
    } else {
      this.dna = [];
      for (let i = 0; i < lifespan; i++) {
        this.dna.push(p5.Vector.random2D().setMag(0.1));
      }
    }
  }
  
  update() {
    if (!this.completed && !this.crashed) {
      this.applyForce(this.dna[lifeCounter]);
      this.vel.add(this.acc);
      this.pos.add(this.vel);
      this.acc.mult(0);
      
      // ์ถฉ๋Œ ๊ฐ์ง€
      if (this.pos.x < 0 || this.pos.x > width || 
          this.pos.y < 0 || this.pos.y > height) {
        this.crashed = true;
      }
      
      // ๋ชฉํ‘œ ๋„๋‹ฌ
      if (dist(this.pos.x, this.pos.y, target.x, target.y) < 20) {
        this.completed = true;
      }
    }
  }
  
  applyForce(force) {
    this.acc.add(force);
  }
  
  show() {
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    
    if (this.completed) {
      fill(0, 255, 0);
    } else if (this.crashed) {
      fill(255, 0, 0);
    } else {
      fill(255);
    }
    
    rectMode(CENTER);
    rect(0, 0, 20, 5);
    pop();
  }
  
  calcFitness(target) {
    let d = dist(this.pos.x, this.pos.y, target.x, target.y);
    this.fitness = map(d, 0, width, width, 0);
    
    if (this.completed) {
      this.fitness *= 10;
    }
    if (this.crashed) {
      this.fitness /= 10;
    }
  }
  
  crossover(partner) {
    let newDNA = [];
    let midpoint = floor(random(this.dna.length));
    
    for (let i = 0; i < this.dna.length; i++) {
      if (i < midpoint) {
        newDNA.push(this.dna[i]);
      } else {
        newDNA.push(partner.dna[i]);
      }
    }
    
    return new Rocket(newDNA);
  }
  
  mutate() {
    let mutationRate = 0.01;
    for (let i = 0; i < this.dna.length; i++) {
      if (random(1) < mutationRate) {
        this.dna[i] = p5.Vector.random2D().setMag(0.1);
      }
    }
  }
}

๐Ÿ”น ์˜ˆ์ œ 3: ์› ํšŒํ”ผ ๋กœ์ผ“ ์ง„ํ™”

let population = [];
let popSize = 50;
let target;
let obstacles = [];
let lifespan = 300;
let lifeCounter = 0;
let generation = 1;

function setup() {
  createCanvas(600, 600);
  target = createVector(width / 2, 50);
  
  // ์žฅ์• ๋ฌผ ์ƒ์„ฑ
  obstacles.push(createVector(width / 2, height / 2));
  obstacles.push(createVector(width / 2 - 100, height / 2 + 100));
  obstacles.push(createVector(width / 2 + 100, height / 2 + 100));
  
  for (let i = 0; i < popSize; i++) {
    population.push(new Rocket());
  }
}

function draw() {
  background(0);
  
  // ๋ชฉํ‘œ ์ง€์ 
  fill(0, 255, 0);
  circle(target.x, target.y, 20);
  
  // ์žฅ์• ๋ฌผ
  fill(255, 0, 0);
  for (let obs of obstacles) {
    circle(obs.x, obs.y, 40);
  }
  
  // ๋กœ์ผ“ ์—…๋ฐ์ดํŠธ
  for (let rocket of population) {
    // ์žฅ์• ๋ฌผ ์ถฉ๋Œ ์ฒดํฌ
    for (let obs of obstacles) {
      if (dist(rocket.pos.x, rocket.pos.y, obs.x, obs.y) < 25) {
        rocket.crashed = true;
      }
    }
    
    rocket.update();
    rocket.show();
  }
  
  lifeCounter++;
  
  if (lifeCounter >= lifespan) {
    lifeCounter = 0;
    evaluate();
    selection();
    generation++;
  }
  
  fill(255);
  textAlign(LEFT);
  text("Generation: " + generation, 10, 20);
  text("Lifespan: " + lifeCounter + "/" + lifespan, 10, 40);
}

function evaluate() {
  let maxFit = 0;
  for (let rocket of population) {
    rocket.calcFitness(target);
    if (rocket.fitness > maxFit) {
      maxFit = rocket.fitness;
    }
  }
  
  for (let rocket of population) {
    rocket.fitness /= maxFit;
  }
}

function selection() {
  let matingPool = [];
  
  for (let rocket of population) {
    let n = floor(rocket.fitness * 100);
    for (let i = 0; i < n; i++) {
      matingPool.push(rocket);
    }
  }
  
  let newPopulation = [];
  for (let i = 0; i < popSize; i++) {
    let parentA = random(matingPool);
    let parentB = random(matingPool);
    let child = parentA.crossover(parentB);
    child.mutate();
    newPopulation.push(child);
  }
  
  population = newPopulation;
  lifeCounter = 0;
}

class Rocket {
  constructor(dna) {
    this.pos = createVector(width / 2, height - 50);
    this.vel = createVector();
    this.acc = createVector();
    this.completed = false;
    this.crashed = false;
    this.fitness = 0;
    
    if (dna) {
      this.dna = dna;
    } else {
      this.dna = [];
      for (let i = 0; i < lifespan; i++) {
        this.dna.push(p5.Vector.random2D().setMag(0.1));
      }
    }
  }
  
  update() {
    if (!this.completed && !this.crashed) {
      this.applyForce(this.dna[lifeCounter]);
      this.vel.add(this.acc);
      this.pos.add(this.vel);
      this.acc.mult(0);
      
      if (this.pos.x < 0 || this.pos.x > width || 
          this.pos.y < 0 || this.pos.y > height) {
        this.crashed = true;
      }
      
      if (dist(this.pos.x, this.pos.y, target.x, target.y) < 20) {
        this.completed = true;
      }
    }
  }
  
  applyForce(force) {
    this.acc.add(force);
  }
  
  show() {
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    
    if (this.completed) {
      fill(0, 255, 0);
    } else if (this.crashed) {
      fill(255, 0, 0, 50);
    } else {
      fill(255, 150);
    }
    
    rectMode(CENTER);
    rect(0, 0, 20, 5);
    pop();
  }
  
  calcFitness(target) {
    let d = dist(this.pos.x, this.pos.y, target.x, target.y);
    this.fitness = map(d, 0, width, width, 0);
    
    if (this.completed) {
      this.fitness *= 10;
    }
    if (this.crashed) {
      this.fitness /= 10;
    }
  }
  
  crossover(partner) {
    let newDNA = [];
    let midpoint = floor(random(this.dna.length));
    
    for (let i = 0; i < this.dna.length; i++) {
      if (i < midpoint) {
        newDNA.push(this.dna[i]);
      } else {
        newDNA.push(partner.dna[i]);
      }
    }
    
    return new Rocket(newDNA);
  }
  
  mutate() {
    let mutationRate = 0.01;
    for (let i = 0; i < this.dna.length; i++) {
      if (random(1) < mutationRate) {
        this.dna[i] = p5.Vector.random2D().setMag(0.1);
      }
    }
  }
}

๐Ÿ”น ์˜ˆ์ œ 4: ํ˜•ํƒœ ์ง„ํ™” (์ƒ‰์ƒ + ํฌ๊ธฐ ์ตœ์ ํ™”)

let population = [];
let popSize = 100;
let targetColor;
let targetSize = 50;
let generation = 1;

function setup() {
  createCanvas(600, 600);
  targetColor = color(255, 100, 150); // ๋ชฉํ‘œ ์ƒ‰์ƒ
  
  for (let i = 0; i < popSize; i++) {
    population.push(new Circle());
  }
}

function draw() {
  background(0);
  
  // ๋ชฉํ‘œ ํ‘œ์‹œ
  fill(targetColor);
  circle(width / 2, 100, targetSize);
  fill(255);
  textAlign(CENTER);
  text("Target", width / 2, 150);
  
  // ํ˜„์žฌ ์„ธ๋Œ€ ํ‘œ์‹œ
  for (let i = 0; i < population.length; i++) {
    let x = (i % 10) * 60 + 30;
    let y = floor(i / 10) * 60 + 200;
    population[i].show(x, y);
  }
  
  // ์ •๋ณด
  fill(255);
  textAlign(LEFT);
  text("Generation: " + generation, 10, 20);
  
  // ํ•œ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ง„ํ™”
  if (frameCount % 60 === 0) {
    evolve();
    generation++;
  }
}

function evolve() {
  // ์ ํ•ฉ๋„ ๊ณ„์‚ฐ
  for (let circle of population) {
    circle.calcFitness(targetColor, targetSize);
  }
  
  // ์ •๊ทœํ™”
  let maxFit = 0;
  for (let circle of population) {
    if (circle.fitness > maxFit) {
      maxFit = circle.fitness;
    }
  }
  for (let circle of population) {
    circle.fitness /= maxFit;
  }
  
  // ์„ ํƒ
  let matingPool = [];
  for (let circle of population) {
    let n = floor(circle.fitness * 100);
    for (let i = 0; i < n; i++) {
      matingPool.push(circle);
    }
  }
  
  // ๊ต๋ฐฐ ๋ฐ ๋Œ์—ฐ๋ณ€์ด
  let newPopulation = [];
  for (let i = 0; i < popSize; i++) {
    let parentA = random(matingPool);
    let parentB = random(matingPool);
    let child = parentA.crossover(parentB);
    child.mutate();
    newPopulation.push(child);
  }
  
  population = newPopulation;
}

class Circle {
  constructor(r, g, b, size) {
    if (r === undefined) {
      this.r = random(255);
      this.g = random(255);
      this.b = random(255);
      this.size = random(10, 80);
    } else {
      this.r = r;
      this.g = g;
      this.b = b;
      this.size = size;
    }
    this.fitness = 0;
  }
  
  show(x, y) {
    fill(this.r, this.g, this.b);
    circle(x, y, this.size);
  }
  
  calcFitness(targetCol, targetSize) {
    let dr = abs(this.r - red(targetCol));
    let dg = abs(this.g - green(targetCol));
    let db = abs(this.b - blue(targetCol));
    let colorDist = (dr + dg + db) / 3;
    
    let sizeDist = abs(this.size - targetSize);
    
    let totalDist = colorDist + sizeDist;
    this.fitness = map(totalDist, 0, 255 + 80, 255 + 80, 0);
  }
  
  crossover(partner) {
    let childR = random(1) < 0.5 ? this.r : partner.r;
    let childG = random(1) < 0.5 ? this.g : partner.g;
    let childB = random(1) < 0.5 ? this.b : partner.b;
    let childSize = random(1) < 0.5 ? this.size : partner.size;
    
    return new Circle(childR, childG, childB, childSize);
  }
  
  mutate() {
    let mutationRate = 0.05;
    
    if (random(1) < mutationRate) {
      this.r += random(-20, 20);
      this.r = constrain(this.r, 0, 255);
    }
    if (random(1) < mutationRate) {
      this.g += random(-20, 20);
      this.g = constrain(this.g, 0, 255);
    }
    if (random(1) < mutationRate) {
      this.b += random(-20, 20);
      this.b = constrain(this.b, 0, 255);
    }
    if (random(1) < mutationRate) {
      this.size += random(-5, 5);
      this.size = constrain(this.size, 10, 80);
    }
  }
}

๐Ÿ”‘ ํ•ต์‹ฌ ๊ฐœ๋… ์ •๋ฆฌ

์œ ์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ 4๋Œ€ ์š”์†Œ

  1. ์ง‘๋‹จ(Population): ์—ฌ๋Ÿฌ ๊ฐœ์ฒด๋ฅผ ๋™์‹œ์— ํ‰๊ฐ€
  2. ์ ํ•ฉ๋„(Fitness): ๋ชฉํ‘œ์— ๋Œ€ํ•œ ํ‰๊ฐ€ ๊ธฐ์ค€
  3. ์„ ํƒ(Selection): ์šฐ์ˆ˜ํ•œ ๊ฐœ์ฒด๋ฅผ ์„ ํ˜ธ
  4. ๋ณ€์ด(Variation): ๊ต๋ฐฐ์™€ ๋Œ์—ฐ๋ณ€์ด๋กœ ๋‹ค์–‘์„ฑ ์œ ์ง€

์žฅ์ 

  • ๋ณต์žกํ•œ ์ตœ์ ํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ
  • ์ง€์—ญ ์ตœ์ ํ•ด์— ๊ฐ‡ํžˆ์ง€ ์•Š์Œ (๋Œ์—ฐ๋ณ€์ด ๋•๋ถ„)
  • ๋ณ‘๋ ฌ ํƒ์ƒ‰์œผ๋กœ ํšจ์œจ์ 

ํ™œ์šฉ ๋ถ„์•ผ

  • ๊ฒŒ์ž„ AI (์บ๋ฆญํ„ฐ ํ–‰๋™ ํ•™์Šต)
  • ๊ฑด์ถ•/๋””์ž์ธ ์ตœ์ ํ™”
  • ๊ฒฝ๋กœ ํƒ์ƒ‰
  • ์Šค์ผ€์ค„๋ง
  • ์ƒ์„ฑ ์•„ํŠธ

๐ŸŽจ ์‹คํ—˜ ์•„์ด๋””์–ด

  • ๋Œ์—ฐ๋ณ€์ด์œจ์„ ์กฐ์ •ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ๊ด€์ฐฐํ•ด๋ณด์„ธ์š”
  • ์ ํ•ฉ๋„ ํ•จ์ˆ˜๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์„ค๊ณ„ํ•˜๋ฉด ์–ด๋–ค ํ˜•ํƒœ๊ฐ€ ๋‚˜์˜ฌ๊นŒ์š”?
  • ์—ฌ๋Ÿฌ ๋ชฉํ‘œ๋ฅผ ๋™์‹œ์— ๋งŒ์กฑํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ์ง„ํ™”์‹œ์ผœ๋ณด์„ธ์š”
  • ๊ต๋ฐฐ ๋ฐฉ์‹(1์  ๊ต๋ฐฐ, 2์  ๊ต๋ฐฐ, ๊ท ๋“ฑ ๊ต๋ฐฐ)์„ ๋ฐ”๊ฟ”๋ณด์„ธ์š”
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€