crackcell's dustbin home
首页 > Life Game的R实现 > 正文

Life Game的R实现

/* -*- author: Tan Menglong; email: tanmenglong_at_gmail; twitter/weibo: @crackcell; 转载请注明出处 -*- */

1 Life Game

生命游戏(Game of Life),又称生命棋,是英国数学家约翰·何顿·康威(John Horton Conway)在1970年发明的细胞自动机(cellular automaton,也翻译成「格状自动机」)。1

  • ./life_game_live.gif

2 规则

整个游戏发生在一个二维格子矩阵世界,每个格子住着一个细菌。细菌的只有存活和死亡2种状态。整个世界不断在迭代,细菌在下一个迭代里的状态由相邻的8个格子中的细菌数决定,规则如下:

  1. 少于2个,死亡,因为群落不发达
  2. 2个或3个,继续存活
  3. 超过3个,死亡,因为食物不够
  4. 邻居为3个的死亡细胞,复活

3 R实现2

#'
#' Copyright (c) 2012, Tan Menglong <tanmenglong@gmail.com>
#'
#' This program is free software; you can redistribute it
#' and/or modify it under the terms of the GPL licence
#'
#'
#' @file life_game.R
#' @author Tan Menglong <tanmenglong@gmail.com>
#' @date Wed Apr  4 17:29:29 2012
#' @brief Life Game implemented by R
#'

InitWorld <- function(nrow, x) {
    #' @brief produce a n*n matrix with x bacteria

    x <- ifelse(x > nrow * nrow, nrow * nrow, x)
    matrix.slots <- rep(0, nrow * nrow)
    for (i in sample(1 : (nrow * nrow), x,
                     replace = FALSE)) {
        matrix.slots[i] = 1
    }
    return(matrix(data = matrix.slots, nrow = nrow, ncol = nrow))
}

InitPlot <- function(world) {
    #' @brief Prepare an empty plot

    plot(c(1 : nrow(world)), c(1 : nrow(world)),
         type = 'n', xlab = '', ylab = '')
}

PlotWorld <- function(world) {
    #' @brief Plot world matrix in a scatter plot

    for (x in 1 : nrow(world)) {
        for (y in 1 : nrow(world)) {
            if (world[x, y] == 1) {
                points(x, y)
            }
        }
    }
}

GetNeighbourNum <- function(world, x, y) {
    #' @brief Get number of neighbours

    left.corner.x <- ifelse(x == 1, 1, x - 1)
    left.corner.y <- ifelse(y == 1, 1, y - 1)
    right.corner.x <- ifelse(x == nrow(world), nrow(world), x + 1)
    right.corner.y <- ifelse(y == ncol(world), ncol(world), y + 1)

    count <- 0
    for (i in left.corner.x : right.corner.x) {
        for (j in left.corner.y : right.corner.y) {
            if (i == x && j == y) {
                next
            }
            if (world[i, j] == 1) {
                count <- count + 1
            }
        }
    }
    return(count)
}

Reproduce <- function(world) {
    #' @brief Reproduce the world

    changed <- FALSE
    bacteria.count.diff <- 0
    for (i in 1 : nrow(world)) {
        for (j in 1 : ncol(world)) {
            n <- GetNeighbourNum(world, i, j)
#            print(paste("[", i, ",", j, "]=", n))
            if (world[i, j] == 1) {
                if(n == 2 || n == 3) {
                    next
                }
                if (n < 2 || n > 3) {
                    changed <- TRUE
                    bacteria.count.diff <- bacteria.count.diff - 1
                    world[i, j] = 0
                }
            } else {
                if (n == 3) {
                    changed <- TRUE
                    bacteria.count.diff <- bacteria.count.diff + 1
                    world[i, j] = 1
                }
            }
        }
    }
    return(list(world, changed, bacteria.count.diff))
}

IsAllDead <- function(world) {
    #' @brief Is all bacteria dead?

    for (i in 1 : nrow(world)) {
        for (j in 1 : ncol(world)) {
            if (world[i, j] == 1) {
                return(FALSE)
            }
        }
    }
    return(TRUE)
}

generation.max <- 50
bacteria.count <- 150
world <- (InitWorld(30, bacteria.count))
generation <- 1
generation.vector <- c()
bacteria.count.vector <- c()
while (1) {
    InitPlot(world)
    PlotWorld(world)
    print(paste("generation: ", generation, ", bacteria: ", bacteria.count))
    generation.vector <- append(generation.vector, generation)
    bacteria.count.vector <- append(bacteria.count.vector, bacteria.count)
    if (IsAllDead(world)) {
        print("all dead")
        break
    }
    if (generation >= generation.max) {
        print("max generation")
        break
    }
    Sys.sleep(1)
    ret <- Reproduce(world)
    changed <- ret[[2]]
    if (!changed) {
        print("stable")
        break
    }
    bacteria.count <- bacteria.count + ret[[3]]
    world <- ret[[1]]
    generation <- generation + 1
}
plot(generation.vector, bacteria.count.vector, type = 'l')

RStudio里面运行,首先能看到一秒刷新一次的细菌分布图,类似如下:

  • ./life_game_snapshot.png

最后绘制了一个种群规模和时间的关系图,类似如下:

  • ./life_game_bacteria_amount_graph.png

4 后话

这里罗列了一种简单的Life Game的实现。此外,还可以扩充很多种玩法,这个仅限于想象力啦。更多信息请参见LifeWiki,Have fun。

Footnotes:

1 Conway's Game of Life, http://en.wikipedia.org/wiki/Conway's_Game_of_Life

2 life_game源码, life_game.R

Date: 2012-04-09 00:51:49 CST

Author: Menglong Tan

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
分享到:

blog comments powered by Disqus

Modified theme and code from Tom Preston-Werner. Hosted by Baidu App Engine.