콘웨이의 생명 게임#
콘웨이의 생명 게임은 1970년 영국의 수학자 존 호턴 콘웨이(John Horton Conway) 가 고안한 셀룰러 오토마톤(Cellular Automaton)입니다.
"생명"의 탄생과 소멸을 아주 단순한 규칙으로 시뮬레이션하는 모델로, 복잡한 행동이 단순한 규칙에서 어떻게 나타나는지를 보여주는 대표적인 예로 유명합니다.
2차원 격자 위에서 각 셀은 살아 있거나 죽어 있는 두 상태 중 하나를 가지며 모든 셀은 자신의 이웃을 관찰해 다음 세대의 상태를 결정합니다.
규칙#
모든 셀은 동시에 다음 세대로 갱신되는데요. 주변 8칸의 이웃에 따라 운명이 결정됩니다.
| 현재 상태 | 주변의 살아 있는 셀 수 | 다음 세대의 상태 |
|---|---|---|
| 살아 있음 | < 2 | 죽음 |
| 살아 있음 | 2 or 3 | 생존 |
| 살아 있음 | > 3 | 죽음 |
| 죽어 있음 | = 3 | 탄생 |
살아 있는 셀은 이웃이 너무 적거나 많아도 죽고, 죽은 셀은 정확히 세 명의 이웃이 있을 때만 다시 살아납니다.
이 네 가지 규칙만으로 무작위 셀들이 모여 스스로 움직이는 세계처럼 만들어집니다.
시각화#
대표적인 몇 가지 패턴을 시각화해 봤습니다.
그리드를 누르면 다음 세대로 갱신됩니다.
정물(Still life)#
정물은 변하지 않는 안정된 구조를 가집니다.
Block
Boat
Tub
Beehive
Loaf
진동자(Oscillator)#
진동자는 주기적으로 반복되는 구조를 가집니다.
Blinker
Toad
Beacon
우주선(Spaceships)#
우주선은 세대를 거듭하며 이동하는 구조를 가집니다.
Glider
장수(Methuselah)#
장수는 작은 초기 배치로 시작하지만 오랜 세대를 거쳐서 안정화되는 구조를 가집니다.
아래 R-Pentomino는 초기에는 3x3 그리드에 그려지지만 1103세대에 안정화 된다고 합니다. 그리드도 훨씬 큰 영역이 필요한데 블로그 영역 상 최소 크기만 표현했습니다.
R-Pentomino
이 영상에서 전체 세대를 확인하실 수 있습니다.
코드#
직접 테스트해 보실 분들을 위해 코드를 공유해 드립니다.
automaton.ts
export type Cell = 1 | 0;// prettier-ignoreconst NEIGHBOR_OFFSETS = [[-1, -1], [0, -1], [1, -1],[-1, 0], [1, 0],[-1, 1], [0, 1], [1, 1],] as const;const BIRTH = new Set([3]);const SURVIVE = new Set([2, 3]);class Automaton {private cell: Cell[][];private width: number;private height: number;constructor(seed: Cell[][]) {this.cell = seed;this.width = seed[0].length;this.height = seed.length;}private get(x: number, y: number) {if (x < 0 || x >= this.width || y < 0 || y >= this.height) return 0;return this.cell[y][x];}private neighbor(x: number, y: number) {let n = 0;for (const [dx, dy] of NEIGHBOR_OFFSETS) {n += this.get(x + dx, y + dy);if (n > 3) return n;}return n;}next() {const nextCell = this.cell.map((row) => row.slice());for (let y = 0; y < this.height; y++) {const row = this.cell[y];const nextRow = nextCell[y];for (let x = 0; x < this.width; x++) {const alive = row[x] === 1;const n = this.neighbor(x, y);nextRow[x] = (alive ? SURVIVE.has(n) : BIRTH.has(n)) ? 1 : 0;}}this.cell = nextCell;}view() {return this.cell;}}