Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway's Game of Life)

1970년경 영국의 수학자 존 콘웨이(John Conway)는 '게임 오브 라이프(Game of Life)'를 고안했습니다. 이는 생물 집단이 무질서해 보이면서도 일정한 패턴을 그리며 성장하는 과정을 규칙으로 표현한 것입니다. '게임 오브 라이프'는 '살아있는(living)' 세포와 '죽은(dead)' 세포로 구성된 2차원 격자(grid) 위에서 진행되는 시뮬레이션입니다.

게임 오브 라이프의 규칙

  • 과밀(Overpopulation): 주변에 살아있는 세포가 3개보다 많으면 해당 세포는 죽습니다(off).

  • 생존(Static): 주변에 살아있는 세포가 2개 또는 3개면 해당 세포는 살아남습니다(on).

  • 과소(Underpopulation): 주변에 살아있는 세포가 2개 미만이면 해당 세포는 죽습니다(off).

  • 번식(Reproduction): 죽어 있는 세포의 주변에 정확히 3개의 살아있는 세포가 있으면 그 자리에서 새로운 세포가 태어납니다(on).

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life)

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life) 다음 타임스텝에서 죽을 세포

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life) 다음 타임스텝에서 살아날 세포

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life) 살아있는 세포

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life) 죽어있는 세포

위의 규칙들을 순차적인 단계마다 반복해서 적용하면 아름답고 예상치 못한 패턴들이 만들어집니다.

구현 단계

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를 눌러 프로그램을 종료할 때까지 계속 출력됩니다.

그래픽 화면 출력

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life)

세포들은 계속 상태를 바꿔가며 매우 아름다운 패턴을 시뮬레이션합니다.

설정값과 슬라이더 값을 변경하면 위의 서브플롯(subplot) 화면도 다양하게 조정할 수 있습니다.

파이썬으로 구현하는 콘웨이의 게임 오브 라이프(Conway s Game of Life)