大家好,欢迎来到Python实战的话题。我们今天还实现了一个小游戏。这个小游戏非常出名,我想大家应该都玩过吧。就是tictactoe,我们打开chrome搜索一下就可以直接找到这个游戏。由于我们使用Python来实现,不会做UI界面,所以看起来不会那么好看。虽然不好看,但是道理是一样的。而且和我们之前做的那些小游戏相比,我们今天做的这个游戏有一个非常大的特点,就是非常适合设计AI。我们只需要用一个非常简单的算法就可以做出一个相当不错的人工智能。当然,我们是循序渐进的,从最简单的游戏功能本身开始。题目今天的题目是用Python写一个没有UI界面的井字游戏。这一次,博弈会涉及到两方,所以我们需要有相关的逻辑来判断博弈的胜负。除此之外,由于涉及两个玩家,我们需要设计一个AI来让我们与计算机对战。最终的效果应该是这样的:即在游戏开始时,支持玩家选择两方参与游戏。这里我们先抛开AI算法的设计,我们可以先做一个随机选择的弱智AI。游戏开始后,双方交替动作,每次执行都会在屏幕上输出相应的具体信息,以及当前的棋盘情况。知识点面向对象的tictac游戏虽然简单,但是涉及的内容却很多。你需要一个棋盘,你需要玩家,你需要添加玩家和执行步骤,等等。如果不把这些逻辑封装起来,全部写成面向过程的,代码会很混乱。很显然,我们需要用面向对象的方式来对这些逻辑进行抽象和封装,以简化编码和思维。我们现在的设计比较简单,不需要使用继承、抽象类等高端用法,只需要使用最基本的面向对象定义类即可。在Python中定义一个类非常简单,使用关键字class即可完成。例如:classGame:passconstructor一般来说,当我们定义一个类的时候,我们需要为它设计一个构造函数。构造函数是我们创建此类的实例时调用的方法。它会为我们做一些初始化工作。Python中类的构造函数是__init__,我们可以直接在类中实现。classGame:def__init__(self):self.board=Board()self.players=[]self.markers=['O','X']self.numbers=[1,-1]比如上面的例子,我们对Game类做了一些初始化设置。例如,给它一个变量,例如board和players。类方法既然是类,自然会有属于类的类方法。类方法的定义和普通函数的定义一样,唯一的区别是写在类内部,第一个参数默认为self。self关键字相当于Java中的this,指的是运行时调用该方法的实例。例如,我们实现了一个将玩家添加到Game类的方法:classGame:def__init__(self):self.board=Board()self.players=[]self.markers=['O','X']self。numbers=[1,-1]defadd_player(self,player):ifplayer=='h'orplayer=='human':self.players.append(HumanPlayer())elifplayer=='r'orplayer=='random':self.players.append(RandomPlayer())让我们看看add_player方法的内部逻辑。在这个方法中,我们通过self关键字调用类实例中的变量。这就是为什么我们需要设置一个self参数。我们在调用的时候不需要关注self参数,Python会自动为我们填充。当然,我们在定义类方法的时候,也可以定义一个不带self参数的方法,但是这样的方法不再属于类的实例,而是属于类本身。如果我们要调用,只能通过类名来访问。例如:classTest:defsay():print("helloworld")在Test类中,我们实现了一个没有self关键字的say方法。如果我们通过Test实例来调用,肯定会出错。因为当我们通过实例调用方法时,Python会自动为我们传入实例作为第一个参数。这样就导致接收和发送的参数不兼容,所以报错。如果我们要调用这个say方法,应该是这样的:Test.say()也就是说,这个方法不再属于类创建的实例,而是属于类本身。可以理解为Java类中通过static关键字修饰的方法。方法的方法Python中方法的定义相对灵活。我们可以为一个类创建方法,也可以在方法内部创建另一个方法。比如下面这个例子:defouter(arg1,arg2):definner(arg1,arg2):returnarg1+arg2returninner(arg1,arg2)由于Python支持函数式编程,方法内部的方法也可以实现闭包、装饰器等等功能.不过这里我们不用这么高端的用法,我们只需要知道最基本的就可以了。最基本的就是在函数内部定义一个函数,主要是在函数内部,定义在外部的变量就可以使用了。例如:defouter(arg1):arg2=10definner(arg1):returnarg1+arg2returninner(arg1)上面的代码没有问题,但是还有一点要注意。虽然可以在内部访问外部定义的参数和变量,但不能修改它们。如果要修改,需要使用nonlocal关键字声明这是一个外层变量。例如:defouter(arg1):arg2=10definner(arg1):nonlocalarg2arg2+=1returnarg1+arg2returninner(arg1)通过在方法中实现方法,可以进一步简化函数内部代码的逻辑,一些非常复杂的函数功能可以进一步拆分和简化。理解这个用法也是学习闭包、函数式编程等高级内容的基础。最后,与前面几篇相比,本题整体实现难度相差不大,主要是涉及到的Python文件较多,而前面几篇都是单文件运行的Python程序。这次需要写多个文件,而且这次引入了面向对象的概念,需要抽象一些功能。所以总的来说,还是有难度的。不会做的可以点击查看原文,获取我的github地址。在这个项目中,我们毫不费力地创建了最简单的随机选择的AI。在后面的题目中,我们会用到一些AI算法,给它加一些AI,让它变得聪明,甚至无敌。本文转载自微信公众号“TechFlow”,作者梁堂。转载请联系TechFlow公众号。
