西安成品网站建设,提高审美网站,国外做兼职的网站,专业的门户网站建设方案python 基础系列篇#xff1a;七、以函数方式编写一个数字华容道 数字华容道游戏分析开始编写完整代码代码解说定义方法的规律 小结 数字华容道
嗯#xff0c;就是一个简单的益智游戏#xff0c;把数字按照特定规律排列#xff0c;并比矩阵少一个格#xff0c;用来进行移… python 基础系列篇七、以函数方式编写一个数字华容道 数字华容道游戏分析开始编写完整代码代码解说定义方法的规律 小结 数字华容道
嗯就是一个简单的益智游戏把数字按照特定规律排列并比矩阵少一个格用来进行移动。
具体游戏方式就不细说了还不了解的可以自行百度一下。
正好老顾最近是没有什么灵感不知道用什么举例来讲解一下怎么去划分函数减少工作然后就在昨天问答有小伙伴问到了数字华容道的问题。然后老顾就决定用这个做个例子来讲解。 CSDN 文盲老顾的博客 https://blog.csdn.net/supewrfei 游戏分析
老规矩我们先分析一下需要完成的内容有哪些。既然是做数字华容道我们先列举一下这个游戏需要什么。 1、在一个特定长和宽的矩阵里有 长 乘 宽 减 一 个可移动的方块 2、只有空位周边的方块可移动至空位 3、游戏开始时方块顺序是混乱的 4、指定一个最终结果作为胜利条件如不指定则以横向连续为胜利条件 5、用户可以通过上下左右wsad来移动方块 在游戏需求列完了我们再列举一下我们需要做哪些准备 1、可由用户指定游戏区域的宽和高 2、每个可移动块要标记一个记号 3、接收用户输入的移动方向 4、记录移动步数 5、生成胜利指定的最终结果用以判定胜利条件 开始编写
相信已经看过 2048 的小伙伴对这个感觉非常熟悉了。没错大部分的内容可能会与 2048 有些重合但今天的内容使用函数才是重点哦。
我们先规划一下我们应该需要定义哪些方法 1、用以定义长和宽的用户输入部分 2、用以显示游戏界面的部分 3、用来进行游戏时接收用户输入的部分及移动 4、用来进行胜利判定的部分 5、用来进行游戏衔接的一些内容比如是否开始新游戏是否退出游戏等 完整代码
任何游戏都有一个进入游戏、开始游戏的指令。由于我们是在 python 开发环境里写所以进入游戏就省略了只写一个开始即可。在开发环境外就用 python 指定运行文件的方式来进入游戏即可。
这次老顾就先放出完整代码然后再进行讲解。边写代码边写博客讲解有点费劲了。
import sys
import re
import random
import copy# 用来呈现用户界面
def ShowBoard(data):# 用户界面用到的制表符─┐┌└┘├┤┬┴┼│# 根据用户定义的区域大小来确定最大数字的长度每个数字占用位置以此为依据计算length len(str(data[blank])) 1# 输出区域顶部边界依据是字符占位宽度 length 和 横向数字数量 data[width]print((┌ ((─ * length) ┬) * data[width])[:-1] ┐)for i in range(data[height]):# 输出每行的数字信息print(│ │.join([str(v).rjust(length) if v ! data[blank] else * length for v in data[board][i * data[width]:(i 1) * data[width]]]) │)if i data[height] - 1:# 如果不是最后一行输出间隔行print((├ ((─ * length) ┼) * data[width])[:-1] ┤)else:# 输出底部边界print((└ ((─ * length) ┴) * data[width])[:-1] ┘)# 对游戏对象进行数据填充
def InitBoard(data):sys.stdout.flush()inp input(如果想更改区域大小请输入两个数字以空格分开)if re.fullmatch(\s*\d\s\d\s*,inp):data[width],data[height] map(int,inp.split())data[blank] data[width] * data[height]data[success] list(range(1, data[blank] 1))data[board] copy.deepcopy(data[success])random.shuffle(data[board])# 是否新开游戏
def NewGame():a while a not in yYnN or len(a) 1:sys.stdout.flush()a input(是否开始新游戏Y/N)return a in yY# 游戏主线程
def GetInput(data):ShowBoard(data)sys.stdout.flush()arrow input(请选择方向上w下s左a右d退出q).lower()if arrow q:return True# 得到当前空位所在的位置blank data[board].index(data[blank])if arrow w and blank // data[width] data[height] - 1:data[steps] 1data[board][blank],data[board][blank data[width]] data[board][blank data[width]],data[board][blank]if arrow a and blank % data[width] data[width] - 1:data[steps] 1data[board][blank],data[board][blank 1] data[board][blank 1],data[board][blank]if arrow s and blank // data[width] 0:data[steps] 1data[board][blank],data[board][blank - data[width]] data[board][blank - data[width]],data[board][blank]if arrow d and blank % data[width] 0:data[steps] 1data[board][blank],data[board][blank - 1] data[board][blank - 1],data[board][blank]if data[board] data[success]:print(你用了{}步取得了胜利。.format(data[steps]))return Truedef HuaRongDao():while True:data {width : 4,height : 4,board : [],steps : 0}if not NewGame():returnInitBoard(data)while True:if GetInput(data):breakif __name__ __main__:HuaRongDao()
代码解说
首先我们在 HuaRongDao 方法里定义了一个死循环通过死循环来保证用户不会跳出游戏。每一次循环表示一轮新游戏。 data {…} 然后在循环里定义了一个初始字典用来存放游戏数据。每轮的数据都需要重新定义。
在初始化游戏字典后我们询问用户是否进行新游戏如果不进行则跳出。 data[‘width’],data[‘height’] 在初始化游戏界面的方法 InitBoard 里我们允许用户输入两个整数来改变游戏区域大小。毕竟是小游戏打发时间的可以自行加难度。而我们默认的初始难度是 4 * 4 算是很简单的了。 data[‘blank’] data[‘width’] * data[‘height’] 不管用户是否改变区域大小我们之后的内容就是根据区域大小来填充游戏数据了先确定最大数字是多少将这个数字定义为空位。 data[‘success’] list(range(1, data[‘blank’] 1)) 然后生成一个连续序列表示胜利时的状态。 data[‘board’] copy.deepcopy(data[‘success’]) 再然后用深拷贝复制一个胜利状态的数据。 random.shuffle(data[‘board’]) 最后用随机洗牌函数将用户需要进行操作的数据打乱。
至此游戏初始化内容完成可以进行游戏了。
在这里除了最初的 data 定义其他都放在了 InitBoard 方法里。
其实最初的定义也可以放到 InitBoard 然后 return data在 之前定义的循环里接收这个结果。老顾随手写的是这样就不修改了。
因为字典是一个引用型对象所以我们通过传递 data 这个对象并直接修改这个对象是相当于在原有对象上操作的不用担心我操作的内容会丢失。
在初始化结束后就是正式的用户交互部分了我们定义了一个 GetInput 的方法。
而在用户输入信息前调用了一个 ShowBoard 方法用来显示游戏当前界面。
在现实了界面后用户才会知道自己应该怎么移动。
在输入部分限定一下输入内容并允许跳出游戏。即只接收 asdwq 5个字符其他字符视为无效。 blank data[‘board’].index(data[‘blank’]) 然后就是根据空位的信息来验证是否移动方式可行。 data[‘steps’] 1 如果可行则移动步数加一。在这里老顾定义的方向也不知道是否符合大家的习惯如果不习惯可以将 ws 互调ad 互调。 if data[‘board’] data[‘success’]: 最后用户移动完成时验证是否胜利。
这样一个简单的数字华容道就完成了。
定义方法的规律
在我们定义的这几个方法里HuaRongDao 的使用频率是最低的他相当于游戏的主控线程。定义这个主要是为了方便外部执行不产生冲突。
其次频率倒数第二低的就是 NewGame 和 InitBoard 了每新开一轮游戏才调用一次如果玩上一下午调用次数还是不少的。
再然后就是调用频率最高的 GetInput 了还有同样频率的 ShowBoard。
至于为什么分成两个一个是管输出一个是管输入控制分开的话逻辑就更清晰维护更方便罢了。
最后老顾在 GetInput 的时候有一些情况下具有了一个 True 的返回值在这个代码里这个返回值就代表了游戏结束哦不管是胜利还是退出对游戏逻辑来说都是一样的只是对用户反馈信息不一样罢了。
那么大体上的朴素逻辑就出来了就是需要多次运行的内容做成函数或方法不同使用频率的则做成不同的方法。而不同用途的或者不同功能性的也尽量拆分开做成不同的方法这样后续维护也很容易定位。
在本文中老顾就是一个举例具体到实际每个人都有自己的定义方法的习惯不用照抄老顾的习惯哦。
小结
这次我们通过一个完整的示例代码来了解了函数、方法的使用方式以及朴素规律后边我们就可以自行发挥培养自己的代码风格和书写习惯了。
多读别人的代码是培养代码风格和熟悉习惯的办法之一。
然后今天引用的几个包再说明一下 1、sys 包 主要使用 sys.stdout.flush() 避免用户输入信息提示串行造成用户输入信息时无响应 2、re 包 用正则方式判断用户是否输入了两个整数来确定是否需要变更区域大小。如果不用正则方式那么用户输入信息的可能性太多做起验证也很麻烦。 3、random 包 所有使用随机数的代码都会用到本文主要用到 random.shuffle对迭代对象进行打乱洗牌处理。 4、copy 包 在复制引用类型的数据时应该使用深拷贝否则你可能引用的是同一个对象最后发现数据全乱套了。 那么今天就到这里大家晚安。