1970년경 영국의 수학자 존 콘웨이(John Conway)는 '게임 오브 라이프(Game of Life)'를 고안했습니다. 이는 생물 집단이 무질서해 보이면서도 일정한 패턴을 그리며 성장하는 과정을 규칙으로 표현한 것입니다. '게임 오브 라이프'는 '살아있는(living)' 세포와 '죽은(dead)' 세포로 구성된 2차원 격자(grid) 위에서 진행되는 시뮬레이션입니다.
게임 오브 라이프의 규칙
과밀(Overpopulation): 주변에 살아있는 세포가 3개보다 많으면 해당 세포는 죽습니다(off).
생존(Static): 주변에 살아있는 세포가 2개 또는 3개면 해당 세포는 살아남습니다(on).
과소(Underpopulation): 주변에 살아있는 세포가 2개 미만이면 해당 세포는 죽습니다(off).
번식(Reproduction): 죽어 있는 세포의 주변에 정확히 3개의 살아있는 세포가 있으면 그 자리에서 새로운 세포가 태어납니다(on).

다음 타임스텝에서 죽을 세포
다음 타임스텝에서 살아날 세포
살아있는 세포
죽어있는 세포
위의 규칙들을 순차적인 단계마다 반복해서 적용하면 아름답고 예상치 못한 패턴들이 만들어집니다.
구현 단계
i. 빈 우주(universe)를 초기화한다 ii. 우주를 시드(seed) 값으로 채운다 iii. 현재 세포가 이웃 세포들을 기준으로 다음 타임스텝까지 생존하는지 계산한다 iv. iii의 생존 판정 함수를 우주 내 모든 세포와 그 이웃들에 대해 반복 적용한다 v. 원하는 세대(generation) 수만큼 iii~iv 과정을 반복한다
라이브러리 설치
콘웨이의 게임 오브 라이프를 구현하기 위해 matplotlib과 numpy 배열 라이브러리를 사용합니다. pip를 이용해 numpy와 matplotlib을 설치합니다.
$pip install numpy matplotlib
구현 프로그램
이제 앞서 정의한 규칙에 따라 실제 코드를 작성해 보겠습니다. 아래는 게임 오브 라이프를 구현한 전체 프로그램입니다.
# 필요한 라이브러리 임포트
import numpy as np
import matplotlib.pyplot as plt
import argparse
import time
#-------------------------------------------------------------------------
class Board(object):
def __init__(self, size, seed = 'Random'):
if seed == 'Random':
self.state = np.random.randint(2, size = size)
self.engine = Engine(self)
self.iteration = 0
def animate(self):
i = self.iteration
im = None
plt.title("Conway's Game of Life")
while True:
if i == 0:
plt.ion()
im = plt.imshow(self.state, vmin = 0, vmax = 2, cmap = plt.cm.gray)
else:
im.set_data(self.state)
i += 1
self.engine.applyRules()
print('Life Cycle: {} Birth: {} Survive: {}'.format(i, self.engine.nBirth, self.engine.nSurvive))
plt.pause(0.01)
yield self
#-------------------------------------------------------------------------
class Engine(object):
def __init__(self, board):
self.state = board.state
def countNeighbors(self):
state = self.state
n = (state[0:-2,0:-2] + state[0:-2,1:-1] + state[0:-2,2:] +
state[1:-1,0:-2] + state[1:-1,2:] + state[2:,0:-2] +
state[2:,1:-1] + state[2:,2:])
return n
def applyRules(self):
n = self.countNeighbors()
state = self.state
birth = (n == 3) & (state[1:-1,1:-1] == 0)
survive = ((n == 2) | (n == 3)) & (state[1:-1,1:-1] == 1)
state[...] = 0
state[1:-1,1:-1][birth | survive] = 1
nBirth = np.sum(birth)
self.nBirth = nBirth
nSurvive = np.sum(survive)
self.nSurvive = nSurvive
return state
#-------------------------------------------------------------------------
def main():
ap = argparse.ArgumentParser(add_help = False) # 인자 파서 초기화
ap.add_argument('-h', '--height', help = 'Board Height', default = 256)
ap.add_argument('-w', '--width', help = 'Board Width', default = 256)
args = vars(ap.parse_args()) # 인자 수집
bHeight = int(args['height'])
bWidth = int(args['width'])
board = Board((bHeight,bWidth))
for _ in board.animate():
pass
#-------------------------------------------------------------------------
if __name__ == '__main__':
main()실행 결과
Console: Life Cycle: 1 Birth: 7166 Survive: 10621 Life Cycle: 2 Birth: 7930 Survive: 8409 Life Cycle: 3 Birth: 7574 Survive: 8756 Life Cycle: 4 Birth: 7114 Survive: 8406 Life Cycle: 5 Birth: 7005 Survive: 8126 Life Cycle: 6 Birth: 6644 Survive: 7926 Life Cycle: 7 Birth: 6266 Survive: 7711 Life Cycle: 8 Birth: 6132 Survive: 7427 Life Cycle: 9 Birth: 5957 Survive: 7322 Life Cycle: 10 Birth: 5769 Survive: 7290 Life Cycle: 11 Birth: 5585 Survive: 6937 Life Cycle: 12 Birth: 5381 Survive: 6791 Life Cycle: 13 Birth: 5208 Survive: 6686 Life Cycle: 14 Birth: 5063 Survive: 6563 …. …
위와 같은 결과가 터미널에서 Ctrl-C를 눌러 프로그램을 종료할 때까지 계속 출력됩니다.
그래픽 화면 출력

세포들은 계속 상태를 바꿔가며 매우 아름다운 패턴을 시뮬레이션합니다.
설정값과 슬라이더 값을 변경하면 위의 서브플롯(subplot) 화면도 다양하게 조정할 수 있습니다.
