原文:NumPy: Beginner’s Guide - Third Edition
译者:飞龙
十一、玩转 Pygame
本章适用于希望使用 NumPy 和 Pygame 快速轻松创建游戏的开发人员。 基本的游戏开发经验会有所帮助,但这不是必需的。
您将学到的东西如下:
- pygame 基础
- matplotlib 集成
- 表面像素数组
- 人工智能
- 动画
- OpenGL
Pygame
Pygame 是 Python 框架,最初由 Pete Shinners 编写, 顾名思义,可用于制作视频游戏。 自 2004 年以来,Pygame 是免费的开放源代码,并获得 GPL 许可,这意味着您基本上可以制作任何类型的游戏。 Pygame 构建在简单 DirectMedia 层(SDL)。 SDL 是一个 C 框架,可以访问各种操作系统(包括 Linux,MacOSX 和 Windows)上的图形,声音,键盘和其他输入设备。
实战时间 – 安装 Pygame
我们将在本节中安装 Pygame。 Pygame 应该与所有 Python 版本兼容。 在撰写时,Python3 存在一些不兼容问题,但很可能很快就会解决。
- 在 Debian 和 Ubuntu 上安装:Pygame 可以在 Debian 档案文件中找到。
- 在 Windows 上安装:从 Pygame 网站下载适用于您正在使用的版本的 Python 的二进制安装程序。
- 在 Mac 上安装 Pygame:适用于 MacOSX 10.3 及更高版本的二进制 Pygame 包可在这个页面中找到。
- 从源代码安装:Pygame 使用
distutils
系统进行编译和安装。 要开始使用默认选项安装 Pygame,只需运行以下命令:
$ python setup.py
- 如果您需要有关可用选项的更多信息,请键入以下内容:
$ python setup.py help
- 要编译代码,您的操作系统需要一个编译器。 进行设置超出了本书的范围。 有关在 Windows 上编译 Pygame 的更多信息,可以在这个页面上找到。 有关在 MacOSX 上编译 Pygame 的更多信息,请参考这里。
HelloWorld
我们将创建一个简单的游戏,在本章中我们将进一步改进 。 与编程书籍中的传统方法一样,我们从Hello World!
示例开始。
实战时间 – 创建一个简单的游戏
重要的是要注意所谓的主游戏循环,在该循环中所有动作都会发生,并使用Font
模块渲染文本。 在此程序中,我们将操纵用于绘制的 Pygame Surface
对象,并处理退出事件。
- 首先,导入所需的 Pygame 模块。 如果正确安装了 Pygame,则不会出现任何错误,否则请返回安装“实战时间”:
import pygame, sys from pygame.locals import *
- 初始化 Pygame,按
300
像素创建400
的显示,并将窗口标题设置为Hello world!
:
pygame.init() screen = pygame.display.set_mode((400, 300)) pygame.display.set_caption('Hello World!')
- 游戏通常会有一个游戏循环,该循环将一直运行直到发生退出事件为止。 在此示例中,仅在坐标
(100, 100)
上设置带有文本Hello world!
的标签。 文字的字体大小为 19,颜色为红色:
while True: sysFont = pygame.font.SysFont("None", 19) rendered = sysFont.render('Hello World', 0, (255, 100, 100)) screen.blit(rendered, (100, 100)) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
- 我们得到以下屏幕截图作为最终结果:
以下是 HelloWorld 的完整代码示例:
import pygame, sys from pygame.locals import * pygame.init() screen = pygame.display.set_mode((400, 300)) pygame.display.set_caption('Hello World!') while True: sysFont = pygame.font.SysFont("None", 19) rendered = sysFont.render('Hello World', 0, (255, 100, 100)) screen.blit(rendered, (100, 100)) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
刚刚发生了什么?
看起来似乎不多,但是我们在本节中学到了很多东西。 下表总结了通过审查的函数:
函数 | 描述 |
pygame.init() |
此函数执行初始化,您必须在调用其他 Pygame 函数之前调用它。 |
pygame.display.set_mode((400, 300)) |
此函数创建一个要使用的所谓的Surface 对象。 我们给这个函数一个表示表面尺寸的元组。 |
pygame.display.set_caption('Hello World!') |
此函数将窗口标题设置为指定的字符串值。 |
pygame.font.SysFont("None", 19) |
此函数根据逗号分隔的字体列表(在本例中为无)和整数字体大小参数创建系统字体。 |
sysFont.render('Hello World', 0, (255, 100, 100)) |
此函数在Surface 上绘制文本。 最后一个参数是表示颜色的 RGB 值的元组。 |
screen.blit(rendered, (100, 100)) |
此函数使用Surface 。 |
pygame.event.get() |
此函数获取Event 对象的列表。 事件表示系统中的特殊事件,例如用户退出游戏。 |
pygame.quit() |
该函数清除由 Pygame 使用的资源。 退出游戏之前,请调用此函数。 |
pygame.display.update() |
此函数刷新表面。 |
动画
大多数游戏,甚至是最静态的游戏,都有一定程度的动画效果。 从程序员的角度来看,动画就是 ,无非就是在不同的时间在不同的位置显示对象,从而模拟运动。
Pygame 提供了一个Clock
对象,该对象管理每秒绘制多少帧。 这样可以确保动画与用户 CPU 的速度无关。
实战时间 – 使用 NumPy 和 Pygame 为对象设置动画
我们将加载图像,然后再次使用 NumPy 定义屏幕周围的顺时针路径。
- 创建一个 Pygame 时钟,如下所示:
clock = pygame.time.Clock()
- 作为本书随附的源代码的一部分,应该有一张头像。 加载此图像并在屏幕上四处移动:
img = pygame.image.load('head.jpg')
- 定义一些数组来保存位置的坐标,我们希望在动画过程中将图像放置在这些位置。 由于我们将移动对象,因此路径有四个逻辑部分:
right
,down
,left
和up
。 每个部分将具有40
等距步长。 将0
部分中的所有值初始化:
steps = np.linspace(20, 360, 40).astype(int) right = np.zeros((2, len(steps))) down = np.zeros((2, len(steps))) left = np.zeros((2, len(steps))) up = np.zeros((2, len(steps)))
- 设置图像位置的坐标很简单。 但是,需要注意一个棘手的问题-
[::-1]
表示法会导致数组元素的顺序颠倒:
right[0] = steps right[1] = 20 down[0] = 360 down[1] = steps left[0] = steps[::-1] left[1] = 360 up[0] = 20 up[1] = steps[::-1]
- 我们可以加入路径部分,但是在执行此操作之前,请使用
T
运算符转置数组,因为它们未正确对齐以进行连接:
pos = np.concatenate((right.T, down.T, left.T, up.T))
- 在主事件循环中,让时钟以每秒 30 帧的速度计时:
clock.tick(30)
- 摇头的屏幕截图如下:
您应该能够观看此动画的电影。 它也是代码包(animation.mp4
)的一部分。
此示例的代码几乎使用了到目前为止我们学到的所有内容,但仍应足够简单以了解:
import pygame, sys from pygame.locals import * import numpy as np pygame.init() clock = pygame.time.Clock() screen = pygame.display.set_mode((400, 400)) pygame.display.set_caption('Animating Objects') img = pygame.image.load('head.jpg') steps = np.linspace(20, 360, 40).astype(int) right = np.zeros((2, len(steps))) down = np.zeros((2, len(steps))) left = np.zeros((2, len(steps))) up = np.zeros((2, len(steps))) right[0] = steps right[1] = 20 down[0] = 360 down[1] = steps left[0] = steps[::-1] left[1] = 360 up[0] = 20 up[1] = steps[::-1] pos = np.concatenate((right.T, down.T, left.T, up.T)) i = 0 while True: # Erase screen screen.fill((255, 255, 255)) if i >= len(pos): i = 0 screen.blit(img, pos[i]) i += 1 for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update() clock.tick(30)
刚刚发生了什么?
在本节中,我们了解了一些有关动画的知识。 我们了解到的最重要的概念是时钟。 下表描述了我们使用的新函数:
函数 | 描述 |
pygame.time.Clock() |
这将创建一个游戏时钟。 |
clock.tick(30) |
此函数执行游戏时钟的刻度。 此处,30 是每秒的帧数。 |
matplotlib
matplotlib
是一个易于绘制的开源库,我们在第 9 章,“matplotlib 绘图”中了解到。 我们可以将 matplotlib 集成到 Pygame 游戏中并创建各种绘图。
实战时间 – 在 Pygame 中使用 matplotlib
在本秘籍中,我们采用上一节的位置坐标,并对其进行绘制。
- 要将 matplotlib 与 Pygame 集成,我们需要使用非交互式后端; 否则,默认情况下,matplotlib 将为我们提供一个 GUI 窗口。 我们将导入主要的 matplotlib 模块并调用
use()
函数。 在导入主 matplotlib 模块之后以及在导入其他 matplotlib 模块之前,立即调用此函数:
import matplotlib as mpl mpl.use("Agg")
- 我们可以在 matplotlib 画布上绘制非交互式绘图。 创建此画布需要导入,创建图形和子图。 将数字指定为
3
乘3
英寸大。 在此秘籍的末尾可以找到更多详细信息:
import matplotlib.pyplot as plt import matplotlib.backends.backend_agg as agg fig = plt.figure(figsize=[3, 3]) ax = fig.add_subplot(111) canvas = agg.FigureCanvasAgg(fig)
- 在非交互模式下,绘制数据比在默认模式下复杂一些。 由于我们需要重复绘图,因此在函数中组织绘图代码是有意义的。 Pygame 最终在画布上绘制了绘图。 画布为我们的设置增加了一些复杂性。 在此示例的末尾,您可以找到有关这些函数的更多详细说明:
def plot(data): ax.plot(data) canvas.draw() renderer = canvas.get_renderer() raw_data = renderer.tostring_rgb() size = canvas.get_width_height() return pygame.image.fromstring(raw_data, size, "RGB")
- 下面的屏幕截图显示了正在运行的动画。 您还可以在代码包(
matplotlib.mp4
)和 YouTube 上查看截屏视频。
更改后,我们将获得以下代码:
import pygame, sys from pygame.locals import * import numpy as np import matplotlib as mpl mpl.use("Agg") import matplotlib.pyplot as plt import matplotlib.backends.backend_agg as agg fig = plt.figure(figsize=[3, 3]) ax = fig.add_subplot(111) canvas = agg.FigureCanvasAgg(fig) def plot(data): ax.plot(data) canvas.draw() renderer = canvas.get_renderer() raw_data = renderer.tostring_rgb() size = canvas.get_width_height() return pygame.image.fromstring(raw_data, size, "RGB") pygame.init() clock = pygame.time.Clock() screen = pygame.display.set_mode((400, 400)) pygame.display.set_caption('Animating Objects') img = pygame.image.load('head.jpg') steps = np.linspace(20, 360, 40).astype(int) right = np.zeros((2, len(steps))) down = np.zeros((2, len(steps))) left = np.zeros((2, len(steps))) up = np.zeros((2, len(steps))) right[0] = steps right[1] = 20 down[0] = 360 down[1] = steps left[0] = steps[::-1] left[1] = 360 up[0] = 20 up[1] = steps[::-1] pos = np.concatenate((right.T, down.T, left.T, up.T)) i = 0 history = np.array([]) surf = plot(history) while True: # Erase screen screen.fill((255, 255, 255)) if i >= len(pos): i = 0 surf = plot(history) screen.blit(img, pos[i]) history = np.append(history, pos[i]) screen.blit(surf, (100, 100)) i += 1 for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update() clock.tick(30)
刚刚发生了什么?
下表解释了绘图相关函数:
函数 | 描述 |
mpl.use("Agg") |
此函数指定使用非交互式后端 |
plt.figure(figsize=[3, 3]) |
此函数创建一个3 x 3 英寸的图形 |
agg.FigureCanvasAgg(fig) |
此函数在非交互模式下创建画布 |
canvas.draw() |
此函数在画布上绘制 |
canvas.get_renderer() |
此函数为画布提供渲染器 |
表面像素
Pygame surfarray
模块处理 Pygame Surface
对象与 NumPy 数组之间的转换 。 您可能还记得,NumPy 可以快速有效地处理大型数组。
实战时间 – 用 NumPy 访问表面像素数据
在本节中,我们将平铺一个小图像以填充游戏屏幕。
array2d()
函数将像素复制到二维数组中(对于三维数组也有类似的功能)。 将头像图像中的像素复制到数组中:
pixels = pygame.surfarray.array2d(img)
- 使用数组的
shape
属性从像素数组的形状创建游戏屏幕。 在两个方向上将屏幕放大七倍:
X = pixels.shape[0] * 7 Y = pixels.shape[1] * 7 screen = pygame.display.set_mode((X, Y))
- 使用 NumPy
tile()
函数可以轻松平铺图像。 数据需要转换为整数值,因为 Pygame 将颜色定义为整数:
new_pixels = np.tile(pixels, (7, 7)).astype(int)
surfarray
模块具有特殊函数blit_array()
在屏幕上显示数组:
pygame.surfarray.blit_array(screen, new_pixels)
以下代码执行图像的平铺:
import pygame, sys from pygame.locals import * import numpy as np pygame.init() img = pygame.image.load('head.jpg') pixels = pygame.surfarray.array2d(img) X = pixels.shape[0] * 7 Y = pixels.shape[1] * 7 screen = pygame.display.set_mode((X, Y)) pygame.display.set_caption('Surfarray Demo') new_pixels = np.tile(pixels, (7, 7)).astype(int) while True: screen.fill((255, 255, 255)) pygame.surfarray.blit_array(screen, new_pixels) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
刚刚发生了什么?
以下是我们使用的新函数和属性的简要说明:
函数 | 描述 |
pygame.surfarray.array2d(img) |
此函数将像素数据复制到二维数组中 |
pygame.surfarray.blit_array(screen, new_pixels) |
此函数在屏幕上显示数组值 |
人工智能
通常,我们需要模仿游戏中的智能行为。 scikit-learn
项目旨在提供一种用于机器学习的 API,而我最喜欢的是其精美的文档。 我们可以使用操作系统的包管理器来安装scikit-learn
,尽管此选项可能有效或无效,具体取决于您的操作系统,但这应该是最方便的方法。 Windows 用户只需从项目网站下载安装程序即可。 在 Debian 和 Ubuntu 上,该项目称为python-sklearn
。 在 MacPorts 上,这些端口称为py26-scikits-learn
和py27-scikits-learn
。 我们也可以从源代码或使用easy_install
安装。 PythonXY, Enthought 和 NetBSD。
我们可以通过在命令行中键入来安装scikit-learn
:
$ [sudo] pip install -U scikit-learn
我们也可以键入以下内容而不是前一行:
$ [sudo] easy_install -U scikit-learn
由于权限的原因,这可能无法正常工作,因此您可能需要在命令前面放置sudo
或以管理员身份登录。
实战时间 – 点的聚类
我们将生成一些随机点并将它们聚类,这意味着彼此靠近的点将放入同一簇中。 这只是scikit-learn
可以应用的许多技术之一。聚类是一种机器学习算法,旨在基于相似度对项目进行分组。 接下来,我们将计算平方亲和矩阵。亲和度矩阵是包含亲和度值的矩阵:例如,点之间的距离。 最后,我们将这些点与[HTG2]中的AffinityPropagation
类聚类。
- 在
400 x 400
像素的正方形内生成 30 个随机点位置:
positions = np.random.randint(0, 400, size=(30, 2))
- 使用到原点的欧式距离作为亲和度度量来计算亲和度矩阵:
positions_norms = np.sum(positions ** 2, axis=1) S = - positions_norms[:, np.newaxis] - positions_norms[np.newaxis, :] + 2 * np.dot(positions, positions.T)
- 给
AffinityPropagation
类上一步的结果。 此类使用适当的群集编号标记点:
aff_pro = sklearn.cluster.AffinityPropagation().fit(S) labels = aff_pro.labels_
- 为每个群集绘制多边形。 涉及的函数需要点列表,颜色(将其绘制为红色)和表面:
pygame.draw.polygon(screen, (255, 0, 0), polygon_points[i])
- 结果是每个群集的一堆多边形,如下图所示:
群集示例代码如下:
import numpy as np import sklearn.cluster import pygame, sys from pygame.locals import * np.random.seed(42) positions = np.random.randint(0, 400, size=(30, 2)) positions_norms = np.sum(positions ** 2, axis=1) S = - positions_norms[:, np.newaxis] - positions_norms[np.newaxis, :] + 2 * np.dot(positions, positions.T) aff_pro = sklearn.cluster.AffinityPropagation().fit(S) labels = aff_pro.labels_ polygon_points = [] for i in xrange(max(labels) + 1): polygon_points.append([]) # Sorting points by cluster for label, position in zip(labels, positions): polygon_points[labels[i]].append(positions[i]) pygame.init() screen = pygame.display.set_mode((400, 400)) while True: for point in polygon_points: pygame.draw.polygon(screen, (255, 0, 0), point) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
刚刚发生了什么?
下表更详细地描述了人工智能示例中最重要的行 :
函数 | 描述 |
sklearn.cluster.AffinityPropagation().fit(S) |
此函数创建AffinityPropagation 对象,并使用相似性矩阵执行拟合 |
pygame.draw.polygon(screen, (255, 0, 0), point) |
给定表面,颜色(在这种情况下为红色)和点列表,此函数绘制多边形 |
OpenGL 和 Pygame
OpenGL 为二维和三维计算机图形指定了 API。 API 由函数和常量组成。 我们将专注于名为PyOpenGL
的 Python 实现。 使用以下命令安装PyOpenGL
:
$ [sudo] pip install PyOpenGL PyOpenGL_accelerate
您可能需要具有 root 访问权限才能执行此命令。 对应于easy_install
的命令如下:
$ [sudo] easy_install PyOpenGL PyOpenGL_accelerate
实战时间 – 绘制 Sierpinski 地毯
为了演示的目的,我们将使用 OpenGL 绘制一个 Sierpinski 地毯,也称为 Sierpinski 三角形或 Sierpinski 筛子。 这是由数学家 Waclaw Sierpinski 创建的三角形形状的分形图案。 三角形是通过递归且原则上是无限的过程获得的。
- 首先,首先初始化一些与 OpenGL 相关的原语。 这包括设置显示模式和背景颜色。 本节末尾提供逐行说明:
def display_openGL(w, h): pygame.display.set_mode((w,h), pygame.OPENGL|pygame.DOUBLEBUF) glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) gluOrtho2D(0, w, 0, h)
- 该算法要求我们显示点,越多越好。 首先,我们将绘图颜色设置为红色。 其次,我们定义一个三角形的顶点(我称它们为点)。 然后,我们定义随机索引,该随机索引将用于选择三个三角形顶点之一。 我们在中间的某个地方随机选择一个点,实际上并不重要。 之后,在上一个点和随机选取的一个顶点之间的一半处绘制点。 最后,刷新结果:
glColor3f(1.0, 0, 0) vertices = np.array([[0, 0], [DIM/2, DIM], [DIM, 0]]) NPOINTS = 9000 indices = np.random.random_integers(0, 2, NPOINTS) point = [175.0, 150.0] for i in xrange(NPOINTS): glBegin(GL_POINTS) point = (point + vertices[indices[i]])/2.0 glVertex2fv(point) glEnd() glFlush()
- Sierpinski 三角形如下所示:
带有所有导入的完整 Sierpinski 垫圈演示代码如下:
import pygame from pygame.locals import * import numpy as np from OpenGL.GL import * from OpenGL.GLU import * def display_openGL(w, h): pygame.display.set_mode((w,h), pygame.OPENGL|pygame.DOUBLEBUF) glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) gluOrtho2D(0, w, 0, h) def main(): pygame.init() pygame.display.set_caption('OpenGL Demo') DIM = 400 display_openGL(DIM, DIM) glColor3f(1.0, 0, 0) vertices = np.array([[0, 0], [DIM/2, DIM], [DIM, 0]]) NPOINTS = 9000 indices = np.random.random_integers(0, 2, NPOINTS) point = [175.0, 150.0] for i in xrange(NPOINTS): glBegin(GL_POINTS) point = (point + vertices[indices[i]])/2.0 glVertex2fv(point) glEnd() glFlush() pygame.display.flip() while True: for event in pygame.event.get(): if event.type == QUIT: return if __name__ == '__main__': main()
刚刚发生了什么?
如所承诺的,以下是该示例最重要部分的逐行说明:
函数 | 描述 |
pygame.display.set_mode((w,h), pygame.OPENGL|pygame.DOUBLEBUF) |
此函数将显示模式设置为所需的宽度,高度和 OpenGL 显示 |
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) |
此函数使用遮罩清除缓冲区。 在这里,我们清除颜色缓冲区和深度缓冲区位 |
gluOrtho2D(0, w, 0, h) |
此函数定义二维正交投影矩阵,其坐标为左,右,上和下剪切平面 |
glColor3f(1.0, 0, 0) |
此函数使用 RGB 的三个浮点值(红色,绿色,蓝色)定义当前图形颜色。 在这种情况下,我们将以红色绘制 |
glBegin(GL_POINTS) |
此函数定义了图元的顶点或图元的组。 这里的原语是点 |
glVertex2fv(point) |
此函数在给定顶点的情况下渲染点 |
glEnd() |
此函数关闭以glBegin() 开头的一段代码 |
glFlush() |
此函数强制执行 GL 命令 |
使用 Pygame 的模拟游戏
作为最后一个示例,我们将使用 Conway 的生命游戏模拟生命。 最初的生命游戏是基于一些基本规则。 我们从二维正方形网格上的随机配置开始。 网格中的每个单元可以是死的或活着的。 此状态取决于小区的邻居。 您可以在这个页面上详细了解规则。在每个时间步上,都会发生以下转换:
- 少于两个活邻居的活细胞死亡。
- 具有两个或三个活邻居的活细胞可以存活到下一代。
- 具有三个以上活邻居的活细胞死亡。
- 具有恰好三个活邻居的死细胞会成为活细胞。
卷积可用于求值游戏的基本规则。 卷积过程需要 SciPy 包。
实战时间 – 模拟生命
以下代码是生命游戏的实现 ,并进行了一些修改:
- 用鼠标单击一次会画一个十字,直到我们再次单击
r
键可将网格重置为随机状态b
键根据鼠标位置创建块g
键创建滑翔机
代码中最重要的数据结构是一个二维数组,其中包含游戏屏幕上像素的颜色值。 该数组用随机值初始化,然后针对游戏循环的每次迭代重新计算。 在下一部分中找到有关所涉及函数的更多信息。
- 要求值规则,请使用卷积,如下所示:
def get_pixar(arr, weights): states = ndimage.convolve(arr, weights, mode='wrap') bools = (states == 13) | (states == 12 ) | (states == 3) return bools.astype(int)
- 使用我们在第 2 章,“从 NumPy 基础知识开始”中学习的基本索引技巧来画十字:
def draw_cross(pixar): (posx, posy) = pygame.mouse.get_pos() pixar[posx, :] = 1 pixar[:, posy] = 1
- 用随机值初始化网格:
def random_init(n): return np.random.random_integers(0, 1, (n, n))
- 以下是完整的代码:
from __future__ import print_function import os, pygame from pygame.locals import * import numpy as np from scipy import ndimage def get_pixar(arr, weights): states = ndimage.convolve(arr, weights, mode='wrap') bools = (states == 13) | (states == 12 ) | (states == 3) return bools.astype(int) def draw_cross(pixar): (posx, posy) = pygame.mouse.get_pos() pixar[posx, :] = 1 pixar[:, posy] = 1 def random_init(n): return np.random.random_integers(0, 1, (n, n)) def draw_pattern(pixar, pattern): print(pattern) if pattern == 'glider': coords = [(0,1), (1,2), (2,0), (2,1), (2,2)] elif pattern == 'block': coords = [(3,3), (3,2), (2,3), (2,2)] elif pattern == 'exploder': coords = [(0,1), (1,2), (2,0), (2,1), (2,2), (3,3)] elif pattern == 'fpentomino': coords = [(2,3),(3,2),(4,2),(3,3),(3,4)] pos = pygame.mouse.get_pos() xs = np.arange(0, pos[0], 10) ys = np.arange(0, pos[1], 10) for x in xs: for y in ys: for i, j in coords: pixar[x + i, y + j] = 1 def main(): pygame.init () N = 400 pygame.display.set_mode((N, N)) pygame.display.set_caption("Life Demo") screen = pygame.display.get_surface() pixar = random_init(N) weights = np.array([[1,1,1], [1,10,1], [1,1,1]]) cross_on = False while True: pixar = get_pixar(pixar, weights) if cross_on: draw_cross(pixar) pygame.surfarray.blit_array(screen, pixar * 255 ** 3) pygame.display.flip() for event in pygame.event.get(): if event.type == QUIT: return if event.type == MOUSEBUTTONDOWN: cross_on = not cross_on if event.type == KEYDOWN: if event.key == ord('r'): pixar = random_init(N) print("Random init") if event.key == ord('g'): draw_pattern(pixar, 'glider') if event.key == ord('b'): draw_pattern(pixar, 'block') if event.key == ord('e'): draw_pattern(pixar, 'exploder') if event.key == ord('f'): draw_pattern(pixar, 'fpentomino') if __name__ == '__main__': main()
- 您应该能够从代码包(
life.mp4
)或 YouTube 上观看截屏视频。 正在运行的游戏的屏幕截图如下:
刚刚发生了什么?
我们使用了一些 NumPy 和 SciPy 函数,这些函数需要说明:
函数 | 描述 |
ndimage.convolve(arr, weights, mode='wrap') |
此函数在包装模式下使用权重将卷积运算应用于给定数组。 该模式与数组边界有关。 |
bools.astype(int) |
此函数将布尔数组转换为整数。 |
np.arange(0, pos[0], 10) |
此函数以 10 为步长创建一个从 0 到pos[0] 的数组。因此,如果pos[0] 等于 1000,我们将得到 0、10、20,… 990。 |
总结
您可能会发现本书中提到 Pygame 有点奇怪。 但是,阅读本章后,我希望您意识到 NumPy 和 Pygame 可以很好地结合在一起。 毕竟,游戏涉及大量计算,因此 NumPy 和 SciPy 是理想的选择,并且它们还需要scikit-learn
中提供的人工智能功能。 无论如何,制作游戏都很有趣,我们希望最后一章相当于十道菜后的精美甜点或咖啡! 如果您仍然渴望更多,请查看《NumPy Cookbook 第二版》, Ivan Idris,Packt Publishing,在本书的基础上以最小的重叠为基础。
附录 A:小测验答案
第 1 章,NumPy 快速入门
小测验 – arange()
函数的功能
arange(5) 做什么? |
它创建一个 NumPy 数组,其值为从 0-4 创建的 NumPy 数组的值,0、1、2、3 和 4 |
第 2 章,从 NumPy 基本原理开始
小测验 – ndarray
的形状
ndarray 的形状如何存储? |
它存储在一个元组中 |
第 3 章,熟悉常用函数
小测验 - 计算加权平均值
哪个函数返回数组的加权平均值? | average |
第 4 章,为您带来便利的便利函数
小测验 - 计算协方差
哪个函数返回两个数组的协方差? | cov |
第 5 章,使用矩阵和ufunc
小测验 – 使用字符串定义矩阵
mat 和bmat 函数接受的字符串中的行分隔符是什么? |
分号 |
第 6 章,深入探索 NumPy 模块
小测验 - 创建矩阵
哪个函数可以创建矩阵? | mat |
第 7 章,探索特殊例程
小测验 - 生成随机数
哪个 NumPy 模块处理随机数? | random |
第 8 章,通过测试确保质量
小测验 - 指定小数精度
assert_almost_equal 函数的哪个参数指定小数精度? |
decimal |
第 9 章,matplotlib 绘图
小测验 – plot()
函数
plot 函数有什么作用? |
它既不执行 1、2 也不执行 3 |
第 10 章,当 NumPy 不够用时 – Scipy 和更多
小测验 - 加载.mat
文件
哪个函数加载.mat 文件? |
loadmat |
附录 B:其他在线资源
本附录包含指向相关网站的链接。
Python 教程
- Think Python 中文第二版↗
- 笨办法学 Python · 续 中文版
- PythonSpot 中文系列教程
- PythonBasics 中文系列教程
- PythonGuru 中文系列教程
- Python 分布式计算↗
数学教程
- MIT 公开课课本/笔记
数据科学文档
- Numpy 技术栈中文文档
数据科学教程
- 斯坦福公开课课本/笔记
- UCB 公开课课本/笔记
- ApacheCN 数据科学译文集⭐
- TutorialsPoint NumPy 教程
- 复杂性思维 中文第二版
- 利用 Python 进行数据分析 · 第 2 版
- fast.ai 数值线性代数讲义 v2
- Pandas Cookbook 带注释源码
- 数据科学 IPython 笔记本
- UCSD COGS108 数据科学实战中文笔记
- USF MSDS501 计算数据科学中文讲义
- 数据可视化的基础知识
- Joyful Pandas↗
附录 C:NumPy 函数的参考
本附录包含有用的 NumPy 函数及其说明的列表。
numpy.apply_along_axis
(func1d, axis, arr, *args
):沿arr
的一维切片应用函数func1d
。numpy.arange([start,] stop[, step,], dtype=None)
:创建一个 NumPy 数组,它在指定范围内均匀间隔。numpy.argsort(a, axis=-1, kind='quicksort', order=None)
:返回对输入数组进行排序的索引。numpy.argmax(a, axis=None)
:返回沿轴的最大值的索引。numpy.argmin(a, axis=None)
:返回沿轴的最小值的索引。numpy.argwhere(a)
:查找非零元素的索引。numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)
:从类似数组的序列(例如 Python 列表)创建 NumPy 数组。numpy.testing.assert_allclose((actual, desired, rtol=1e-07, atol=0, err_msg='', verbose=True)
:如果两个对象在指定的精度下不相等,则引发错误。numpy.testing.assert_almost_equal()
:如果两个数字在指定的精度下不相等,则引发异常。numpy.testing.assert_approx_equal()
:如果两个数字在某个有效数字下不相等,则引发异常。numpy.testing.assert_array_almost_equal()
:如果两个数组在指定的精度下不相等,则引发异常。numpy.testing.assert_array_almost_equal_nulp(x, y, nulp=1)
:将数组与其最低精度单位(ULP)。numpy.testing.assert_array_equal()
:如果两个数组不相等,则引发异常。numpy.testing.assert_array_less()
:如果两个数组的形状不同,并且第一个数组的元素严格小于第二个数组的元素,则会引发异常。numpy.testing.assert_array_max_ulp(a, b, maxulp=1, dtype=None)
:确定数组元素最多相差 ULP 的指定数量。numpy.testing.assert_equal()
:测试两个 NumPy 数组是否相等。numpy.testing.assert_raises()
:如果使用定义的参数调用的可调用对象未引发指定的异常,则失败。numpy.testing.assert_string_equal()
:断言两个字符串相等。numpy.testing.assert_warns()
:如果未引发指定的警告,则失败。numpy.bartlett(M)
:返回带有M
点的 Bartlett 窗口。 此窗口类似于三角形窗口。numpy.random.binomial(n, p, size=None)
:从二项分布中抽取随机样本。numpy.bitwise_and(x1, x2[, out])
:计算数组的按位AND
。numpy.bitwise_xor(x1, x2[, out])
:计算数组的按位XOR
。numpy.blackman(M)
:返回一个具有M
点的布莱克曼窗口,该窗口接近最佳值,并且比凯撒窗口差。numpy.column_stack(tup)
:堆叠以元组列形式提供的一维数组 。numpy.concatenate ((a1, a2, ...), axis=0)
:将数组序列连接在一起。numpy.convolve(a, v, mode='full')
:计算一维数组的线性卷积。numpy.dot(a, b, out=None)
:计算两个数组的点积。numpy.diff(a, n=1, axis=-1)
:计算给定轴的 N 阶差。numpy.dsplit(ary, indices_or_sections)
:沿着第三轴将数组拆分为子数组。numpy.dstack(tup)
:沿第三轴堆叠以元组形式给出的数组。numpy.eye(N, M=None, k=0, dtype=<type 'float'>)
:返回单位矩阵。numpy.extract(condition, arr)
:使用条件选择数组的元素。numpy.fft.fftshift(x, axes=None)
:将信号的零频率分量移到频谱的中心。numpy.hamming(M)
:返回带有M
点的汉明窗口。numpy.hanning(M)
:返回具有M
点的汉宁窗口。numpy.hstack(tup)
:水平堆叠以元组形式给出的数组。numpy.isreal(x)
:返回一个布尔数组,其中True
对应于输入数组的实数(而不是复数)元素。numpy.kaiser(M, beta)
:对于给定的beta
参数,返回带有M
点的凯撒窗口。numpy.load(file, mmap_mode=None)
:从.npy
,.npz
,或腌制中加载 NumPy 数组或腌制对象。 内存映射的数组存储在文件系统中,不必完全加载到内存中。 这对于大型数组尤其有用。numpy.loadtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0)
:将文本文件中的数据加载到 NumPy 数组中。numpy.lexsort (keys, axis=-1)
:使用多个键进行排序。numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
:返回在间隔内均匀间隔的数字。numpy.max(a, axis=None, out=None, keepdims=False)
:沿轴返回数组的最大值。numpy.mean(a, axis=None, dtype=None, out=None, keepdims=False)
:沿给定轴计算算术平均值。numpy.median(a, axis=None, out=None, overwrite_input=False)
:沿给定轴计算中位数 。numpy.meshgrid(*xi, **kwargs)
:返回坐标向量的坐标矩阵。 例如:
In: numpy.meshgrid([1, 2], [3, 4]) Out: [array([[1, 2], [1, 2]]), array([[3, 3], [4, 4]])]
numpy.min(a, axis=None, out=None, keepdims=False)
:沿轴返回数组的最小值。numpy.msort(a)
:返回沿第一轴排序的数组的副本。numpy.nanargmax(a, axis=None)
:返回给定一个忽略 NaN 的轴的最大值的索引。numpy.nanargmin(a, axis=None)
:返回给定的轴的最小值索引,忽略 NaN。numpy.nonzero(a)
:返回非零数组元素的索引。numpy.ones(shape, dtype=None, order='C')
:创建指定形状和数据类型的 NumPy 数组,包含 1s。numpy.piecewise(x, condlist, funclist, *args, **kw)
:分段求值函数。numpy.polyder(p, m=1)
:将多项式微分为给定阶数。numpy.polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False)
:执行最小二乘多项式拟合。numpy.polysub(a1, a2)
:减去多项式。numpy.polyval(p, x)
:以指定值求值多项式。numpy.prod(a, axis=None, dtype=None, out=None, keepdims=False)
:返回指定轴上数组元素的乘积 。numpy.ravel(a, order='C')
:展平数组,或在必要时返回副本。numpy.reshape(a, newshape, order='C')
:更改 NumPy 数组的形状 。numpy.row_stack(tup)
:逐行堆叠数组。numpy.save(file, arr)
:以 NumPy.npy
格式将 NumPy 数组保存到文件中。numpy.savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', footer='', comments='# ')
:将 NumPy 数组保存到文本文件。numpy.sinc(a)
:计算sinc
函数。numpy.sort_complex(a)
:首先以实部,然后是虚部对数组元素进行排序。numpy.split(a, indices_or_sections, axis=0)
:将数组拆分为子数组。numpy.std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False)
:沿给定轴返回标准差。numpy.take(a, indices, axis=None, out=None, mode='raise')
:使用指定的索引从数组中选择元素。numpy.vsplit(a, indices_or_sections)
:将数组垂直拆分为子数组。numpy.vstack(tup)
:垂直堆叠数组。numpy.where(condition, [x, y])
:基于布尔条件从输入数组中选择数组元素。numpy.zeros(shape, dtype=float, order='C')
:创建指定形状和数据类型的 NumPy 数组,其中包含零。