经典 Hangman 游戏的 MiniScript 移植:命令行与 Mini Micro 双环境运行及源码解析
2026/9/23 23:41:01 网站建设 项目流程
  • 示例工程

【免费下载链接】basic-computer-games

An updated version of the classic "Basic Computer Games" book, with well-written examples in a variety of common MEMORY SAFE, SCRIPTING programming languages. See https://coding-horror.github.io/basic-computer-games/

项目地址:https://gitcode.com/gh_mirrors/ba/basic-computer-games
点击查看免费下载

这篇技术指南围绕 00_Alternate_Languages/44_Hangman/MiniScript/ 目录下的 README.md 与 hangman.ms 展开,完整讲解这款源自 20 世纪 70 年代《Basic Computer Games》的经典猜词游戏,在MiniScript语言下如何在「命令行环境」与「Mini Micro 图形化环境」两种方式中安装运行,并结合源码逐段剖析其词库、绞刑架绘制与游戏主循环的实现原理。读完本文,你将掌握 MiniScript 程序的两类运行方式、本仓库 MiniScript 移植版的完整代码结构,以及它与原版 BASIC 实现的对应关系,可直接上手运行并二次修改词库。

一、移植背景:从 Vintage BASIC 到 MiniScript

Hangman(刽子手)是《Basic Computer Games》书中的经典文字猜谜游戏:计算机秘密挑选一个英文单词,玩家逐字母猜测,每猜错一次就向绞刑架上添一笔,累计 10 次错误后小人被"吊死",游戏结束。

在 00_Alternate_Languages/44_Hangman/ 目录下,这一经典玩法以多种"非 10 大主流语言"的形式重新实现(该目录专门收录不在约定 10 种语言之列、但内存安全或脚本化的补充移植,用于教学目的,详见 00_Alternate_Languages/README.md)。其中 MiniScript 子目录即本次主题:它的 README.md 明确说明,该移植的原始代码下载自 Vintage Basic 站点,随后被转换为MiniScript语言。MiniScript 是一门轻量、可嵌入的脚本语言,既可作为命令行解释器运行.ms文件,也可在名为Mini Micro的复古风格虚拟主机中运行。

原版 BASIC 程序位于 00_Alternate_Languages/44_Hangman/hangman.bas,全文约 80 行,是理解本次 MiniScript 移植逻辑的最佳对照物(详见下文第五节)。

二、两种运行环境与安装启动步骤(README 核心操作)

这是 README.md 给出的完整运行指引,也是本移植版最直接的"上手手册"。

2.1 方式一:命令行 MiniScript(Command-Line MiniScript)

适合纯文本终端场景,步骤为:

  1. 下载并安装命令行版 MiniScript:前往 MiniScript 官网的 Command-Line 下载页面,选择与你的操作系统(Windows / macOS / Linux)匹配的版本并完成安装。
  2. 运行程序:在终端进入包含hangman.ms的目录,执行:
miniscript hangman.ms

即可看到标题横幅 "Hangman" 与 "Creative Computing Morristown, New Jersey"(对应 hangman.ms),随后进入猜词循环。命令名miniscript即命令行解释器可执行文件的默认名称。

2.2 方式二:Mini Micro(虚拟复古主机)

Mini Micro 是 MiniScript 官方提供的图形化运行环境,带有虚拟磁盘与命令提示符,步骤为:

  1. 下载并启动 Mini Micro:从官网的 Mini Micro 页面获取对应系统的版本,解压后启动。
  2. 挂载游戏目录:点击界面顶部的磁盘槽位(top disk slot),选择"Mount Folder...",在弹出的文件选择器中选中包含hangman.ms与本文 README 的目录(即仓库中的 00_Alternate_Languages/44_Hangman/MiniScript/ 文件夹)。
  3. 加载并运行:在 Mini Micro 的命令提示符中依次输入:
load "hangman" run

load会从已挂载的磁盘目录中按文件名(不区分.ms后缀)加载脚本,run启动游戏。相比命令行方式,Mini Micro 还额外支持直接从系统词库文件读取单词(详见第四节末尾的扩展说明)。

三、源码逐段解析:hangman.ms 的核心实现

hangman.ms 全文件共 127 行,结构清晰:先打印标题,再初始化词库,随后定义两个核心函数addToPicdoOneGame,最后是"再来一局"主循环。下面按执行顺序展开。

3.1 词库初始化与随机洗牌

words = [] words += ["gum","sin","for","cry","lug","bye","fly"] words += ["ugly","each","from","work","talk","with","self"] words += ["pizza","thing","feign","fiend","elbow","fault","dirty"] words += ["budget","spirit","quaint","maiden","escort","pickax"] words += ["example","tension","quinine","kidney","replica","sleeper"] words += ["triangle","kangaroo","mahogany","sergeant","sequence"] words += ["moustache","dangerous","scientist","different","quiescent"] words += ["magistrate","erroneously","loudspeaker","phytotoxic"] words += ["matrimonial","parasympathomimetic","thigmotropism"]

(见 hangman.ms)

词库共 49 个单词,按长度从 3 个字母(gumsin…)到超长单词(parasympathomimeticthigmotropism)分级排列,与原版 BASIC 的DATA语句(hangman.bas)逐条对应。MiniScript 用+=直接向列表追加一组元素,比 BASIC 的READ循环更简洁。

随后一行:

words.shuffle

(hangman.ms)

shuffle是 MiniScript 列表的内置方法,将全部单词随机打乱。这样游戏出题顺序完全随机,且不需要像 BASIC 版那样用U(Q)=1数组记录"哪些词已经用过"(hangman.bas)——MiniScript 版直接在游戏过程中用words.pull从列表末尾取出并移除单词,天然避免重复。

3.2 addToPic:10 步渐进绘制绞刑架小人

addToPic = function(guessCount) if guessCount == 1 then print "First, we draw a head" ps[3][6]="-"; ps[3][7]="-"; ps[3][8]="-"; ps[4][5]="("; ps[4][6]="." ps[4][8]="."; ps[4][9]=")"; ps[5][6]="-"; ps[5][7]="-"; ps[5][8]="-" else if guessCount == 2 then print "Now we draw a body." for i in range(6, 9); ps[i][7]="x"; end for else if guessCount == 3 then print "Next we draw an arm." for i in range(4, 7); ps[i][i-1]="\"; end for else if guessCount == 4 then print "This time it's the other arm." ps[4][11]="/"; ps[5][10]="/"; ps[6][9]="/"; ps[7][8]="/" else if guessCount == 5 then print "Now, let's draw the right leg." ps[10][6]="/"; ps[11][5]="/" else if guessCount == 6 then print "This time we draw the left leg." ps[10][8]="\"; ps[11][9]="\" else if guessCount == 7 then print "Now we put up a hand." ps[3][11]="\" else if guessCount == 8 then print "Next the other hand." ps[3][3]="/" else if guessCount == 9 then print "Now we draw one foot" ps[12][10]="\"; ps[12][11]="-" else if guessCount == 10 then print "Here's the other foot -- you're hung!!" ps[12][3]="-"; ps[12][4]="/" end if for i in range(1, 12) print ps[i].join("") end for print end function

(见 hangman.ms)

该函数以猜错次数guessCount(1~10)为输入,每错一次向 12×12 的字符画布ps中写入一笔,并打印一句旁白("First, we draw a head"、"Now we draw a body." 等,与原版 BASIC 第 415~460 行的输出完全一致)。关键设计点:

  • 画布模型ps是一个二维列表(外层 13 行、内层 12 列),通过ps[行][列] = 字符直接赋值。这是对 BASIC 版DIM P$(12,12)字符串数组(hangman.bas)的忠实映射;
  • 绘制顺序:头(第 1 错)→ 身体(第 2 错)→ 左臂(第 3 错)→ 右臂(第 4 错)→ 右腿(第 5 错)→ 左腿(第 6 错)→ 左手(第 7 错)→ 右手(第 8 错)→ 左脚(第 9 错)→ 右脚(第 10 错,同时宣告 "you're hung!!");
  • 输出方式:每次绘制后,用ps[i].join("")将每行字符数组拼接为字符串,循环打印 1~12 行,把当前完整画面刷新到终端。

3.3 doOneGame:单局游戏主逻辑

doOneGame = function usedLetters = [] globals.ps = [] for i in range(0, 12); ps.push [" "]*12; end for for i in range(1,12); ps[i][1] = "X"; end for for i in range(1, 7); ps[1][i] = "X"; end for; ps[2][7] = "X" secretWord = words.pull.upper //print "(Secret word: " + secretWord + ")" visibleWord = ["-"] * secretWord.len wrongGuesses = 0 while true print "Here are the letters you used:" print usedLetters.join(",") print print visibleWord.join("") print guess = input("What is your guess? ").upper guess = (guess + " ")[0] if guess < "A" or guess > "Z" then continue if usedLetters.indexOf(guess) != null then print "You guessed that letter before!" continue end if usedLetters.push guess for i in visibleWord.indexes if secretWord[i] == guess then visibleWord[i] = guess end for if visibleWord.indexOf("-") == null then print "You found the word!" return true else if secretWord.indexOf(guess) != null then print print visibleWord.join("") print guess = input("What is your guess for the word? ").upper if guess == secretWord then print "Right!! It took you " + usedLetters.len + " guesses!" return true else print "Wrong. Try another letter." end if print else print "Sorry, that letter isn't in the word." wrongGuesses += 1 addToPic wrongGuesses if wrongGuesses > 9 then print "Sorry, you lose. The word was " + secretWord return false end if end if end while end function

(见 hangman.ms)

doOneGame封装一局完整的游戏,逐行解读如下:

  1. 局前状态初始化(L60-L68):清空usedLetters(已猜字母列表);重建globals.ps画布并填充空格,再绘制固定不变的绞刑架骨架——左侧竖杆(第 1 列X)、顶部横杆(第 1 行X)与吊绳(ps[2][7]),这与 BASIC 版第 80~90 行的初始化逐字符对应。随后words.pull.upper从词库取出一个单词并转大写作为secretWordvisibleWord是等长的"-"占位数组,wrongGuesses归零。第 66 行注释掉的调试语句//print "(Secret word: ...)"可取消注释以作弊查看答案。

  2. 每轮猜测循环(L69-L110):

    • 先打印已用字母(usedLetters.join(","))与当前猜测进度(visibleWord.join("")),对应 BASIC 版第 170~200 行的输出;
    • input("What is your guess? ")读取输入并转大写;guess = (guess + " ")[0]巧妙地把任意输入截断为第一个字符(即使玩家误敲了回车或输入整词,也只取首字母);
    • 输入校验if guess < "A" or guess > "Z" then continue过滤非字母字符;usedLetters.indexOf(guess) != null检测重复猜测,重复时提示 "You guessed that letter before!" 并跳过本轮(对应 BASIC 版第 220~240 行的查重逻辑);
    • 更新揭示板:遍历visibleWord.indexes,凡是secretWord[i] == guess的位置,把占位符-替换为猜中的字母;
    • 三种分支结果
      • visibleWord中已无-(全部字母猜中),打印 "You found the word!" 并return true
      • 若该字母确实存在于单词中但尚未全中,BASIC 版特有的"猜完整单词"机会被保留:程序打印当前进度,追问"What is your guess for the word?"(hangman.ms),猜对则提示 "Right!! It took you N guesses!" 并获胜返回,猜错则 "Wrong. Try another letter." 继续;
      • 否则字母不在词中:wrongGuesses += 1并调用addToPic绘制一笔;一旦wrongGuesses > 9(即达到第 10 错),打印 "Sorry, you lose. The word was ..." 并return false

3.4 顶层主循环:再来一局与词库耗尽

while true if not words then print "You did all the words!!" break end if won = doOneGame if won then yn = input("Want another word? ").upper else yn = input("You missed that one. Do you want another word? ").upper end if if not yn or yn[0] != "Y" then break end while print print "It's been fun! Bye for now."

(见 hangman.ms)

主循环每次先检查words列表是否为空——因为words.pull会从列表中移除已用单词,所以 49 个词全部猜完后打印 "You did all the words!!" 并结束(对应 BASIC 版第 97 行的YOU DID ALL THE WORDS!!)。否则调用doOneGame开始一局;结束后根据胜负分别询问 "Want another word?" 或 "You missed that one. Do you want another word?"(对应 BASIC 版第 370、610 行),输入以Y开头(yn[0] != "Y")即再来一局,否则打印 "It's been fun! Bye for now." 退出。

四、词库扩展:如何更换或加载自定义单词

本移植版的词库硬编码在 hangman.ms 的words列表中,直接增删或修改列表元素即可定制词库。值得特别注意的是源码第 15~16 行的注释:

// Note: on Mini Micro, you could instead do: // words = file.readLines("/sys/data/englishWords.txt")

(hangman.ms)

这提供了一个 Mini Micro 专属的进阶用法:由于 Mini Micro 自带系统数据文件englishWords.txt,只需把初始化代码替换为words = file.readLines("/sys/data/englishWords.txt"),即可从系统英文词库加载海量单词,彻底摆脱内置 49 词的局限。这一用法依赖 Mini Micro 的文件系统 API(file.readLines),因此在纯命令行 MiniScript 环境下不可用——这也解释了为何它被保留为注释而不是默认代码。替换后建议保留words.shuffle的随机化调用。

五、与原版 BASIC 的逐点对照

MiniScript 移植版在玩法与输出上与原版 hangman.bas 保持高度一致,可对照观察两门语言的表达差异:

功能点原版 BASIC(hangman.bas)MiniScript 移植(hangman.ms)
词库定义DATA语句 +READ按序号读取(L100-L150, L71-L79)字面量列表 +words.shuffle随机洗牌(L5-L18)
防重复出题U(Q)=1数组记录已用序号(L110-L115)words.pull取出即移除(L65)
画布声明DIM P$(12,12)二维字符串数组(L30)嵌套列表psglobals.ps声明为全局(L61-L62)
猜错绘制ON M GOTO分支 + 逐格赋值(L410-L570)addToPic函数的else if分支(L20-L52)
输入处理INPUT G$单字符(L210)input(...)+ 取首字符 + 字母范围校验(L75-L77)
重复字母检测循环比对N$(I)(L220-L240)usedLetters.indexOf(guess)(L78)
完整单词机会INPUT B$二次询问(L330)input("What is your guess for the word? ")(L93)

可以看出,MiniScript 移植在保留原版玩法与全部提示文案的同时,用列表内建方法(shufflepullindexOfjoin)和for/while结构化控制流,消除了 BASIC 的GOTO跳转与ON M GOTO分支表,可读性显著提升。同一游戏的另一份 C 语言移植(00_Alternate_Languages/44_Hangman/C/main.c,词库见 dictionary.txt)也保留了相同的 10 笔绘制流程,可作为跨语言对照的补充参考。

六、小结

本文完整覆盖了 MiniScript 版 Hangman 的 README 给出的两种运行方式:命令行miniscript hangman.ms与 Mini Micro 的load "hangman"+run;并基于 hangman.ms 全量源码,从词库初始化、10 步绞刑架绘制、单局猜测循环到"再来一局"主循环做了逐段解析,同时给出了将其扩展为 Mini Micro 系统词库(englishWords.txt)的替换方法。这一移植版完整继承了《Basic Computer Games》原版 Hangman 的玩法、文案与绘制流程,是学习 MiniScript 列表操作、字符串处理与终端字符画的良好示例,也便于与同目录的 BASIC、C 版本进行跨语言对比阅读。

  • 示例工程

【免费下载链接】basic-computer-games

An updated version of the classic "Basic Computer Games" book, with well-written examples in a variety of common MEMORY SAFE, SCRIPTING programming languages. See https://coding-horror.github.io/basic-computer-games/

项目地址:https://gitcode.com/gh_mirrors/ba/basic-computer-games
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询