第一章:编程、游戏和AI
一、代码统治世界
现代社会中,代码无处不在。让我们设想你生活中的一天。早上,智能手机响起铃声将你从梦中唤醒,其闹钟程序乃至于底层系统都是代码构建的。洗漱完毕后,你准备搭乘出租或地铁去工作。整个交通工具的调度系统都是由代码构建的。汽车的自动稳定系统,或者更智能的自动驾驶系统,都是由代码驱动的。到达公司里,你打开电脑,浏览最近重要的新闻信息。这些新闻信息都是由代码构建的推荐系统生成的。
除了以上的生活场景,代码也推动着整个世界的生产力向上提升。
制造领域,以制造大型客机为例,设计图纸已经转变成数字化形态,设计工程师在三维数字世界里设计飞机。在软件控制的自动生产线上,智能机械手臂根据数字图纸实现飞机的装配建模。飞行员使用自动驾驶软件来控制飞机,隐藏在背后的数百万行代码不断感知飞机姿态,控制进气量、发动机功率和机翼的角度。
信息领域,以搜索引擎为例,谷歌每天要支持接受数十亿次的搜索查询,其分布在世界各地的数据中心功不可没。数据中心的智能系统有一整套分布式并行集群架构,支持其先进的大数据管理和处理,而且通过数据中心部署的机器学习能力,谷歌可以很好的控制其冷却系统的能源消耗,对环境更为友好。
医疗领域,以影像检测为例,基于人工神经网络的人工智能,在检测乳腺癌症状及其他状况方面,与人类放射科医师表现同样优秀。 除了帮助临床医生发现疾病的早期症状,人工智能还可帮助处理跟踪各种数量惊人的医学影像,并通过检测患者病历中的重要资料,将相关影像提供给临床医生,实现更好的临床管理。
科研领域,以火星探索为例,登陆火星的“好奇”号火星车,其系统含有六大部件。它们需要协同整合。在全部自动化过程中,不能出现一丝差错,对于失误完全是“零容忍”。整个系统的整合,一共使用了50万行计算机代码,令其可以在极端的火星环境下全自动地进行工作,帮助人类走向星辰大海。
二、什么是编程
代码是编程的产物。编程的含义很简单,就是用代码的方式告诉计算机它要做什么。但是编程这件事情并不简单。因为计算机并不是人类,它本身只能干一些加减法一类容易的事。问题就在于,要让只会做加减法的计算机完成复杂高难度的任务,这就很有挑战。这就需要我们人类来当翻译。所以编程的挑战就在于我们要去理解、解答和翻译。
当我们需要编程去完成一个任务时,并不是直接把这个任务扔给计算机。而是我们自己先理解这个问题,思考这个问题的解决方法,再将解决思路翻译成代码,最后让计算机执行完成。所以编程的难点不在于代码语言本身,而在于清晰的思考问题。这也是作为人类最核心的能力。
我们一般是帮助用户来解决某个问题,整个编程的过程分为四个阶段。
- 分析,我们要解决的问题是什么?用户需要什么?
- 设计,我们如何来解决这个问题?整体的系统结构应该是什么样的?应该包括哪些功能模块?这些模块间如何协同交互?系统如何与用户交互?
- 编程,将设计方案用代码的形式进行表达,代码编写要满足时间、成本在内的约束条件,确保代码正确且可维护。
- 测试,保证软件工作正常可靠。
这四个阶段并不是完全独立或串行的,有时候在测试阶段发现一些问题反馈,我们会重新分析问题,重新设计优化代码。编程是一个不断试错,不断迭代的过程。
三、什么是人工智能
编程是需要人类先思考再告诉计算机如何工作。那么,计算机能否自主思考和工作呢?我们能否创造出和人类一样聪明的机器,帮助我们思考和决策呢?人工智能应运而生。人工智能(Artificial Intelligence)也简称为AI,是人类最伟大的梦想之一。普通人对人工智能的了解恐怕还是主要来自于电影中的角色,例如在“2001太空漫游”电影中的AI电脑管家HAL,“终结者”系列电影中的杀手机器人,以及“钢铁侠”电影中的战衣AI。电影中的这些人工智能都具备一些共同特征:能力异常强大,和人一样会思考,甚至会有感情。现实世界中的AI能力也在突飞猛进。例如可以自动帮我们打扫房间的扫地机器人,可以和人愉快聊天的ChatGPT,以及能在一定程度上自主驾驶的无人汽车。
就让计算机像人类一样思考和行动,这个目标没法直接通过编程来完成。我们可以通过编程让计算机完成很多超级复杂的任务,例如排序,搜索等工作。但是有些任务在人类看来很容易,在计算机编程角度来看却很困难。例如识别一张图片中是否包括人脸,判断一句话的潜台词,或是创造一幅油画。在后续章节我们会了解,人工智能程序是通过数据进行学习训练得到的,这个AI训练的过程是我们编程实现的。
人工智能的概念也是不断发展的。学术界对人工智能的看法会更富有哲学意味,他们希望人工智能最终能理解人类思考的本质,而且能对思考过程进行建模。 认为人工智能需要像人一样思考和行动,完全的模仿人类。但是,人类建造的飞机最终能飞上天,依赖是并不是对鸟的模仿,而是对空气动力学的研究。工业界对人工智能的看法更侧重实用,他们觉得人工智能完成人类擅长的任务就不错了。人工智能需要能够自主的操作、感知环境、长期持续、适应变化并实现最佳期望结果,也就是最优的完成一个即定的目标。所以人工智能需要通过圆满完成任务来检验其能力。
早期的人工智能研究专注于“专家系统”。一个专家系统就是一个数据库,其中存放了大量的规则用以判断决策。但在复杂场景下,专家系统遇到实际应用的挫折。于是在1990年左右,以概率统计理论为基石的神经网络方法出现了。最近十年,随着计算机硬件、开源算法和互联网数据规模的大幅提升,深度学习大行其道。它促进了图像识别和自然语言处理等领域的算法进步。借助深度学习的力量,深度强化学习也强势崛起,它有望在现实世界中推动人工智能得到广泛应用。
四、游戏意味着什么
游戏的历史
人类的所有活动,不是在谋生,就是在游戏。自有历史记录以来,人类就在玩游戏。古希腊作家希罗多德的著作中记载了一个小故事。大约三千年前,吕底亚人生活在小亚细亚,有一年全国出现了大饥荒,人们苦不堪言。于是有人发明了一种奇怪的东西来让生活更好受一点。这是一种用动物关节制造的骰子游戏。他们经常用一整天的时间来玩这种游戏,避免进食以节约粮食,就这样熬过了饥荒的岁月。
后来考古学家也发现了各种不同的人类游戏,例如在美索不达米亚发现了古老的乌尔皇家游戏。它是一种古老的棋盘游戏。 这个游戏至少有 4500 年的历史,这是一款类似于西洋双陆棋的游戏,规则非常简单,但可能具有极其复杂的战略机制。在中世纪时间,撒克逊贵族也开始玩一种类似于国际象棋的游戏。 历史学家不确定国际象棋是在何时何地发明的,但流行的推测认为它的发明是在公元 6 世纪或 7 世纪出现的。 在 10 世纪开始在欧洲流行起来。
在中华文明中,也出现了数千年历史悠久的围棋游戏。相传是尧创造了围棋来教育他的儿子。可信的记载则是在左传中出现的文字,“弈者举棋不定,不胜其耦”,意思是下棋的人如果拿着棋子主意不定,就不能战胜对方。数千年后,AlphaGo横空出世,击败了顶尖的人类棋手,使这个古老游戏在全世界得到关注和传播。
计算机的出现也推动了游戏的发展。最早有记录的电子游戏诞生于1952年,剑桥大学计算机科学家A.S. Douglas开发出了史上第一款电脑游戏《Noughts&Crosses》,也就是“井字棋游戏”。1980年,NAMCO (南梦宫)推出了火遍全球的《吃豆人》游戏。1983年,全球总销售量6000万台的任天堂FC正式问世。从此世界上开始孕育出一种新的文化,电子游戏文化。1994年,划时代的电子游戏机PlayStation问世,同时PC平台上,也出现了《德军总部》和《毁灭战士》,创造了一种前所未有的全新游戏模式。街机游戏也进入了繁荣时期,1991年,《街霸2》横空出世,直接带动了整个街机产业的发展。2000年左右,计算机硬件和互联网的迅速发展带来了网络游戏的一波崛起。2007年,iPhone的问世,给手机产业带来了颠覆性改变,手机游戏市场随之火热起来。
游戏为什么有趣
电子游戏有很多的类型,有让你指挥千军万马的战略游戏,有让你沉浸于田园生活的经营养成游戏,有让你体验精彩人生故事的角色扮演游戏,有让你血脉偾张的动作游戏。哲学家归纳说,玩游戏,就是自愿尝试克服种种不必要的障碍。所有好玩的游戏都可以总结成以下几个特点:
- 目标,每个游戏都有明确的目标,它需要玩家拼尽全力才能达到的结果。
- 规则,每个游戏都有清晰的规则,它限制玩家达成目标的手段和方式。它约束了玩家,提高了难度,推动玩家去探索未知的可能。
- 反馈,每个游戏都有及时的反馈系统,它告诉玩家获得了什么,是成功还是失败,它给了玩家继续玩下去的动力。
- 参与,每个游戏都是自愿参与的,它让所有玩家了解目标和规则,保留继续游戏或离开游戏的自由。
心理学有一门分支称为积极心理学,它专门研究如何让人类过上幸福的生活。其中一项研究发现,幸福的生活离不开心流体验。而当人们去做有意义和有意思的事情时会发生心流。出现心流有如下几个特征:
- 有明确的顶层目标和意义
- 有清晰的阶段性目标和评估反馈机制。
- 目标任务需要有一定挑战性,但不会过分的困难。
- 需要掌握一定的技能来完成任务
- 自己喜欢做这个事情
- 需要专注此事,进入忘我状态。
对比以上两个清单,我们可以看到,游戏之所以有趣,是因为它正好满足了心流的几个特征。人们一旦进入心流状态,就喜欢一直呆在里面。游戏还能唤起我们积极的情感,体验自豪的快感。想象未来的人类世界,也许是天下大同、一个衣食无忧、心灵满足的乌托邦世界,活在其中的人类早已心想事成,对客观世界已经无事可做,剩下唯一能做的就只有玩游戏,玩游戏将变成人类存在理想的全部。
游戏的意义
对于个人来讲,游戏的意义在于满足人的需求。在现实世界里,人们每天进行着重复劳动,难以找到能发挥其潜力的机会,自然难以得到心流体验。在现实世界里,人们也找不到游戏中的力量感、团队协作和成就感。我们和数千年前的饥渴的吕底亚人没什么不同,只不过现代社会人的饥渴并非粮食方面,而是对更满意工作的饥渴,对集体认同的饥渴,对更有意义的人生的饥渴,而游戏则在一定程度上治疗了这种精神饥渴。游戏不仅是一种消遣,它是现实世界的补丁,它给无数普通人带来了幸福感。同时,游戏也是一种极有价值的教育工具,在心流体验中,学习者更能积极参与,培养各种数字技能。
对于社会来讲,游戏的意义还在于提升人类社会的核心生产力。游戏是孕育和壮大芯片和人工智能产业最肥沃的土壤。正是人们对电子游戏的需求推动了计算机图形学和GPU的迅速发展。高品质的3D游戏,驱动了实时、高性能、低成本的算力不断发展,推动高算力显卡的不断研发升级。游戏中的NPC决策,也推动了人工智能的不断进步。2015年,基于深度强化学习的程序已经能够通关大部分的经典街机游戏。2017年,AlphaGo战胜人类顶尖棋手后,宣告了人工智能在棋盘游戏上的统治。不久以后,人工智能已经可以在《Dota 2》和《星际争霸 2 》等经典电竞项目中击败人类职业选手。体现了AI在复杂场景感知、分析决策、长期规划运营等方面的高超能力。
五、游戏编程和游戏AI
游戏编程是用代码的方式来构建一个虚拟世界,也就是电子游戏。在这个虚拟世界中,玩家能开心的在里面玩耍。从技术角度而言,电子游戏是指一种多媒体的交互式的实时模拟器。它是由若干功能模块构成的。例如,要将游戏内容显示给玩家,则需要图形显示模块,要将游戏音乐播放给玩家,则需要音频系统模块。此外,还需要有负责和玩家交互的界面模块和人工智能模块等等。我们先介绍一下游戏编程中的一些特有要素。
游戏主循环
除非用户中断游戏,不然一个游戏程序需要一直运行下去。所以游戏程序中必然会存在一个循环,让循环中的代码反复运行。这个循环称之为游戏主循环(Game Loop)。每运行一次循环中的代码,称之为游戏中的一帧(frame)。基于人类眼球的生理特性,游戏一般采用30或是60的帧率来运行,设置游戏帧率为30,也就意味着每秒运行30次循环,换句话说,单次循环消耗时间要控制在33毫秒左右,不能多也不能少。游戏中能否达到这个帧率依赖很多因素,如果硬件很强,代码计算很简单,一个循环可能只需要几毫秒就会跑完。
游戏主循环中应该包括主要的游戏逻辑代码,例如处理用户的输入,更新游戏数据,生成游戏输出。以经典的射击游戏坦克大战为例。用户输入可能是键盘控制方向和发射炮弹,当用户按下发射按键时。程序要保存处理这个信息。在更新游戏数据阶段,程序要根据用户的输入信息,在内存中生成炮弹对象,如果之前已经有炮弹在路上,需要判断炮弹是否击中敌人,敌人是否被击杀。这里可能要处理上百个对象和多种游戏逻辑。在生成游戏输出的阶段,最重要的就是绘制显示在屏幕上的东西,要绘制坦克,绘制路上的炮弹,绘制爆炸效果等等。
二维图形显示
游戏角色需要显示在屏幕的某个位置上,这个位置坐标的原点一般是在左上角,坐标的单位是像素点。例如下面图例中,我们建立了一个600*300的屏幕窗口,在窗口的左上角位置放置了一个边长为100像素的正方形,正方形的左上角坐标就是原点(0,0)。我们也可以通过设置左上角或其它位置的坐标来控制矩形的位置。
游戏角色(spirit)一般有两个要素构成,一个是角色的“外皮”,即使用什么样的图片文件来表现视觉效果,另一个是角色的“骨架”,即构成图片外部边缘的边框矩形。角色的图片需要在初始化时进行资源加载,加载到内存后,可以获取其边框对象,再设置边框的坐标位置来控制角色的位置。
最后需要注意的是缓冲机制,在游戏主循环中,生成游戏角色并不会直接输出到窗口上,而是会输出到显卡的内存缓冲区中,等到输出到缓冲区完成之后,再会将缓冲区的信息显示更新到屏幕上。就像是话剧表演的时候,幕布关闭时,后台人员忙着摆放场景道具。一起放置妥当后,幕布拉起,将场景展示给了观众,这样的好处是不会将没准备好的舞台给观众看。
输入处理
游戏编程的一个特点是需要和用户交互。用户会输入各种各样的信息,例如键盘按键,鼠标移动或点击,甚至是游戏手柄的按键输入等等。这些信息通常是通过一个事件队列存放的,在游戏主循环中,每次循环中,程序需要从事件队列中取出关注的信息进行处理。
举个例子,你是一位战场上统领大军的将军,你需要关注的敌人部队有没有发生移动。于是你每个小时的整点时都会派出一队侦查兵,散在战场四周去获取情报,然后侦查兵会在每个小时结束时将情报传回到你的手中。有的情报是关于天气变化的,有的情报是关于敌人动向的,有的情报是关于粮草供应的。每份情报都被写在一张纸上,按顺序叠放在桌上。你的参谋会每小时看一次情报,检查某中是否有关于敌人移动的情报。当参谋发现对应的情报后,你会基于这份情报来调动部队出击。
游戏编程中是类似的,每一帧里,我们都会收集到各种输入事件,按顺序存放在一个队列中。程序会不停的检查这个队列,当我们关心的事件发生时,例如玩家接下了空格键,这个特别的事件就满足了判断条件。主循环接下来的代码就会发射炮弹。
游戏AI
AI能给予游戏更多的乐趣。从工程角度来看,游戏AI并不是越复杂越好,重要的是能满足游戏的需要。AI领域有一个黄金原则,“搜索和知识是相互关联的,当你拥有更多的知识,你就需要更少的搜索,当你拥有更少的知识,你就需要更多的搜索。”一个人类围棋职业选手,拥有相当丰富的对弈知识,在某个局面下只需针对几个落子选项进行少量的计算。而初版的AlphaGo,没有什么对弈知识背景下,只能反复在海量选项下进行最优落子的搜索。
最早的吃豆人游戏中,有四个追逐玩家的幽灵角色,其AI设置相当简单。它们追逐玩家的路线都是通过硬编码的规则确定好的,有的是直接追逐玩家角色,有的是去占领某个交通要道,有的是随机选择某条通路。这套游戏AI编写的很简单,但整体的配合却让玩家感觉很有智能,因为简单的规则中包括了相当丰富的游戏对抗的知识。因此,需要视情况采用不同复杂程度的游戏AI。有时候,简单的的随机方法或是贪心算法就可以模拟出一个不错的NPC出来。
本书将主要介绍三种游戏中会用到的AI算法,分别是深度强化学习、遗传算法和蒙特卡洛树搜索。深度强化学习(Deep Reinforcement Learning)是深度学习与强化学习相结合的产物,它集成了深度学习在函数拟合上的强大能力,以及强化学习基于环境反馈进行试错和决策的能力。深度强化学习的出现得以解决现实场景中的复杂问题。遗传算法(Genetic Algorithm)是计算数学中用于解决最优化的搜索算法,是进化算法的一种。进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等等。蒙特卡洛树搜索(Monte Carlo tree search)是一种用于决策过程的启发式搜索算法,最引人注目的是在AlphaGo中的使用。它也用于其他棋盘游戏、即时电子游戏以及不确定性游戏。
本章小结
古印度的神话中说,人类世界是存在于一只巨龟的背上。实际上,是无数的代码构成了现代人类世界的数字巨龟。我们通过编程,将人类的智慧凝结到代码中,让计算机为我们服务。学习编程不止是学习编程语言本身,核心是学习思考解决问题的方法,并将其翻译成代码。人工智能是另一种更有趣的思路,我们编写的程序中包含了更广泛的知识,AI程序可以更聪明的解决问题。
有史以来,人类就在玩游戏。在现代社会,电子游戏意义重大,它不仅能满足人类个人的幸福感,还能提升人类社会的生产力。所以我们会将编程的主要领域聚焦在电子游戏领域,即游戏编程。不同于其它领域,游戏编程有一些重要的东西要考虑,例如游戏主循环、图形显示、输入处理和游戏AI。在第三章我们将进入游戏编程的实操环节,基于Python的游戏模块PyGame来学习如何进行游戏编程。在此之前,我们会在第二章做一些热身活动,复习一些必要的Python知识。