진화 알고리즘, Node.js에서 실시간 구현

발행: (2025년 12월 17일 오후 10:00 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

위에 제공된 링크에 있는 전체 텍스트를 번역하려면 해당 글의 내용을 복사해서 여기 붙여 주세요.
링크 자체와 마크다운 형식, 코드 블록, URL 등은 그대로 유지하고, 본문만 한국어로 번역해 드릴 수 있습니다.

소개

강화 학습, 진화 알고리즘, 그리고 컴퓨터가 볼 수 있게 하는 모든 것은 가장 시각적으로 만족스러운 문제 중 하나입니다. 실시간으로 무언가가 학습하는 모습을 보는 것은 결코 질리지 않죠.

이 글은 사실 제가 Node.js용으로 만든 작은 그래픽 렌더러 **tessera.js**를 자랑하기 위한 변명에 불과합니다.

그리고 솔직히 말해서 진화 알고리즘의 hello world라 할 수 있는 데모보다 더 좋은 예시를 생각할 수 없습니다: 무작위 색상이 변이하면서 서서히 목표 색상으로 진화하는 모습 말이죠.

evo algorithm

그래픽 프로그래밍이 처음이라면 걱정하지 마세요—디지털 세계에서 시각적인 모든 것의 단위는 놀라울 정도로 간단합니다. 여기서 그 내용을 다룹니다:

Graphics 101: A Node.js Renderer

타깃 색 찾기

tessera 설치

npm i tessera.js@latest

렌더러 시작

import { loadRenderer, PixelBuffer, ShapeDrawer } from "tessera.js";

const { Renderer } = loadRenderer();
const renderer = new Renderer();

if (!renderer.initialize(1200, 800, "Evolution Algos Hello World")) {
  console.error("failed to init renderer");
  process.exit(1);
}

renderer.targetFPS = 60;
const canvas = new PixelBuffer(renderer, 1200, 800);

매개변수와 유전체 (“종”)

// Target color we're evolving toward
const TARGET = { r: 120, g: 80, b: 150 };

/**
 * Genome = full DNA
 * Genotype = specific gene values
 * Phenotype = what we actually see
 *
 * Here: genome is just RGB.
 */
class Genome {
  constructor(r = null, g = null, b = null) {
    this.r = r ?? Math.floor(Math.random() * 256);
    this.g = g ?? Math.floor(Math.random() * 256);
    this.b = b ?? Math.floor(Math.random() * 256);
    this.fitness = 0;
  }
}

const MUTATION_RATE = 0.3;   // more chaos
const ELITE_COUNT   = 5;     // more stability
const POP_SIZE      = 150;   // slower but more thorough

let population = Array.from({ length: POP_SIZE }, () => new Genome());
let generation = 0;
let bestEver   = null;

핵심 진화 알고리즘

// Fitness: lower distance = better
function evaluateFitness(genome) {
  const dr = genome.r - TARGET.r;
  const dg = genome.g - TARGET.g;
  const db = genome.b - TARGET.b;
  genome.fitness = Math.sqrt(dr * dr + dg * dg + db * db);
}

// Tournament selection
function selectParent(pop) {
  const tournamentSize = 3;
  let best = pop[Math.floor(Math.random() * pop.length)];
  for (let i = 1; i  a.fitness - b.fitness);

  if (!bestEver || population[0].fitness  {
    const x = gridX + (i % cols) * (cellSize + 5);
    const y = gridY + Math.floor(i / cols) * (cellSize + 5);
    ShapeDrawer.fillRect(canvas, x, y, cellSize, cellSize,
      g.r, g.g, g.b, 255);
  });

  canvas.upload();
}
renderer.onRender(() => {
  canvas.draw(0, 0);

  renderer.drawText("TARGET",    { x: 50,  y: 30 }, 16, { r: 1, g: 1, b: 1, a: 1 });
  renderer.drawText("BEST NOW", { x: 250, y: 30 }, 16, { r: 1, g: 1, b: 1, a: 1 });
  renderer.drawText("BEST EVER",{ x: 450, y: 30 }, 16, { r: 1, g: 1, b: 1, a: 1 });

  const best = population[0];
  // ... (additional rendering logic can go here)
});
renderer.drawText(
  `Gen: ${generation} | Fitness: ${best.fitness.toFixed(2)} | RGB: (${best.r}, ${best.g}, ${best.b})`,
  { x: 50, y: 650 },
  16,
  { r: 1, g: 1, b: 1, a: 1 }
);

루프

let frameCount = 0;
const FRAME_DELAY = 60;

evolve();

function Loop() {
  frameCount++;
  renderer.input.GetInput();

  if (frameCount % FRAME_DELAY === 0) {
    evolve();
  }

  render();

  if (renderer.step()) {
    setImmediate(Loop);
  } else {
    renderer.shutdown();
  }
}

Loop();

~100줄(보일러플레이트 제외) 정도로, Node.js에서 완전하게 애니메이션된 진화 알고리즘을 실행하고 있습니다.

그래픽 프로그래밍에 관심이 있다면, 더 많은 콘텐츠가 곧 올라올 예정입니다.

레포를 확인해 보세요:

https://github.com/sklyt/render – 별표를 눌러서 예제들을 살펴보세요.

나에 대한 더 많은 글

여기서 찾아보세요

Back to Blog

관련 글

더 보기 »

Axios에서 Get, Post, Put, Delete

설치 npm bash npm install axios bower bash bower install axios yarn bash yarn add axios Axios 가져오기 javascript import axios from 'axios';