生命游戏 单元测试python

标签:至少1个,最多5个
Game of Life I
According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."
Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):
Any live cell with fewer than two live neighbors dies, as if caused by under-population. Any live cell with two or three live neighbors lives on to the next generation. Any live cell with more than three live neighbors dies, as if by over-population.. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. Write a function to compute the next state (after one update) of the board given its current state.
Follow up:
Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?
时间 O(NN) 空间 O(1)
最简单的方法是再建一个矩阵保存,不过当inplace解时,如果我们直接根据每个点周围的存活数量来修改当前值,由于矩阵是顺序遍历的,这样会影响到下一个点的计算。如何在修改值的同时又保证下一个点的计算不会被影响呢?实际上我们只要将值稍作编码就行了,因为题目给出的是一个int矩阵,大有空间可以利用。这里我们假设对于某个点,值的含义为
0 : 上一轮是0,这一轮过后还是0
1 : 上一轮是1,这一轮过后还是1
2 : 上一轮是1,这一轮过后变为0
3 : 上一轮是0,这一轮过后变为1
这样,对于一个节点来说,如果它周边的点是1或者2,就说明那个点上一轮是活的。最后,在遍历一遍数组,把我们编码再解回去,即0和2都变回0,1和3都变回1,就行了。
注意编码方式,1和3都是这一轮过后为1,这样就可以用一个模2操作来直接解码了
我实现的时候并没有预先建立一个对应周围8个点的数组,因为实际复杂度是一样,多加几个数组反而程序可读性下降
public class Solution {
public void gameOfLife(int[][] board) {
int m = board.length, n = board[0].
for(int i = 0; i & i++){
for(int j = 0; j & j++){
int lives = 0;
// 判断上边
if(i & 0){
lives += board[i - 1][j] == 1 || board[i - 1][j] == 2 ? 1 : 0;
// 判断左边
if(j & 0){
lives += board[i][j - 1] == 1 || board[i][j - 1] == 2 ? 1 : 0;
// 判断下边
if(i & m - 1){
lives += board[i + 1][j] == 1 || board[i + 1][j] == 2 ? 1 : 0;
// 判断右边
if(j & n - 1){
lives += board[i][j + 1] == 1 || board[i][j + 1] == 2 ? 1 : 0;
// 判断左上角
if(i & 0 && j & 0){
lives += board[i - 1][j - 1] == 1 || board[i - 1][j - 1] == 2 ? 1 : 0;
//判断右下角
if(i & m - 1 && j & n - 1){
lives += board[i + 1][j + 1] == 1 || board[i + 1][j + 1] == 2 ? 1 : 0;
// 判断右上角
if(i & 0 && j & n - 1){
lives += board[i - 1][j + 1] == 1 || board[i - 1][j + 1] == 2 ? 1 : 0;
// 判断左下角
if(i & m - 1 && j & 0){
lives += board[i + 1][j - 1] == 1 || board[i + 1][j - 1] == 2 ? 1 : 0;
// 根据周边存活数量更新当前点,结果是0和1的情况不用更新
if(board[i][j] == 0 && lives == 3){
board[i][j] = 3;
} else if(board[i][j] == 1){
if(lives & 2 || lives & 3) board[i][j] = 2;
for(int i = 0; i & i++){
for(int j = 0; j & j++){
board[i][j] = board[i][j] % 2;
另一种编码方式是位操作,将下轮该cell要变的值存入bit2中,然后还原的时候右移就行了。
public void solveInplaceBit(int[][] board){
int m = board.length, n = board[0].
for(int i = 0; i & i++){
for(int j = 0; j & j++){
int lives = 0;
// 累加上下左右及四个角还有自身的值
for(int y = Math.max(i - 1, 0); y &= Math.min(i + 1, m - 1); y++){
for(int x = Math.max(j - 1, 0); x &= Math.min(j + 1, n - 1); x++){
// 累加bit1的值
lives += board[y][x] & 1;
// 如果自己是活的,周边有两个活的,lives是3
// 如果自己是死的,周边有三个活的,lives是3
// 如果自己是活的,周边有三个活的,lives减自己是3
if(lives == 3 || lives - board[i][j] == 3){
board[i][j] |= 2;
// 右移就是新的值
for(int i = 0; i & i++){
for(int j = 0; j & j++){
board[i][j] &&&= 1;
时间 O(NN) 空间 O(512)
上面的方法实测都比较慢,对于的矩阵计算时间都在600-1000ms,甚至比简单的用buffer的方法慢,我们再介绍一个能将速度提高一倍的方法。一般来说,优化程序有这么几个思路:
尽量减少嵌套的循环
减少对内存的读写操作
上个解法中,使用多个for循环的就比较慢,如果我们能够直接计算出该点的值而不用for循环就好了。这里我们可以用一个“环境”变量,表示该点所处的环境,这样我们根据它以及它周围八个点的值就可以直接算出它的环境值,而不需要用for循环来检查周围8个点。有人说,这不就只是把读取操作放到循环外面来了吗?其实这只是用了优化了第一点,减少循环,对于第二点我们也有优化,我们计算环境值这样计算,对于以n4为中心的点,其环境为
则环境值environment = n8 * 256 + n7 * 128 + n6 * 64 + n5 * 32 + n4 * 16 + n3 * 8 + n2 * 4 + n1 * 2 + n0 * 1,这么做的好处是把每一个格子的死活信息都用一个bit来表示,更巧妙地是当我们计算以n1为中心的环境时,是可以复用这些信息的,我们不用再读取一遍n5, n4, n3, n2, n1, n0的值,直接将上一次的环境值模上64后再乘以8,就是可以将他们都向左平移一格,这时候再读取三个新的值a, b, c就行了。
通过这种方法,我们将内存的读取次数从每个点九次,变成了每个点三次。另外我们还要预先制作一个表,来映射环境值和结果的关系。比如环境值为7时,说明n2, n1, n0都是活的,结果应该为1(下一轮活过来)。这里制作表的程序可以这么写:
int[] table = new int[512];
for(int i = 0; i & 512; i++){
int lives = Integer.bitCount(i);
if(lives == 3 || (lives - ((i & 16) & 0 ? 1 : 0) == 3)){
table[i] = 1;
public void solveWithTable(int rounds, int[][] board){
int[] lookupTable = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int m = board.length, n = board[0].
if(n == 0)
int[][] buffer = new int[m][n];
for(int i = 0; i & i++){
// 每一行开始时,先计算初始的环境值(左边两列)
int environment = (i - 1 &= 0 && board[i - 1][0] == 1? 4 : 0) + (board[i][0] == 1 ? 2 : 0) + (i + 1 & m && board[i + 1][0] == 1 ? 1 : 0);
// 对该行的每一列,通过加入右边新的一列,来计算该点的环境值
for(int j = 0; j & j++){
// 将之前的环境值模64再乘以8,然后加上右边新的三列
environment = (environment % 64) * 8 + (i - 1 &= 0 && j + 1 & n && board[i - 1][j + 1] == 1 ? 4 : 0) + (j + 1 & n && board[i][j + 1] == 1 ? 2 : 0) + (i + 1 & m && j + 1 & n && board[i + 1][j + 1] == 1 ? 1 : 0);
buffer[i][j] = lookupTable[environment];
for(int i = 0; i & i++){
for(int j = 0; j & j++){
board[i][j] = buffer[i][j];
后续 Follow Up
如果循环矩阵如何解决?循环的意思是假设一个3x3的矩阵,则a[0][0]的左边是a[0][1],其左上是a[2][2]这样我们的坐标要多加一个数组长度,使用坐标时还要取模
public void solveInplaceCircular(int rounds, int[][] board){
for(int round = 0; round & round++){
int m = board.length, n = board[0].
for(int i = 0; i & i++){
for(int j = 0; j & j++){
int lives = 0;
// 多加一个数组长度
for(int y = i + m - 1; y &= i + m + 1; y++){
for(int x = j + n - 1; x &= j + n + 1; x++){
// 使用的时候要取模
lives += board[y % m][x % n] & 1;
if(lives == 3 || lives - board[i][j] == 3){
board[i][j] |= 2;
for(int i = 0; i & i++){
for(int j = 0; j & j++){
board[i][j] &&&= 1;
如果矩阵很大如何优化?我们可以只记录存活节点的信息,存入一个live的list中,这里active代表着存活节点,或者存活节点的邻居。每次只计算这个list中节点和其邻居的情况。进一步优化的话,我们可以用一个active的list,只记录上次更新的节点,或者该节点的邻居。等计算完这个列表后,将产生更新的节点和它的邻居们存入一个新列表中,再用这个新列表里节点的值来更新矩阵。下一轮时,就计算这个新列表,再产生一个新列表。
如果多核的机器如何优化?因为是多核,我们可以用线程来实现并行计算。如图,将矩阵分块后,每个线程只负责其所在的分块的计算,不过主线程每一轮都要更新一下这些分块的边缘,并提供给相邻分块。所以这里的开销就是主线程和子线程通信这个边缘信息的开销。如果线程变多分块变多,边缘信息也会变多,开销会增大。所以选取线程的数量是这个开销和并行计算能力的折衷。
如果是多台机器如何优化?同样的,我们可以用一个主机器负责处理边缘信息,而多个子机器处理每个分块的信息,因为是分布式的,我们的矩阵可以分块的存储在不同机器的内存中,这样矩阵就可以很大。而主机在每一轮开始时,将边缘信息通过网络发送给哥哥分块机器,然后分块机器计算好自己的分块后,把新自己内边缘信息反馈给主机器。下一轮,等主机器收集齐所有边缘后,就可以继续重复。不过多台机器时还有一个更好的方法,就是使用Map Reduce。Map Reduce的简单版本是这样的,首先我们的Mapper读入一个file,这个file中每一行代表一个存活的节点的坐标,然后Mapper做出9个Key-Value对,对这个存活节点的邻居cell,分发出一个1。而对于节点自身,也要分发出一个1。这里Reducer是对应每个cell的,每个reducer累加自己cell得到了多少个1,就知道自己的cell周围有多少存活cell,就能知道该cell下一轮是否可以存活,如果可以存活则分发回mapper的文件中,等待下次读取,如果不能则舍弃。,那我们主要优化的地方则是mapper和reducer通信的开销,因为对于每个存活节点,mapper都要向9个reducer发一次信息。我们可以在mapper中用一个哈希表,当mapper读取文件的某一行时,先不向9个reducer发送信息,而是以这9个cell作为key,将1累加入哈希表中。这样等mapper读完文件后,再把哈希表中的cell和该cell对应的累加1次数,分发给相应cell的reducer,这样就可以减少一些通信开销。相当于是现在mapper内做了一次累加。这种优化在只有一个mapper是无效的,因为这就等于直接在mapper中统计完了,但是如果多个mapper同时执行时,相当于在每个mapper里先统计一会,再交给reducer一起统计每个mapper的统计结果。
1: class Mapper:
2: method Map ():
3: hash = ?
4: for line ∈ stdin:
cell, state = Parse (line)
hash[cell] += state
for neighbor in Neighborhood (cell):
hash[neighbor] += 2*state
9: for cell in hash:
strip-number = cell.row / strip-length
11: Emit (cell, strip-number, hash[cell])
1: class Reducer:
2: method Reduce ():
3: H = 0; last-cell = None
4: for line ∈ stdin:
strip-number, current-cell, in-value = Parse (line);
if current-cell ≠ last-cell :
if last-cell ≠ None:
Emit (last-cell, state=F(E(H))
H = 0; last-cell = current-cell
H += in_value
11: Emit (last-cell, state=F(E(xi))
如果整个图都会变,有没有更快的方法?参见,大意是用哈希记录一下会重复循环的pattern
Game of Life II
In Conway's Game of Life, cells in a grid are used to simulate biological cells. Each cell is considered to be either alive or dead. At each step of the simulation each cell's current status and number of living neighbors is used to determine the status of the cell during the following step of the simulation.
In this one-dimensional version, there are N cells numbered 0 through N-1. The number of cells does not change at any point in the simulation. Each cell i is adjacent to cells i-1 and i+1. Here, the indices are taken modulo N meaning cells 0 and N-1 are also adjacent to eachother. At each step of the simulation, cells with exactly one living neighbor change their status (alive cells become dead, dead cells become alive).
For example, if we represent dead cells with a '0' and living cells with a '1', consider the state with 8 cells:
Cells 0 and 6 have two living neighbors. Cells 1, 2, 3, and 4 have one living neighbor. Cells 5 and 7 have no living neighbors. Thus, at the next step of the simulation, the state would be:
时间 O(N) 空间 O()
一维数组需要考虑的情况更少,要注意的是这里头和尾是相连的,所以在判断其左右两边时要取模。
public void solveOneD(int[] board){
int n = board.
int[] buffer = new int[n];
// 根据每个点左右邻居更新该节点情况。
for(int i = 0; i & i++){
int lives = board[(i + n + 1) % n] + board[(i + n - 1) % n];
if(lives == 1){
buffer[i] = (board[i] + 1) % 2;
buffer[i] = board[i];
for(int i = 0; i & i++){
board[i] = buffer[i];
In Place 一维解法
public void solveOneD(int rounds, int[] board){
int n = board.
for(int i = 0; i & i++){
int lives = board[(i + n + 1) % n] % 2 + board[(i + n - 1) % n] % 2;
if(lives == 1){
board[i] = board[i] % 2 + 2;
board[i] = board[i];
for(int i = 0; i & i++){
board[i] = board[i] &= 2 ? (board[i] + 1) % 2 : board[i] % 2;
时间 O(N) 空间 O()
和上题的表优化一个意思,不过这里用到了循环数组,并且规则不太一样。
public void solveOneDWithTable(int[] board){
int n = board.
int[] lookupTable = {0, 1, 0, 1, 1, 0, 1, 0};
int[] buffer = new int[n];
int env = board[n - 1] * 2 + board[0] * 1;
for(int i = 0; i & i++){
env = (env % 4) * 2 + board[(i + n + 1) % n] * 1;
buffer[i] = (lookupTable[env] + board[i]) % 2;
System.out.println(env);
for(int i = 0; i & i++){
board[i] = buffer[i];
2 收藏&&|&&10
你可能感兴趣的文章
本作品采用 署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
我要该,理由是:您正在使用IE低版浏览器,为了您的雷锋网账号安全和更好的产品体验,强烈建议使用更快更安全的浏览器
发私信给张驰
导语:生命游戏(Game of Life)是英国数学家约翰·何顿·康威在1970年发明的细胞自动机,它也是一款“零玩家”游戏。
同步到新浪微博
不受意识控制地报道那些让人感动的产品技术和事件...... ;微信:nksimons;《脑洞》公众号:hackmind
当月热门文章
为了您的账户安全,请
您的邮箱还未验证,完成可获20积分哟!
您的账号已经绑定,现在您可以以方便用邮箱登录
请填写申请人资料用Python 实现康威生命游戏 - 推酷
用Python 实现康威生命游戏
17:44 阅读 2 收藏 0 推荐 0
康威生命游戏是一个久负盛名的数学游戏,有简单的规则和无穷无尽的组合。它不仅看起来非常迷幻,而且极富理性,可能用来模拟物理、化学、生态、社会等等各种现象。在国内外,都有很多小组专门研究这个游戏,并创造出了许多惊为天人的模型。
本课程将使用 pygame 模块来实现这样一个游戏,让你在趣味游戏中提升对 Python 的理解,入门 pygame。
,完整教程、代码及在线练习地址:
(更多课程请查看
一、实验介绍
1.1 实验内容
康威生命游戏,又称康威生命棋,是英国数学家约翰&何顿&康威在1970年发明的细胞自动机。
它最初于1970年10月在《科学美国人》杂志上马丁&葛登能的“数学游戏”专栏出现。
生命游戏是一个零玩家游戏。它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。
可以先在下面的地址中玩玩看。(注意这游戏迷幻的很)
每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。
人口过少:当周围低于2个(不包含2个)存活细胞时, 本单元活细胞死亡。
稳定:当周围有2个或3个存活细胞时, 本单元细胞保持原样。
人口过剩:当周围有3个以上的存活细胞时,本单元活细胞死亡。
繁殖:当周围有3个存活细胞时,本单元细胞存活/活化。
可能初看觉得这就是个模拟细胞繁衍的东西,规则也很简单,这能有什么意思。
这很有意思。
在游戏的进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状已经锁定,不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。对于生成的形状和秩序我们称作 pattern。或者在这里,我们也把它称作 creature。
静物族:面包
震荡族:信号灯
震荡族:脉冲星
宇宙飞船族:LWSS
XXX族:质数计算器
(本图出自 golly 的主页:
golly 是一个跨平台专门用于探索生命游戏与元胞自动机的开源软件)
趣闻:康威曾经相信没有 pattern 能够生成无限多的活细胞,并悬赏 50刀 奖赏能在 1970 年前找到反例的人。结果就是在生命游戏的世界中出现了很多枪支弹药啦(不懂这个梗的同学可以去看一看
中间的Gosper Glider Gun)
1.2 实验知识点
有限状态机
pygame模块
python 2.7
1.3 实验环境
1.4 适合人群
本课程难度中等,适合具有 Python 基础和 pygame 基础的同学学习。
1.5 代码获取
你可以通过下面命令将代码下载到实验楼环境中,作为参照对比进行学习。
$ wget http://labfile./courses/769/LifeGame.py
二、开发准备
我们会用 pygame 来实现一个生命游戏程序
安装 pygame
$ sudo apt-get update
$ sudo apt-get install python-pygame
三、实验步骤
我们使用矩阵来记录我们的游戏世界,其中单位值为 0 代表细胞死亡。单位值为 1 代表细胞存活。
这篇博文,一行代码解决了计算细胞周围活细胞数量的问题。
nbrs_count = sum(np.roll(np.roll(X, i, 0), j, 1)
for i in (-1, 0, 1) for j in (-1, 0, 1)
if (i != 0 or j != 0))
由于我们的游戏世界是上下左右循环的,所以将矩阵往8个方向进行循环移位得到8个新矩阵,将8个新矩阵相加就能够得到每个细胞周围的活细胞数量的矩阵了。
np.roll&操作就是循环移位操作。np.roll(X, i, 0)&中的&X&代表输入矩阵,i&代表移位的大小,0&代表移位的维度,np.roll(X, 1, 0)&代表矩阵下移一格,np.roll(X, 1, 1)&代表右移一格,if (i != 0 or j != 0))&是为了将原矩阵从计算中去除。
通过活细胞数量矩阵根据更新规则更新我们的世界。因为矩阵单位只有两种状态,这里我们只考虑存活态就可以了。注意到存活的条件:
稳定:当周围有2个或3个存活细胞时, 本单元细胞保持原样。
繁殖:当周围有3个存活细胞时,本单元细胞存活/活化。
即细胞周围数量等于3 或者 本单元细胞存活的同时周围有2个存活细胞的时候。本单元细胞将在下一代存活(也可看作繁衍)
翻译过来就是:
(nbrs_count == 3) | (X & (nbrs_count == 2))
注意到这种方法虽然便捷,但显然效率不怎么样。因为这种做法更新了矩阵的每一个单元,这完全没有必要,大部分情况下矩阵都是稀疏的。但如何改进就不在本文的讨论范畴内了。欢迎读者自己尝试提升效率。
我们实现的生命游戏操作如下:
R键 :重置世界
回车键 :进行演化
空格键 :暂停演化
鼠标左键 :增添一个细胞
鼠标右键 :销毁一个细胞
下面是用 pygame 实现的全部代码。
#-*- coding:utf8 -*-
import pygame, sys, time
import numpy as np
from pygame.locals import *
#矩阵宽与矩阵高
WIDTH = 80
HEIGHT = 40
#记录鼠标按键情况的全局变量
pygame.button_down = False
#记录游戏世界的矩阵
pygame.world=np.zeros((HEIGHT,WIDTH))
#创建 Cell 类方便细胞绘制
class Cell(pygame.sprite.Sprite):
def __init__(self, position):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([self.size, self.size])
self.image.fill((255,255,255))
# 创建一个以左上角为锚点的矩形
self.rect = self.image.get_rect()
self.rect.topleft = position
#绘图函数,注意到我们是把画布重置了再遍历整个世界地图,因此有很大的性能提升空间
def draw():
screen.fill((0,0,0))
for sp_col in range(pygame.world.shape[1]):
for sp_row in range(pygame.world.shape[0]):
if pygame.world[sp_row][sp_col]:
new_cell = Cell((sp_col * Cell.size,sp_row * Cell.size))
screen.blit(new_cell.image,new_cell.rect)
#根据细胞更新规则更新地图
def next_generation():
nbrs_count = sum(np.roll(np.roll(pygame.world, i, 0), j, 1)
for i in (-1, 0, 1) for j in (-1, 0, 1)
if (i != 0 or j != 0))
pygame.world = (nbrs_count == 3) | ((pygame.world == 1) & (nbrs_count == 2)).astype('int')
#地图初始化
def init():
pygame.world.fill(0)
return 'Stop'
#停止时的操作
def stop():
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN and event.key == K_RETURN:
return 'Move'
if event.type == KEYDOWN and event.key == K_r:
return 'Reset'
if event.type == MOUSEBUTTONDOWN:
pygame.button_down = True
pygame.button_type = event.button
if event.type == MOUSEBUTTONUP:
pygame.button_down = False
if pygame.button_down:
mouse_x, mouse_y = pygame.mouse.get_pos()
sp_col = mouse_x / Cell.
sp_row = mouse_y / Cell.
if pygame.button_type == 1: #鼠标左键
pygame.world[sp_row][sp_col] = 1
elif pygame.button_type == 3: #鼠标右键
pygame.world[sp_row][sp_col] = 0
return 'Stop'
#计时器,控制帧率
pygame.clock_start = 0
#进行演化时的操作
def move():
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN and event.key == K_SPACE:
return 'Stop'
if event.type == KEYDOWN and event.key == K_r:
return 'Reset'
if event.type == MOUSEBUTTONDOWN:
pygame.button_down = True
pygame.button_type = event.button
if event.type == MOUSEBUTTONUP:
pygame.button_down = False
if pygame.button_down:
mouse_x, mouse_y = pygame.mouse.get_pos()
sp_col = mouse_x / Cell.
sp_row = mouse_y / Cell.
if pygame.button_type == 1:
pygame.world[sp_row][sp_col] = 1
elif pygame.button_type == 3:
pygame.world[sp_row][sp_col] = 0
if time.clock() - pygame.clock_start & 0.02:
next_generation()
pygame.clock_start = time.clock()
return 'Move'
if __name__ == '__main__':
#状态机对应三种状态,初始化,停止,进行
state_actions = {
'Reset': init,
'Stop': stop,
'Move': move
state = 'Reset'
pygame.init()
pygame.display.set_caption('Conway\'s Game of Life')
screen = pygame.display.set_mode((WIDTH * Cell.size, HEIGHT * Cell.size))
while True: # 游戏主循环
state = state_actions[state]()
pygame.display.update()
六、实验总结
实验本身还是很有难度的,但是做出来的成就感就爆棚了呀。在学习的过程中就是需要一些自己感兴趣的东西来带动我们提升。如果还对游戏类的 Python 项目感兴趣的话,推荐
去深入理解 pygame 模块。
七、延伸阅读
八、课后习题
自由地玩耍或者用自己喜欢的语言实现一个生命游戏。
完整教程、代码及在线练习地址:
(更多课程请查看
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 生命游戏 java代码 的文章

 

随机推荐