使用 Python 构建一个掷骰子应用程序

17 Mar 2025 | 阅读 17 分钟

制作一些小型项目,例如基于文本用户界面(TUI)的掷骰子应用,可以帮助我们提升 Python 编程能力。我们将学习如何收集和验证用户输入、从包和模块中导入代码、编写函数、使用循环和条件语句,以及使用字符串和 print() 函数显示输出。

在本教程中,我们将编写一个模拟掷骰子事件的应用程序。为此,我们将使用 Python 的 random 模块。

在本教程中,我们将学习如何:

  • 利用 random.randint() 模拟掷骰子的情况
  • 我们可以使用内置的 input() 函数来请求用户输入
  • 解析并验证用户输入
  • 使用 .center() 和 .join() 等方法修改字符串

演示

在这个分步项目中,我们将创建一个模拟掷骰子的应用程序。该应用程序最多可以掷 6 个六面骰子。每次掷骰后,应用程序会创建骰子面的 ASCII 图并显示在屏幕上。

当我们打开掷骰子模拟应用时,会看到一个提示,要求输入想要掷的骰子数量。如果输入一个在 1 到 6 范围内的可接受整数,应用程序就会模拟掷骰子事件,并在屏幕上显示骰子面的图形。

项目概述

我们的掷骰子模拟器应用程序将具有一个简单但用户友好的文本用户界面(TUI),允许我们选择想要掷的六面骰子的数量。我们可以使用这个 TUI 在线掷骰子。

以下是该应用程序内部工作原理的说明:

要运行的任务要使用的工具要编写的代码
要求用户选择他们将掷的骰子数量,然后读取用户的输入。Python 内置的 input() 函数使用适当的参数调用 input()
验证并解析用户的输入字符串方法、比较运算符以及条件断言一个名为 parse_input() 的用户定义函数
进行掷骰子模拟Python 的 random 模块,特别是 randint() 函数一个名为 roll_dice() 的用户定义函数
使用骰子面创建一个 ASCII 图循环、list.append() 和 str.join()一个名为 generate_dice_faces_diagram() 的用户定义函数
在屏幕上绘制骰子面的图片Python 内置的 print() 函数使用适当的参数调用 print()

了解了这些内部工作原理后,我们将编写三个自定义函数来实现应用程序的主要特性和功能。这些函数定义了应用程序的公共 API,我们将调用这些 API 来使我们的应用程序运行起来。

为了组织我们的掷骰子模拟器代码,我们将在文件系统中的任意位置创建一个名为 dice1.py 的文件。现在就创建这个文件,开始吧!

前提条件

在构建这个掷骰子模拟游戏之前,用户必须熟悉以下概念和技术:

  • 执行 Python 程序的策略
  • Python 的导入机制
  • Python 数据类型的基础知识,主要是字符串和整数
  • 基本的数据结构,特别是列表
  • Python 的变量和常量
  • Python 的比较运算符
  • 布尔值和逻辑表达式
  • 条件语句
  • Python 中的循环
  • Python 中输入、输出和字符串格式化的基础知识

如果用户不确定在开始这个项目前是否具备所有必要的技能,可以通过继续这个项目来学习!如果遇到困难,随时可以停下来查阅本页列出的资源。

第 1 步:为我们的 Python 掷骰子应用编写 TUI 代码

在此阶段,我们将编写代码,询问用户希望在游戏中掷多少个骰子。此外,我们将编写一个 Python 函数,该函数接收用户输入,进行验证,并在验证成功时将其作为整数返回。如果验证失败,该函数将再次要求用户输入。

在命令行获取用户输入

我们可以开始编写与用户交互的代码。这段代码将创建应用程序的文本用户界面,并主要依赖于 input()。这个内置函数会直接从命令行读取用户的输入。其回调参数允许我们指定想要接收的输入类型。

第 5 行的 input() 调用将提示我们选择用户希望掷的骰子数量。如提示所示,骰子数量必须在 1 到 6 之间(含 1 和 6)。

第 6 行调用 parse_input1() 函数,并将其返回值存储在骰子数量变量中。在下一节,我们将实现这个函数。

解析并验证用户输入

parse_input1() 的目的是接收字符串形式的用户输入,验证它是否是一个有效的整数,然后将其作为 Python 的 int 对象返回。我们可以将以下代码行添加到 dice1.py 文件中应用程序主代码之前:

以下是代码的逐行解释:

  • 第 3 行 定义了 “parse_input1()”,它接受一个字符串输入作为参数。
  • 第 4-9 行 包含了该函数的文档字符串(Docstring)。在函数中加入清晰且格式良好的文档是 Python 编程的最佳实践,因为文档字符串可以帮助我们记录代码的功能。
  • 第 10 行 检查用户的输入是否有效,即是否为可掷的骰子数量。.strip() 函数 会移除输入行周围任何不必要的空格。该运算符判断我们的输入是否在允许的骰子数量范围内。在这种情况下,我们使用一个集合,因为 Python 集合中的成员资格测试非常高效。
  • 第 11 行 将输入转换为整数并返回给调用者。
  • 第 13 行 在屏幕上显示一条消息,告知用户输入不正确(如果适用)。
  • 第 14 行 通过一个 SystemExit 异常和错误码 1 来退出应用程序,表示出现了问题。

通过 parse_input(),我们验证并处理来自命令提示符的用户输入。验证来自用户或任何不受信任来源的输入对于我们的应用程序安全可靠地运行至关重要。

我们必须确保这些功能通过一个直观的 TUI 和可靠的输入验证系统正常工作。这正是我们将在下一节中要做的事情。

试用掷骰子应用的 TUI

为了测试我们目前编写的代码,我们可以打开一个命令行终端并运行 dice1.py 脚本。

输出

How many dice do the user want to roll? [1-6] 3

输入

输出

How many dice do the user want to roll? [1-6] 7
Error:
Please enter a number between 1 to 6.

如果我们输入一个 1 到 6 之间的整数,程序不会显示错误消息。但是,如果输入不是一个整数或超出了指定范围,我们会收到一条警告,提示用户需要一个 1 到 6 之间的整数。

到目前为止,我们已经编写了可以请求和处理命令行输入的功能代码。该代码是基于 input() 函数构建的应用程序 TUI。它还有一个函数用于验证用户输入并将其作为整数返回。现在是时候掷骰子了!

第 2 步:在 Python 中模拟掷六面骰子

掷骰子应用程序现在提供了一个 TUI,可以接收并处理用户输入。为了继续开发应用程序的核心功能,我们需要创建我们的 roll_dice1() 函数,它允许用户模拟掷骰子事件。此函数将接收用户想要掷的骰子数量作为输入。

标准库的 Python random 模块包含一个 randint() 函数,该函数在指定区间内生成伪随机整数。我们可以使用此函数来创建掷骰子的模拟。

以下是实现 roll_dice1() 的代码:

此代码片段的 第 2 行 将 random 引入到我们当前的命名空间中。这个导入使我们之后能够使用 randint() 函数。以下是其余代码的分解:

  • 第 6 行 定义了 roll_dice(),它接受一个参数,指示在特定调用中将掷多少个骰子。
  • 第 7-11 行 提供了该函数的文档字符串。
  • 第 12 行 创建了一个名为 roll_results 的空列表,用于记录骰子模拟的结果。
  • 第 13 行 指定了一个 for 循环,该循环将为用户希望掷的每个骰子重复执行。
  • 第 14 行 使用 randint() 生成一个 1 到 6(含)之间的伪随机整数。该数字是掷一个六面骰子的结果。每次迭代调用只产生一个数字。
  • 第 15 行 将当前掷骰子的结果添加到 roll_results 中。
  • 第 16 行 提供了掷骰子模拟结果的列表。

要测试我们新开发的功能,请在 dice1.py 文件的末尾添加以下代码行:

在这个例子中,第 9 行 的代码使用 num_dice1 作为参数调用了 roll_dice1()第 11 行 使用 print() 将结果作为一组数字显示在屏幕上。每个数字都是一个骰子的结果。测试完代码后,可以删除 第 11 行

使用命令行启动运行应用程序。

输入

输出

How many dice do the user want to roll? [1-6] 5
[6, 1, 3, 6, 6]

输入

输出

How many dice do the user want to roll? [1-6] 2
[2, 6]

屏幕上显示的结果列表会有所不同,因为我们正在生成伪随机数。在这种情况下,我们分别模拟了掷两个和五个骰子。值得注意的是,每个骰子的值都在 1 到 6 的范围内。这是因为我们使用的是六面骰子。

一旦我们创建并测试了模拟掷骰子的代码,现在是时候继续前进,为我们的应用程序提供一种更吸引人的方式来显示结果了。这正是我们将在下一节中要做的事情。

第 3 步:生成并显示骰子面的 ASCII 图

此时,我们的应用程序已经可以模拟掷多个骰子并将结果记录在一个数字数组中。然而,从用户的角度来看,数字列表看起来并不吸引人。最好有一个更精美的输出,这样我们的应用程序才会显得专业。

在本节中,我们将编写所需的代码来生成一个显示所有骰子面的图表。为此,我们将设计一个 ASCII 艺术元素。

设置骰子面的图表

掷骰子模拟器应用程序需要一种方式来显示掷骰子时发生的情况。为此,它将使用骰子面的 ASCII 表示来显示掷六面骰子的结果。例如,当我们掷 4 个骰子时,图形会是这样的:

Build a Dice-Rolling Application with Python

此图中的每个骰子面代表一次模拟运行的结果。为了开始编写构建此图的功能,我们必须组合 ASCII 艺术。回到我们的代码编辑器并添加以下内容:

第 4 行到第 47 行,我们用 ASCII 字符制作了六个骰子面。我们将这些骰子面保存在 DICE_ART 中,这是一个将每个面转换为其整数等价物的字典。

第 48 行 定义了 DIE_HEIGHT,即一个特定面预期占用的行数。在这种情况下,每个面占用五行。同样,第 49 行 指定了 DIE_WIDTH 来存储绘制一个骰子面所需的列数。在这种情况下,它是 11。

然后,第 50 行 定义了 DIE_FACE_SEPARATOR,它包含一个空白字符。这些常量将用于为我们的应用程序创建和显示骰子面的 ASCII 图。

生成骰子面的图表

在这个阶段,我们已经为每个面构建了 ASCII 艺术。为了将这些部分组合成一个显示模拟完整结果的图表,我们将编写第二个自定义函数。

这个函数完成了以下工作:

  • 第 5 行 定义了 generate_dice_faces_diagram(),它带有一个名为 dice_values 的参数。这个参数是使用 roll_dice() 产生的掷骰子整数值列表。
  • 第 6-18 行 包含了该函数的文档字符串。
  • 第 20 行 创建了一个名为 dice_faces1 的列表,用于保存输入列表中与骰子点数对应的骰子面。当创建 ASCII 图时,这些骰子面将会出现。
  • 第 21 行 定义了一个循环,用于遍历骰子的值。
  • 第 22 行 用于从 DICE_ART1 中检索与当前骰子值对应的骰子面,然后将该骰子面添加到 dice_faces1 中。
  • 第 25 行 创建一个空列表,用于存放最终骰子面图表的行。
  • 第 26 行 定义了一个循环,重复从 0 到 DIE_HEIGHT - 1 的索引。每个索引代表骰子面图表中特定行的编号。
  • 第 27 行row_components 定义为一个空列表,用于存放构成该行的骰子面部分。
  • 第 28 行 创建一个嵌套的 for 循环,该循环遍历骰子的各个面。
  • 第 29 行 记录每一行的组成部分。
  • 第 30 行 将行的各个组成部分连接起来形成一个行字符串,并用空格分隔这些部分。
  • 第 31 行 将行添加到将构成我们最终图形的行列表中。
  • 第 34 行 创建一个变量来保存当前骰子面图表的尺寸。
  • 第 35 行 创建一个显示 "RESULTS" 字样的标题。为此,它使用了 str.center(),并以图表的宽度和波浪号(~)作为参数。
  • 第 37 行 创建一个包含最终骰子面图表的字符串。换行符 (\n) 作为行分隔符。.join() 的参数是一个字符串,它结合了图表的标题和构成骰子面的字符串(行)。
  • 第 38 行 将一个准备好打印的骰子面图表返回给调用者。

哇!这可真不少!我们稍后会回顾我们编写的代码并进行修改,使其更易于管理。但在那之前,我们需要测试一下应用程序,并且需要编写主代码块。

完成应用的主代码并掷骰子

有了 generate_dice_faces_diagram() 之后,我们现在可以完成应用程序主代码的编写,这将使我们能够在屏幕上生成并显示骰子面的图表。我们可以在 dice1.py 的最后一行添加以下代码:

第 12 行 调用 generate_dice_faces_diagram(),并以 roll_results 作为参数。此方法创建并显示与当前掷骰结果相匹配的骰子面图。第 14 行调用 print() 在屏幕上显示该图。

有了这个更新,我们将能够重新启动应用程序。回到命令行并运行此命令。

输入

输出

How many dice do the user want to roll? [1-6] 5

~~~~~~~~~~~~~~~~~~~~~~~~~ RESULTS ~~~~~~~~~~~~~~~~~~~~~~~~~
┌─────────? ┌─────────? ┌─────────? ┌─────────? ┌─────────?
│ ●     ● │ │ ●     ● │ │ ●     ● │ │ ●       │ │ ●     ● │
│    ●    │ │ ●     ● │ │ ●     ● │ │         │ │         │
│ ●     ● │ │ ●     ● │ │ ●     ● │ │      ●  │ │ ●     ● │
└─────────? └─────────? └─────────? └─────────? └─────────?

酷!掷骰子模拟器现在呈现出一个格式精美的 ASCII 图,显示了模拟的结果。这真不错。

如果我们回到 generate_dice_faces_diagram() 的实现,我们会注意到它包含一些注释,指出了代码相应部分的功能:

这种类型的注释通常表明我们的代码可以通过一些重新设计得到改进。在下一节中,我们将应用一种流行的重构技术,帮助我们整理代码,使其更易于维护。

第 4 步:重构生成骰子面图的代码

我们的 generate_dice_faces_diagram() 函数需要解释性注释,因为它同时执行多个操作,违反了单一职责原则。

简单来说,这个规则指出每个函数或类只应该做一件事。因此,对某个特定函数的更改不会影响程序的其余部分。最终,我们将得到一个改进的、更健壮的代码。

有一种称为“提取方法”的重构方法,可以通过移除能够独立工作的功能来帮助我们改进代码。例如,我们可以从之前 generate_dice_faces_diagram() 的实现中提取第 20 行到第 22 行的代码,并将其放入一个名为 _get_dice_faces1() 的非公开辅助函数中:

我们可以从 generate_dice_faces_diagram() 中调用 _get_dice_faces1() 来获取所需结果。使用这种方法,我们可以完全重构 generate_dice_faces_diagram() 以满足单一职责原则。这是一个重构后的 generate_dice_faces_diagram() 版本,它利用了 _get_dice_faces() 并实现了另一个名为 _generate_dice_faces_rows() 的辅助函数,以提取第 25 行到第 31 行的功能:

辅助函数允许我们选择可读性强或描述性的标题,从而无需冗长的解释。新增的辅助函数从初始函数中提取了功能。现在每个辅助函数都有其自己的职责。

重构代码以提高效率是作为 Python 开发者应具备的一项宝贵技能。有关代码重构的更深入信息,请参阅《如何重构 Python 应用程序以确保简洁性》。

代码重构的一个关键思想是,修改后的代码必须与原始代码的功能完全相同。为了测试这一点,请再次运行我们的应用程序!

我们已经完成了我们的工作!我们开发了一个功能齐全的 TUI 应用程序,可以让我们模拟掷骰子的体验。每次启动应用程序,我们都可以掷最多六个六面骰子。我们甚至可以在一个漂亮的 ASCII 图中看到骰子的结果。

结论

我们用 Python 编写了一个功能强大的程序,它是一个基于文本用户界面的应用程序,可以模拟掷六面骰子。通过这个项目,我们学习并实践了基本技能,如收集和验证用户输入、导入代码、创建使用循环和条件语句的函数,以及在屏幕上显示格式良好的输出。

在本教程中,我们学习了如何:

  • 利用 randint() 模拟掷骰子
  • 通过内置函数 input() 从命令行获取用户输入
  • 使用各种工具和方法读取并验证用户输入
  • 使用 .center().join() 等方法修改字符串

此外,我们还学习了如何组织、构建文档以及执行 Python 脚本和程序。通过获得这些知识,我们为继续我们的 Python 编程之旅做好了更充分的准备。