进化算法,实时渲染于 Node.js

发布: (2025年12月17日 GMT+8 21:00)
5 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容(文章正文),我将为您翻译成简体中文并保留原有的格式、Markdown 语法以及代码块和链接。谢谢!

介绍

强化学习、进化算法以及任何让计算机 看见 的技术,都是最让人视觉上满足的课题之一。实时观看事物学习的过程永远不会让人感到厌倦。

本文其实只是一个借口,用来展示我为 Node.js 构建的一个小型图形渲染器:tessera.js

说实话,我想不出比进化算法的 hello world 更好的演示了:一堆随机颜色在变异,慢慢进化到目标颜色。

进化算法

如果你是图形编程新手,别担心——数字世界中 所有视觉 的基本单元其实非常简单。我在这里做了介绍:

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';` 基础…