- 示例工程
【免费下载链接】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/
这篇技术指南围绕 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)
适合纯文本终端场景,步骤为:
- 下载并安装命令行版 MiniScript:前往 MiniScript 官网的 Command-Line 下载页面,选择与你的操作系统(Windows / macOS / Linux)匹配的版本并完成安装。
- 运行程序:在终端进入包含
hangman.ms的目录,执行:
miniscript hangman.ms即可看到标题横幅 "Hangman" 与 "Creative Computing Morristown, New Jersey"(对应 hangman.ms),随后进入猜词循环。命令名miniscript即命令行解释器可执行文件的默认名称。
2.2 方式二:Mini Micro(虚拟复古主机)
Mini Micro 是 MiniScript 官方提供的图形化运行环境,带有虚拟磁盘与命令提示符,步骤为:
- 下载并启动 Mini Micro:从官网的 Mini Micro 页面获取对应系统的版本,解压后启动。
- 挂载游戏目录:点击界面顶部的磁盘槽位(top disk slot),选择"Mount Folder...",在弹出的文件选择器中选中包含
hangman.ms与本文 README 的目录(即仓库中的 00_Alternate_Languages/44_Hangman/MiniScript/ 文件夹)。 - 加载并运行:在 Mini Micro 的命令提示符中依次输入:
load "hangman" runload会从已挂载的磁盘目录中按文件名(不区分.ms后缀)加载脚本,run启动游戏。相比命令行方式,Mini Micro 还额外支持直接从系统词库文件读取单词(详见第四节末尾的扩展说明)。
三、源码逐段解析:hangman.ms 的核心实现
hangman.ms 全文件共 127 行,结构清晰:先打印标题,再初始化词库,随后定义两个核心函数addToPic与doOneGame,最后是"再来一局"主循环。下面按执行顺序展开。
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 个字母(gum、sin…)到超长单词(parasympathomimetic、thigmotropism)分级排列,与原版 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封装一局完整的游戏,逐行解读如下:
局前状态初始化(L60-L68):清空
usedLetters(已猜字母列表);重建globals.ps画布并填充空格,再绘制固定不变的绞刑架骨架——左侧竖杆(第 1 列X)、顶部横杆(第 1 行X)与吊绳(ps[2][7]),这与 BASIC 版第 80~90 行的初始化逐字符对应。随后words.pull.upper从词库取出一个单词并转大写作为secretWord;visibleWord是等长的"-"占位数组,wrongGuesses归零。第 66 行注释掉的调试语句//print "(Secret word: ...)"可取消注释以作弊查看答案。每轮猜测循环(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) | 嵌套列表ps,globals.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 移植在保留原版玩法与全部提示文案的同时,用列表内建方法(shuffle、pull、indexOf、join)和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/
相关推荐
用 MiniScript 运行经典篮球模拟游戏《Basketball》:命令行与 Mini Micro 双运行指南及源码解析
用 MiniScript 运行经典篮球模拟游戏《Basketball》:命令行与 Mini Micro 双运行指南及源码解析 导读 本文围绕 00_Altern
示例工程Teable 如何修改 Prisma 数据库模型并创建、适用 migration?
Teable 如何修改 Prisma 数据库模型并创建、适用 migration? Teable 使用 Prisma 作为 ORM 管理数据库。当你需要在主数据
示例工程basic-computer-games 之 Fur Trader MiniScript 移植版:命令行与 Mini Micro 双环境运行全指南
basic computer games 之 Fur Trader MiniScript 移植版:命令行与 Mini Micro 双环境运行全指南 导读 本文围
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考