书名:Python AI游戏编程入门 : 基于Pygame和PyTorch
ISBN:978-7-115-64580-7
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 肖 凯
责任编辑 贾鸿飞
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书以Python为开发环境、以小游戏开发为载体对编程及AI技术进行讲解,让读者在学习Python编程的同时学习AI知识。
本书共16章,可分为4个部分。第1部分为第1~3章,分别是编程、游戏和AI,Python环境准备和预备知识及Pygame基础知识。第2部分为第4~7章,分别讲解了贪吃蛇游戏编程、打砖块游戏编程、笨鸟先飞游戏编程和五子棋游戏编程。第3部分为第8~12章,分别介绍了神经网络和PyTorch基础、蒙特卡罗模拟、强化学习入门、深度强化学习算法DQN及遗传算法。第4部分为第13~16章,分布介绍了贪吃蛇游戏AI编程、打砖块游戏AI编程、笨鸟先飞游戏AI编程和五子棋游戏AI编程。
本书内容系统性强,语言精练,适合具备Python基础语法知识的、对AI游戏编程感兴趣的读者阅读,也适合作为青少年游戏编程培训用书。
有人说,未来是代码的世界,也是AI的世界。
这种说法或许有些绝对,但从信息技术发展的速度及趋势来看,会编写程序代码和懂AI(artificial intelligence,人工智能)技术,确实是技能跟上科技发展的必要条件。而毋庸置疑的是,要想真正理解AI技术,会编程是基础之一。
回望历史,从某种意义上说,AlphaGo的诞生是AI时代到来的标志,因为人们普遍承认下围棋是一种高级别的智能过程——而当AlphaGo击败李世石、柯洁等世界顶尖围棋高手之后,人们感受到了围棋机器人“大脑”的强大。AlphaGo通过蒙特卡罗模拟使用不同的落子方案进行预演,不断刷新落子方案,进而不断增加胜算。东京大学电子信息学专业教授伊庭齐志曾说,研究游戏AI或许是解密人类大脑思考方式的方法之一。游戏AI领域里的经典算法一边计算资源消耗,一边计算如何获得游戏胜利,这种兼顾成本与收益的规划方式已经初步具备与人类“权衡”活动相似的评估眼前与未来的能力。
计算机程序背后的代码本身是抽象而枯燥的,但游戏是趣味横生的。为了让更多对AI技术感兴趣的读者步入AI的世界,本书以Python为开发环境,以小游戏开发为载体,对编程思想及AI技术进行讲解,让读者同时点亮这两种技能。希望读者能通过本书欣赏和领悟到代码世界的奇妙。
本书共16章,可分为4个部分。
第1部分为第1~3章,介绍了基于Python环境进行游戏编程所需要的基础,分别是编程、游戏和AI,Python环境准备和预备知识及Pygame基础知识。
第2部分为第4~7章,为游戏编程案例讲解,包含贪吃蛇、打砖块、笨鸟先飞和五子棋4个游戏。
第3部分为第8~12章,介绍了AI的部分基础知识,分别是神经网络和PyTorch基础、蒙特卡罗模拟、强化学习入门、深度强化学习算法DQN和遗传算法。
第4部分为第13~16章,介绍了第2部分讲解的4款游戏引入AI如何进行编程。
就内容主线来讲,本书先通过Pygame编写小游戏让读者熟悉Python代码编写,然后引入神经网络、强化学习、DQN算法及PyTorch工具,让读者了解AI基础知识后对游戏代码进行改编,从而实现AI游戏编程。
需要说明的是,本书中的代码均用浅灰色底纹标识,代码运行的输出结果以上下虚线边框标识。
Pygame是一个开源的Python库,主要用于2D游戏的开发。Pygame提供了丰富的功能,如图像渲染、声音播放、键盘和鼠标操作、像素操作、碰撞检测等,这些功能让游戏开发更加简单。PyTorch则是开源的深度学习框架,以出色的灵活性和易用性著称,用于构建深度学习模型的功能完备,通常用于图像识别和语言处理等应用程序的机器学习。对于想以Python为工具理解游戏开发和AI技术的读者来说,学好这二者的使用是一个非常具有性价比的选择。
另外,本书提及的文件均提供下载,请在哔哩哔哩网站/App搜索“肖老师的退休生活”,从UP主个人主页获取下载方式。
在本书写作过程中,笔者力求简洁、精确,但囿于水平,书中难免存在疏漏之处。若读者在学习中遇到问题,请发送E-mail至jiahongfei@ptpress.com.cn与我们联系。
编者
2024年2月
现代社会中,代码无处不在。
早上,智能手机响起铃声将你从梦中唤醒,这闹钟程序及其底层系统是由代码构建的。洗漱完毕后,你准备搭乘出租车或地铁去工作,交通工具的调度系统是由代码构建的。另外,汽车的自动稳定系统,或者更智能的自动驾驶系统是由代码驱动的。到达公司,你打开计算机浏览重要的新闻,推送这些新闻信息的推荐系统也是由代码构建的。
除此之外,代码还在推动生产力的提升。
在制造领域,以大型客机制造为例,飞机的设计图纸已经转变为数字形态,工程师在三维数字世界里设计飞机。在由软件控制的自动生产线上,智能机械手臂根据数字化的设计图纸实现飞机的装配。飞行员使用自动驾驶软件来控制飞机,数百万行代码控制的各种装置不断感知飞机姿态,控制进气量、发动机功率和机翼的角度等。
在信息领域,以搜索引擎为例,谷歌能支撑每天数十亿次的搜索,其分布在世界各地的数据中心功不可没。数据中心的智能系统有一整套分布式并行集群架构,支持先进的大数据管理和处理,而且通过在数据中心部署的机器学习功能,谷歌可以很好地控制其冷却系统的能源消耗,对环境更为友好。
在医疗领域,以影像检测为例,基于人工神经网络的AI在检测乳腺癌症状方面,与有经验的放射科医生有着同样优秀的表现。除了帮助临床医生发现疾病的早期症状,AI还可帮助处理、跟踪数量惊人的各种类型的医学影像,并通过检测患者病历等重要资料,将相关影像提供给临床医生,从而实现更好的临床管理。
在科研领域,以火星探索为例,登陆火星的“好奇”号火星车内部系统含有六大部件,这些部件需要协同整合。在执行任务的过程中,内部系统不能出现任何差错,对失误零容忍。而对内部系统的整合,一共使用了约50万行计算机代码,令“好奇”号火星车可以在极端的火星环境下全自动地进行工作。
编程的直接产物是代码。编程的含义很简单,就是用代码的方式告诉计算机它要做什么。但是编程这件事情并不简单。因为计算机并不是人类,它本身只能做一些如加法、减法这类容易的事。要让只会做加法、减法的计算机完成复杂的、高难度的任务本身就很有挑战性,这就需要我们当翻译。所以编程的挑战性就在于我们要完成理解、解答和翻译的工作。
我们通过编程解决一个问题时,并不是直接把这个问题“扔”给计算机,而是自己先理解这个问题,思考这个问题的解决方法,再将解决方法翻译成代码,最后让计算机执行代码。所以编程的难点不在于代码本身,而在于清晰地思考问题的解决方法。这也是人类最核心的能力之一。
一般来说,编程需要解决某个具体的问题。编程的过程通常分为如下4个阶段。
■ 分析:我们要解决的问题是什么?用户需要什么?
■ 设计:我们如何解决这个问题?整体的系统结构应该是什么样的?应该包括哪些功能模块?这些模块间如何协同交互?系统如何与用户交互?
■ 编程:将设计方案用代码的形式进行表达,代码编写要满足包括时间、成本在内的约束条件,确保代码正确且可维护。
■ 测试:保证软件能正常、可靠地工作。
这4个阶段并不是完全独立或串行的,有时在测试阶段发现的问题需要重新分析,需要我们重新设计并优化代码。编程是一个不断试错、不断迭代的过程。
编程过程中,人类需要思考问题的解决方法,再告诉计算机如何工作。那么,计算机能否自主思考和工作呢?我们能否创造出和人类一样聪明的机器,帮助我们思考和决策呢?随着这类思潮的涌起,AI应运而生。
AI是人类最伟大的梦想之一。很多人对AI的了解主要来自电影中的角色,例如《2001太空漫游》中的AI计算机管家HAL9000、《终结者》中的杀手机器人,以及《钢铁侠》中钢铁侠的战衣AI。这些电影中的AI具备一些共同特征:能力异常强大,和人一样会思考,甚至有感情。现实世界中,AI的能力也在突飞猛进。例如扫地机器人可以自动帮我们打扫房间,ChatGPT可以和人愉快地聊天,无人汽车能在一定程度上自动驾驶。
让计算机像人类一样思考和行动,这个目标无法直接通过编程达成。但是我们可以通过编程让计算机完成很多超级复杂的任务,例如排序、搜索等任务,然而有些任务从人类的角度来看很容易,从计算机编程的角度来看却很困难。例如识别一张图片中是否包括人脸、判断一句话的潜台词,或者创作一幅油画。在后续章节中我们会了解到,AI程序的“智能”是通过数据进行学习、训练得到的,这个学习、训练的过程是通过编程实现的。
AI的概念也是不断发展的。
学术界对AI的看法更富有哲学意味,他们希望AI最终能理解人类思考的本质,而且希望能对AI的思考过程进行建模。他们认为AI需要像人类一样思考和行动,完全地模仿人类。但是,人类建造的飞机最终能飞上天,依赖的并不是对鸟的模仿,而是对空气动力学的研究。
工业界对AI的看法更侧重实用性,他们认为AI能完成特定的任务就不错了。AI需要能够自主操作、感知环境、长期持续地适应变化并实现最佳期望结果,也就是以最优的方法完成一个既定的任务。所以AI需要通过圆满完成任务来体现其能力。
早期的AI研究专注于专家系统。一个专家系统就是一个数据库,其中存放了大量的规则用于判断、决策。但在实际应用的复杂场景下,专家系统遇到了挫折。在1990年前后,以概率统计理论为基石的神经网络方法出现了。最近10年,随着计算机硬件技术、开源算法的发展和互联网数据规模的大幅提升,深度学习迅速发展。它促进了图像识别和自然语言处理等领域算法的进步。借助深度学习的力量,深度强化学习也强势崛起,并有望在现实世界中推动AI得到广泛应用。
游戏编程用代码构建一个虚拟世界。在这个虚拟世界中,玩家能开心地进行游戏。从技术角度而言,电子游戏指一种多媒体的交互式的实时模拟器,它由若干功能模块构成。例如,要将游戏内容展示给玩家,需要图形显示模块;要将游戏音乐播放给玩家,需要音频系统模块;此外,还需要负责和玩家进行交互的界面模块和AI模块等。我们先介绍游戏编程中的一些特有要素。
除非用户中断游戏,否则游戏程序需要一直运行。所以游戏程序中必然存在一个循环,循环中的代码反复运行。这个循环称为游戏循环(game loop)。每运行一次循环中的代码,称为游戏中的一帧(frame)。基于人类眼球的生理特性,游戏一般采用30或者60的帧率来运行,设置游戏帧率为30,就意味着每秒运行30次循环,换句话说,单次循环消耗的时间要控制在33毫秒左右,不能过多也不能过少。游戏中能否达到这个帧率依赖于很多因素,如果硬件性能很强大,代码中的计算很简单,一个循环中的代码可能只需要几毫秒就运行完毕。
游戏循环中应该包括主要的游戏逻辑代码,例如处理玩家输入、更新游戏数据、生成游戏输出这3个阶段的代码。以经典的射击游戏《坦克大战》为例,玩家输入可能是键盘的按键,即通过键盘控制方向和发射炮弹,当用户按下发射炮弹对应的按键时,程序要保存并处理这个信息。在更新游戏数据阶段,程序要根据用户的输入信息,在内存中生成炮弹对象,如果之前已经发射了炮弹,则需要判断炮弹是否击中敌人,敌人是否被击杀。这里可能要处理上百个对象和多种游戏逻辑。在生成游戏输出阶段,最重要的事情之一就是绘制显示在屏幕上的内容,如坦克、发射的炮弹、爆炸效果等。
游戏角色需要显示在屏幕的某个位置上,这个位置对应的坐标系原点一般在屏幕左上角,坐标的单位是像素点。如图1-1所示,我们建立了一个600px×300px的屏幕窗口,在窗口的左上角位置放置了一个边长为100px的正方形,正方形的左上角坐标(0,0)就是原点。我们也可以通过设置左上角或其他位置的坐标来控制正方形的位置。
图1-1
游戏角色(Spirit)一般由两个要素构成,一个是角色的“外皮”,即用来表现视觉效果的图片文件,另一个是角色的“骨架”,即构成图片外部边缘的边框。角色的图片文件需要在初始化时加载,将其加载到内存后,可以获取其边框对象,然后通过设置边框的坐标值来控制角色的位置。
最后需要注意的是缓冲机制。在游戏主循环中,游戏角色生成后并不会被直接输出到游戏界面窗口中,而是先输出到显卡的内存缓冲区,计算机再将内存缓冲区的游戏角色信息更新并输出到屏幕上。就像在话剧表演中,帷幕落下后,后台人员忙着摆放场景道具;将这些场景道具放置妥当后,帷幕升起。这样做的好处是不会将尚未准备好的舞台展示给观众,而帷幕遮挡的舞台就相当于存放游戏角色信息的内存缓冲区。
游戏编程的一个特点是编写的程序需要和用户交互。用户的输入包含各种各样的信息,例如键盘按键信息、鼠标移动或单击信息,甚至游戏手柄的按键信息等。这些信息通常是通过事件队列存放的,在每次运行游戏主循环中的代码时,程序都需要从事件队列中取出需要的信息进行处理。
举个例子,你是一位在战场上统领大军的将军,你需要关注敌方部队有没有发生移动。于是你每个小时开始时都会派出一队侦察兵,让他们分散在战场四周获取情报,侦察兵会在每个小时结束时将情报传回你的手中。有的情报是关于天气变化的,有的情报是关于敌人动向的,有的情报是关于粮草供应的。每份情报都被写在一张纸上,按顺序叠放在桌上。你的参谋会每小时看一次情报,检查这些情报中是否有关于敌人动向的信息。当参谋发现这一类情报后,你会基于相关情报来调动部队出击。
游戏编程中的输入处理与此类似。每一帧里,程序都会收到各种输入事件,并将它们按顺序存放在一个事件队列中。程序会不停地检查这个队列,当程序“关注”的事件发生时,例如玩家按下了空格键,程序就会运行相应的代码来发射炮弹。
AI能给予游戏更多的乐趣。从工程角度来看,游戏AI并不是越复杂越好,而是能满足游戏的需要。AI领域有一个黄金原则——搜索和知识是相互关联的,当你拥有更多的知识,你就需要更少的搜索;当你拥有更少的知识,你就需要更多的搜索。一个职业围棋选手拥有相当丰富的对弈知识,通常只需针对几个落子选项进行少量的计算。而初版的AlphaGo,在没有足够丰富的对弈知识的背景下,只能反复在海量选项中进行最优落子方案的搜索。
最早的《吃豆人》游戏中,有4个追逐玩家的幽灵角色,其AI设置相当简单。它们追逐玩家的路线都是通过硬编码的规则确定好的,有的是直接追逐玩家,有的是占领某个交通要道,有的是随机选择某条通道。这套游戏AI编写得很简单,但整体的配合却让玩家感觉很智能,因为简单的规则中包含相当丰富的游戏对抗的知识。因此,需要视情况采用复杂程度不同的游戏AI。有时候,简单的随机方法或者贪心算法就可以模拟出一个不错的NPC。
本书将主要介绍游戏中会用到的3种AI算法,分别是深度强化学习、遗传算法和蒙特卡罗树搜索。深度强化学习(deep reinforcement learning)是深度学习与强化学习相结合的产物,它集成了深度学习在函数拟合上的强大能力,以及强化学习基于环境反馈进行试错和决策的能力。深度强化学习可用来解决现实场景中的复杂问题。遗传算法(genetic algorithm)是计算数学中用于解决最优化问题的搜索算法,是进化算法的一种。进化算法是借鉴进化生物学中的一些原理而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等。蒙特卡罗树搜索(Monte Carlo tree search)是一种用于决策过程的启发式搜索算法,它最引人注目的使用场景之一是被用在AlphaGo中。它也用于其他棋牌类等即时电子游戏和不确定性游戏。
在古印度神话中,人类世界存在于一只巨龟的背上。今天,无数的代码构成人类世界的数字巨龟。通过编程,可将人类的智慧凝结到代码中,让计算机为我们服务。学习编程不只是学习编程语言本身,其核心是学习思考和解决问题的方法,并将其翻译成代码。AI是另一种更有趣的思路,我们编写的程序中包含更广泛的知识,AI程序可以更聪明地解决问题。
电子游戏不仅能让人放松和开心,还能提升人类社会的生产力。本书将编程的主要领域聚焦在电子游戏,即游戏编程。不同于其他领域,游戏编程有一些重要的东西要考虑,例如游戏主循环、图形显示、输入处理和游戏AI。第3章将进入游戏编程的实操环节,基于Python的游戏模块Pygame来介绍如何进行游戏编程。在此之前,我们会在第2章做一些热身活动,学习一些必要的Python知识。