python 基础系列篇:七、以函数方式编写一个数字华容道

news2024/11/25 7:10:18

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'] += 1
        data['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'] += 1
        data['board'][blank],data['board'][blank + 1] = data['board'][blank + 1],data['board'][blank]
    if arrow == 's' and blank // data['width'] > 0:
        data['steps'] += 1
        data['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'] += 1
        data['board'][blank],data['board'][blank - 1] = data['board'][blank - 1],data['board'][blank]
    if data['board'] == data['success']:
        print('你用了{}步,取得了胜利。'.format(data['steps']))
        return True

def HuaRongDao():
    while True:
        data = {
            'width' : 4,
            'height' : 4,
            'board' : [],
            'steps' : 0
            }
        if not NewGame():
            return
        InitBoard(data)
        while True:
            if GetInput(data):
                break

if __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 包
在复制引用类型的数据时,应该使用深拷贝,否则你可能引用的是同一个对象,最后发现数据全乱套了。

那么,今天就到这里,大家晚安。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/459471.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CRM客户关系管理系统主要有哪些功能?

一、CRM客户管理系统是什么 客户关系管理&#xff08;Customer Relationship Management&#xff0c;简称CRM&#xff09;&#xff0c;是指企业为提高核心竞争力&#xff0c;利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互&#xff0c;从而提升…

将CSDN博客内容转为PDF进行下载

打开博客文章页面–F12–控制台–输入以下代码-回车–选择“另存为PDF”–设置样式并预览–打印 回车之后需要等待一些时间 设置之后导出即可 (function(){ use strict;var articleBox $("div.article_content");articleBox.removeAttr("style");…

超低延时交换机助力金融证券极速交易场景应用

一、 极速交易技术的兴起 随着计算机技术和金融科技的快速发展&#xff0c;量化交易和高频交易在全球金融市场中已经被运用到各种交易场景&#xff0c;特别是在股票&#xff0c;期货&#xff0c;期权等衍生品市场&#xff0c;已经逐渐取代人工做市&#xff08;market maker)&am…

Android 动画—补间动画

帧动画是通过连续播放图片来模拟动画效果&#xff0c;而补间动画开发者只需指定动画开始&#xff0c;以及动画结束"关键帧"&#xff0c;而动画变化的"中间帧"则由系统计算并补齐&#xff01; 1.补间动画的分类和Interpolator Andoird所支持的补间动画效果…

【应急响应】挖矿脚本检测指南威胁情报样本定性文件清除入口修复

文章目录 挖矿样本-Win&Linux-危害&定性Linux-Web安全漏洞导致挖矿事件Windows-系统口令爆破导致挖矿事件Linux-个人真实服务器被植入挖矿分析 挖矿样本-Win&Linux-危害&定性 危害&#xff1a;CPU拉满&#xff0c;网络阻塞&#xff0c;服务器卡顿、耗电等 定性…

Opencv+Python笔记(十)灰度直方图、直方图均衡化、掩模的应用

目录 一、灰度直方图二、图像掩模的应用三、直方图均衡化1.直方图均衡化2.自适应的直方图均衡化 一、灰度直方图 概念&#xff1a; 灰度直方图是关于灰度级分布的函数&#xff0c;是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素&#xff0c;按照灰度值的大小…

SAP-重复制造行业为什么推荐定额工艺路线

翻译一篇大佬的文章&#xff1a; Why Rate Routing is (recommended) used in Repetitive Manufacturing? 看多了博客解Routing和Rate routing的区别&#xff0c;看来还是有很多会员不满意或者不清楚&#xff0c;对此类问题的概念或解释。我认为很少有屏幕截图的博客可以帮助…

UML--类图--软件工程系统学习-- idea查看类图-类关系图

文章目录 什么是类图类图的用途类图的组成 类什么是类类符号类关系依赖&#xff08;Dependence&#xff09;idea查看依赖 关联关系&#xff08;association&#xff09;继承/泛化idea查看继承 实现&#xff08;realization&#xff09;聚合组成组合和聚合之间的差异 类图详解id…

无感平滑迁移:海量高并发数据库如何进行国产化改造?

首先&#xff0c;讲一下数据库国产化的大背景。 一、数据库国产化的背景 国家战略方面的&#xff0c;随着外部形势的日益复杂&#xff0c;核心技术急需实现自主可控、安全可靠、高效开放&#xff1b;另一个要求是业务方面的&#xff0c;当业务高速发展后各种问题会接踵而至&a…

Go | 一分钟掌握Go | 4 - 数组

作者&#xff1a;Mars酱 声明&#xff1a;本文章由Mars酱编写&#xff0c;部分内容来源于网络&#xff0c;如有疑问请联系本人。 转载&#xff1a;欢迎转载&#xff0c;转载前先请联系我&#xff01; 说明 特意省去了很多基础章节&#xff0c;比如常量、变量、条件语句、判断语…

GPT应用-使用中文操作数据库

GPT应用-使用中文操作数据库 本次尝试使用langchain来操作数据库&#xff1b; 环境配置 下面是数据库相关的表&#xff0c;使用Mysql5.7 数据库,数据库名students 下面是相关表的介绍 学生表&#xff0c;有名字、分数、和老师的备注 学生父母表&#xff0c;其中有学生的名…

053:cesium显示网格切片标识,展示X、Y、Level 坐标

第053个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载瓦片网格切分标识地图。,它在切片方案中的每个渲染图块周围绘制一个框,并在其中绘制一个标签,指示图块的 X、Y、Level 坐标。 这主要用于调试地形和图像渲染问题。 直接复制下面的 vue+cesium源代码,操…

【Buildroot】基础知识:目录、根文件系统目录覆盖、编译性能分析(编译时间、目标尺寸、包依赖图)

文章目录 一、Buildroot目录介绍二、Finalizing target2.1 fs overlay2.2 post build2.3 post image 三、编译性能3.1 编译耗时3.2 编译依赖关系3.3 编译结果尺寸分析3.4 其他文件 buildroot官方教程 buildroot使用介绍 Buildroot官网上可以下载发布版 国内的SOC厂商Rockchip就…

第二届SWCTF部分WP

1、misc &#xff08;1&#xff09;Misc1 下载附件&#xff0c;压缩包里面有两张jpg图片 解压后习惯性的放进kali里面分析一下&#xff0c;没有隐藏文件 放到Stegsolve里分析&#xff0c;因为是两张一样的图片&#xff0c;combiner也没啥发现 分别对两张图片单独分析也没有发…

网卡,dma,内存关系

本篇主要讲网卡的工作原理 最近在做一个网卡仿真程序。主要目的是用程序代替网卡去向内存中填充报文。 网卡与内存的交互方式 1. rx阶段 网卡通过DMA向内存中发送数据包。 在内存中主要有三个数据结构 ① DMA环(rx_ring), 其中存储了DMA描述符, DMA描述符指向了实际物理地址…

【Python | 基础语法篇】01、字面量、注释、变量、数据类型及转换

目录 一、字面量 1.1 什么是字面量 1.2 常用的值类型 1.3 字符串 1.4 如何在代码中写它们 1.5 总结 二、注释 2.1 注释的作用 2.2 注释的分类 2.3 注释实战 2.4 总结 2.5 思考 三、变量 3.1 什么是变量 3.2 案例——模拟钱包 3.3 变量的特征 3.4 思考 3.5 …

一篇文章看懂MySQL的多表连接(包含左/右/全外连接)

MySQL的多表查询 这是第二次学习多表查询&#xff0c;关于左右连接还是不是很熟悉&#xff0c;因此重新看一下。小目标&#xff1a;一篇文章看懂多表查询&#xff01;&#xff01; 这篇博客是跟着宋红康老师学习的&#xff0c;点击此处查看视频&#xff0c;关于数据库我放在了…

主动式电容笔是什么?苹果平替电容笔性价比高的推荐

苹果Pencil在市场上有需求吗&#xff1f;苹果的原装电容笔&#xff0c;虽然功能强大&#xff0c;但价格却非常的昂贵。当然&#xff0c;你也可以用这个苹果Pencil&#xff0c;不过&#xff0c;如果你不想花大价钱买它&#xff0c;就可以选一支平替的电容笔。就当前的科技水平而…

黑客利用WordPress 插件暗中建立后门网站

东方联盟网络安全组织在上周发布的一份报告中透露&#xff0c;有人观察到威胁行为者利用一个合法但过时的 WordPress 插件暗中建立后门网站&#xff0c;作为正在进行的活动的一部分。 有问题的插件是 Eval PHP&#xff0c;由名为 flashpixx 的开发人员发布。它允许用户插入 PH…

从需求分析到上线发布,一步步带你开发收废品小程序

在如今的环保和可持续性的大趋势下&#xff0c;废品回收已经成为了人们日常生活中不可或缺的一部分。收废品小程序的开发可以帮助人们更方便地找到回收废品的地点&#xff0c;并有效减少废品对环境造成的污染。因此&#xff0c;我们的收废品小程序需要满足以下需求&#xff1a;…