RとPythonによるデータマイニング

企業でデータ分析などやっています。主にRやPythonによるデータマイニング・機械学習関連の話題と日々の日記やその他備忘録について書いてます。

Pythonによるライフゲーム

そうだ、ブログを書こう!
ということでブログを新設しました。
RやPythonによるデータマイニングやその他雑記、備忘録を適当に載せる予定です。


取り合えず日記でも。
今日はPythonライフゲームを作りました。pygameの練習として作ったのですが、結構面白いです。
参考にしたのは以下のサイトです。
http://aidiary.hatenablog.com/entry/20080507/1269694935
ライフゲームが何かという所から、Pythonによる実装まで超詳しく載ってます。
その他の記事も機械学習とか勉強している身からすると非常に面白いのでお勧めです。

作成したPythonのコードは以下の通り。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import random
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

SCR_RECT = Rect(0,0,800,600)
CS = 10
NUM_ROW = SCR_RECT.height / CS
NUM_COL = SCR_RECT.width / CS
DEAD, ALIVE = 0, 1
RAND_LIFE = 0.1
RATE_SUDDEN_ALIVE = 0.01
RATE_SUDDEN_DETH = 0.01
BC = 1

class LifeGame:
    def __init__(self):
        pygame.init()
        screen = pygame.display.set_mode(SCR_RECT.size)
        self.font = pygame.font.SysFont(None, 16)
        self.field = [[DEAD for x in range(NUM_COL)] for y in range(NUM_ROW)]
        self.generation = 0
        self.run = False
        self.cursor = [NUM_COL/2, NUM_ROW/2]
        self.clear()

        clock = pygame.time.Clock()
        while True:
            clock.tick(60)
            self.update()
            self.draw(screen)
            pygame.display.update()
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == KEYDOWN:
                    if event.key == K_ESCAPE:
                        pygame.quit()
                        sys.exit()
                    elif event.key == K_LEFT:
                        self.cursor[0] -= 1
                        if self.cursor[0] < 0: self.cursor[0] = 0
                        print self.cursor
                    elif event.key == K_RIGHT:
                        self.cursor[0] += 1
                        if self.cursor[0] > NUM_COL-1: self.cursor[0] - 1
                    elif event.key == K_UP:
                        self.cursor[1] -= 1
                        if self.cursor[1] < 0: self.cursor[1] = 0
                    elif event.key == K_DOWN:
                        self.cursor[1] += 1
                        if self.cursor[1] > NUM_ROW-1: self.cursor[1] = NUM_ROW-1
                    elif event.key == K_SPACE:
                        x, y = self.cursor
                        if self.field[y][x] == DEAD:
                            self.field[y][x] = ALIVE
                        elif self.field[y][x] == ALIVE:
                            self.field[y][x] = DEAD
                    elif event.key == K_s:
                        self.run = not self.run
                    elif event.key == K_n:
                        self.step()
                    elif event.key == K_c:
                        self.clear()
                    elif event.key == K_r:
                        self.rand()
                elif event.type == MOUSEBUTTONDOWN and event.button == 1:
                    px, py = event.pos
                    x, y = px/CS, py/CS
                    self.cursor = [x,y]
                    if self.field[y][x] == DEAD:
                        self.field[y][x] = ALIVE
                    elif self.field[y][x] == ALIVE:
                        self.field[y][x] = DEAD

    def clear(self):
        self.generation = 0
        for y in range(NUM_ROW):
            for x in range(NUM_COL):
                self.field[y][x] = DEAD

    def rand(self):
        for y in range(NUM_ROW):
            for x in range(NUM_COL):
                if random.random() < RAND_LIFE:
                    self.field[y][x] = ALIVE

    def update(self):
        if self.run:
            self.step()

    def step(self):
        next_field = [[False for x in range(NUM_COL)] for y in range(NUM_ROW)]
        for y in range(NUM_ROW):
            for x in range(NUM_COL):
                num_alive_cells = self.around(x,y)
                if num_alive_cells == 2:
                    next_field[y][x] = self.field[y][x]
                elif num_alive_cells == 3:
                    next_field[y][x] = ALIVE
                elif num_alive_cells > 0:
                    next_field[y][x] = DEAD

                if num_alive_cells >= 2:
                    if self.field[y][x] == ALIVE and random.random() <= RATE_SUDDEN_DETH:
                        next_field[y][x] = DEAD
                    elif self.field[y][x] == DEAD and random.random() <= RATE_SUDDEN_ALIVE:
                        next_field[y][x] = ALIVE

        self.field = next_field
        self.generation += 1

    def draw(self,screen):
        for y in range(NUM_ROW):
            for x in range(NUM_COL):
                if self.field[y][x] == ALIVE:
                    pygame.draw.rect(screen,(255,255,0),Rect(x*CS,y*CS,CS,CS))
                elif self.field[y][x] == DEAD:
                    pygame.draw.rect(screen,(0,0,0),Rect(x*CS,y*CS,CS,CS))
                pygame.draw.rect(screen,(50,50,50),Rect(x*CS,y*CS,CS,CS),1)
        pygame.draw.line(screen,(255,0,0),(0,SCR_RECT.height/2),(SCR_RECT.width,SCR_RECT.height/2))
        pygame.draw.line(screen,(255,0,0),(SCR_RECT.width/2,0),(SCR_RECT.width/2,SCR_RECT.height))
        pygame.draw.rect(screen,(0,0,255),Rect(self.cursor[0]*CS,self.cursor[1]*CS,CS,CS),1)
        screen.blit(self.font.render("generation:%d" % self.generation, True, (0,255,0)),(0,0))
        screen.blit(self.font.render("spase : birth/kill", True, (0,255,0)),(0,12))
        screen.blit(self.font.render("s : start/stop",True, (0,255,0)),(0,24))
        screen.blit(self.font.render("n : next",True, (0,255,0)),(0,36))
        screen.blit(self.font.render("r : random", True, (0,255,0)),(0,48))

    def around(self, x, y):
        if BC == 0:
            if x == 0 or x == NUM_COL-1 or y == 0 or y == NUM_ROW-1:
                return 0
        elif BC == 1:
            sum = 0
            sum += self.field[(y-1+NUM_ROW)%NUM_ROW][(x-1+NUM_COL)%NUM_COL]
            sum += self.field[(y-1+NUM_ROW)%NUM_ROW][x]
            sum += self.field[(y-1+NUM_ROW)%NUM_ROW][(x+1)%NUM_COL]
            sum += self.field[y][(x-1+NUM_COL)%NUM_COL]
            sum += self.field[y][(x+1)%NUM_COL]
            sum += self.field[(y+1)%NUM_ROW][(x-1+NUM_COL)%NUM_COL]
            sum += self.field[(y+1)%NUM_ROW][x]
            sum += self.field[(y+1)%NUM_ROW][(x+1)%NUM_COL]
        return sum

if __name__ == "__main__":
    LifeGame()

今度ブログにコードを載せる方法を調べよう。。。

基本は参考サイトの丸パクリだけど、周期的境界条件と突然死(と自然発生)の部分を追加しています。
突然死と自然発生はパラメータを調整することで、ユニバーサリティを持つのがわかるので面白いです。