尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用Besiege搭建三进制全加器:从编码到逻辑门完整指南

用Besiege搭建三进制全加器:从编码到逻辑门完整指南 1. 背景与核心概念之前一直在 Besiege 里搭机械结构飞机、投石车、攻城锤都玩过一轮之后总想换点更有挑战的方向。后来看到有人用游戏里的逻辑构件搭出了计算器甚至还有人在研究三进制计算机才意识到这个游戏不仅能做物理模拟还能用来验证计算机组成原理里的各种电路设计。这次我把三进制全加器的开发过程记录下来从三进制编码到逻辑门布局再到完整接线验证方便同样感兴趣的朋友一起讨论。1.1 Besiege 是什么为什么能做逻辑电路Besiege 是一款以中世纪攻城战为主题的物理沙盒游戏核心玩法是建造攻城机械并完成关卡目标。玩家可以自由组合木材、钢铁、火药、滑轮、开关、传感器等零件搭建出各种能动的机器。真正让它变成“可以玩计算机组成原理”的工具是因为游戏提供了多种可控组件包括逻辑门组件AND、OR、NOT、XOR、延时等输入组件按钮、开关、压力传感器、角度传感器输出组件LED 灯、机械臂、轮子、活塞连接组件软轴、硬轴、线缆类信号连接这些组件组合起来就可以在游戏里搭建数字逻辑电路。和真实硬件不同Besiege 里不需要焊电路板也不需要接电阻电容只需要规划好组件的相对位置把输入输出用线缆连接起来就能构建出各种组合逻辑电路和时序逻辑电路。对于想学习计算机组成原理但手边没有实验箱的开发者来说Besiege 其实是一个非常直观的“3D 逻辑实验平台”。1.2 为什么选择三进制而不是二进制绝大多数现代计算机都使用二进制原因是晶体管天然具备“导通/截止”两种稳定状态而且二值逻辑的电路实现最成熟。那为什么要在 Besiege 里做三进制计算机第一个原因是三进制本身有研究价值。历史上苏联曾研发过三进制计算机 Setun使用平衡三进制表示数字在表示负数、减少进位传递延迟方面有一定优势。第二个原因是 Besiege 的逻辑组件本身提供了丰富的状态表达方式。游戏里的压力传感器、角度传感器可以输出多个阈值档位机械角度也可以在一个转轴上同时标记多个状态。相比于二进制只需要区分“开”和“关”三进制需要在“低/中/高”三个电平状态之间做区分在游戏里反而更贴近机械结构的自然状态。第三个原因很现实那就是“难”。三进制全加器在网上能搜到的资料远少于二进制全加器尤其是 Besiege 这种特殊环境下的实现更是需要自己一点点推演。写这篇文章也是希望把这类设计思路沉淀下来减少后续开发者的试错成本。1.3 全加器在计算机中的位置加法器是 CPU 里最基础、最核心的运算部件之一。无论是加法、减法还是乘法、除法底层最终都要转换为加法运算。全加器是加法定级电路的一个关键单元它负责处理“两个输入位”和“低位的进位输入”三者相加输出“和位”与“进位输出”。在普通二进制中全加器的真值表只有 8 种输入组合。但换成三进制之后输入组合数量变成 27 种设计复杂度显著上升这也是本文需要重点拆解的部分。2. 环境准备与版本说明在设计三进制全加器之前先说明一下开发和验证环境。这类时间跨度较长的项目环境版本的差异可能影响组件名称和逻辑行为因此有必要先把环境固定下来。2.1 游戏环境游戏平台BesiegeSteam 版本游戏模式沙盒模式或关卡编辑器是否使用模组纯原版组件未使用额外模组需要说明的是Besiege 的版本更新比较频繁逻辑组件在不同版本中的名称和参数可能略有差异。例如早期版本里部分组件叫“开关”后期版本中统一为“逻辑门”系列。因此本文描述的是通用设计思路具体组件名称以你当前版本为准。如果使用模组建议优先使用逻辑构建类模组例如“Building Tools”或“NoBounds”。但本文示例不依赖这些模组原版组件足够完成全加器搭建。2.2 辅助验证工具Besiege 内的电路规模变大后直接在游戏里排查逻辑错误效率很低。为此我使用 Python 对三进制全加器的真值表做了预演逻辑确认无误后再回到游戏里搭建。Python 脚本只需要标准库不需要额外安装第三方依赖。2.3 基本术语约定在开始之前先明确几个术语避免后续混淆术语含义输入位 A加数之一输入位 B加数之一进位输入 Cin低位传来的进位和位 Sum当前位的直接运算结果进位输出 Cout传递给高位的进位三态编码把一个三进制数字用多个逻辑信号组合表示3. 三进制与全加器原理拆解设计三进制全加器的第一步不是打开 Besiege 开始拖组件而是先在纸面上把编码方案和真值表定义清楚。这一步如果没做好后面在游戏里会反复返工。3.1 三进制的基本编码方式三进制数字通常有三种编码方式普通三进制、平衡三进制、双位二进制编码。普通三进制使用 0、1、2 三个数字逢三进一。计算机内部如果要表达这三个状态最简单的方式是把电压分为三档。平衡三进制使用 -1、0、1 三个数字好处是负数表示非常自然缺点是逻辑门的设计习惯与常见数字逻辑不太一致。Besiege 的逻辑门最终输出的是布尔信号也就是只有两种状态。想让逻辑门表达三态最稳妥的方案是采用“双位编码”用两个布尔信号的组合来表示三进制的三个数字。这样可以直接复用二进制逻辑门来实现三态运算。例如约定三进制数字高区位 H低区位 L000101210这里的 H 和 L 是两个独立的布尔信号。你也可以换一种编码把 1 编码为 0/1高区位 0低区位 1把 0 编码为 00甚至可以用 1/0 表示“高电平”。只要在后续的加法逻辑中保持一致任何编码方案都可行。本文后续统一采用上面这张表的编码方式。3.2 三进制加法规则三进制加法遵循逢三进一的规则0 0 00 1 10 2 21 1 21 2 10即 3本位写 0向高位进 12 2 11即 4本位写 1向高位进 1当存在来自低位的进位 Cin 时需要把 A、B、Cin 三个数全部加起来再决定本位和与进位输出。3.3 三进制全加器真值表设输入为 A、B、Cin输出为 Sum、Cout其中 A 和 B 的取值范围是 0、1、2Cin 的取值范围是 0 或 1因为进位最多为 1。三者的和为 T A B Cin。T 的范围是 0 到 5。当 T 小于 3 时Cout 为 0Sum 等于 T当 T 大于等于 3 时Cout 为 1Sum 等于 T 减 3。真值表如下ABCinSumCout000000011001010011200202002101100101012011020111011200112111200202010121001211112201122121有了真值表后续就可以设计逻辑表达式了。3.4 为什么先算加法规则再进游戏很多玩家在 Besiege 里搭逻辑电路时容易犯一个错误拿到组件就开搭边搭边想逻辑。对于简单的 NOT 门或 AND 门这样做没问题但全加器属于组合逻辑电路输出不仅有本位和还有进位多条信号路径之间有严格的关系。没有真值表你根本不知道自己需要多少个逻辑门也容易接错信号。所以我的建议是正式搭建之前把真值表和信号编码表打印出来或者截图放到第二块屏幕上随时比对。这样遇到异常输出时才能快速定位是编码问题还是接线问题。4. Besiege 中三态信号的实现方式三进制全加器在 Besiege 中落地的核心难点就是怎样把“0/1/2”这三个状态转换成游戏内可识别的物理信号并通过逻辑门完成运算。4.1 用双位编码表达三态前面提到双位编码可以把三进制数字拆成两个二进制信号。在 Besiege 中这两个信号可以用两个逻辑门的输出来表达分别称为高区位 H 和低区位 L。例如要表示数字 2就让高区位输出 1低区位输出 0要表示数字 1就让高区位输出 0低区位输出 1要表示数字 0就让两个位置都输出 0。这套方案的优点是所有逻辑门仍然是二值逻辑门不需要新增组件真值表可以直接推导出逻辑表达式用 LED 灯可以直观调试缺点是位数会翻倍。一个三进制全加器需要处理三组双位信号A_H、A_L、B_H、B_L加上进位信号输入信号线一共有 5 根。后续如果需要扩展到多位三进制加法器信号线数量会进一步增加因此在布局时要注意空间规划。4.2 Besiege 中逻辑门的输入输出特性Besiege 的逻辑门通常支持多个输入端口和一个输出端口。以常见的 AND 门为例当所有输入都为 1 时输出才为 1。OR 门只要有一个输入为 1输出就是 1。NOT 门则对输入取反。组合逻辑设计时需要特别注意 Besiege 的逻辑门是否存在“非零即真”的处理。也就是说如果一个组件的输入信号不是严格的 0/1而是某个模拟值逻辑门可能会将非零值统一视为真。因此在搭建时尽量保证信号电平是明确的高/低状态避免出现悬浮信号或弱驱动信号。4.3 进位信号的处理思路进位信号本身只有 0 和 1 两种状态因此可以直接用一个布尔信号表示。它不需要双位编码。这也是三进制全加器和二进制全加器的区别之一输入数据是三进制的进位却是二进制的。处理进位时需要注意进位输出需要馈入下一个高位全加器。在 Besiege 中可以使用线缆把进位输出连接到下一个加数输入端口。如果信号路径太长可能要考虑信号延迟必要时加装中继器实际上就是一级 NOT 或缓冲门来增强信号。5. 完整实战三进制全加器电路设计现在进入真正的搭建环节。这里分为几个阶段半加器设计、全加器推导、逻辑门布局、接线验证。5.1 用逻辑表达式描述三态运算为了在 Besiege 里直观地布置逻辑门我们需要把三进制全加器的行为拆解成若干布尔逻辑表达式。设 A 的双位编码为 AH、ALB 的双位编码为 BH、BL。数字与编码的对应关系为数字 0H0L0数字 1H0L1数字 2H1L0这一套编码是设计讨论的基础。你也可以在游戏里自定义为其他编码但后续表达式需要相应调整。定义一个函数 V(AH, AL)表示一个三态输入信号代表的数值。那么当前位的加法结果 T V(AH, AL) V(BH, BL) Cin。其中 T 的取值从 0 到 5。为了确定 Sum 的低位 SL 和高位 SH以及 Cout我们按真值表逐项分析。这里可以采用“卡诺图降维”的思路把 A、B、Cin 转换为布尔组合然后直接导出对应输出。不过在三进制场景下直接按真值表枚举更直观也更不容易出错。我们将在后面的 Python 验证里用字典形式枚举 27 种输入组合。5.2 半加器不含进位的三进制加法全加器可以先拆成半加器。三进制半加器的输入只有 A 和 B不包含进位输出为 Sum 和 Cout。先看几个关键组合当 A0B0 时Sum0Cout0当 A1B1 时Sum2Cout0当 A1B2 时Sum0Cout1当 A2B2 时Sum1Cout1可以看出Cout 为 1 的情况只在 AB 大于等于 3 时出现。具体判断条件是A 和 B 的取值组合是否为 (1,2)、(2,1)、(2,2)。在双位编码下每个三态信号都拆成 H、L 两根线。要让 Cout 为 1需要检测A 为 1 且 B 为 2AL1AH0BL0BH1A 为 2 且 B 为 1AH1AL0BH0BL1A 为 2 且 B 为 2AH1AL0BH1BL0这些条件可以通过 AND 门和 OR 门组合得到。Sum 的生成更复杂因为 Sum 本身也可能是一个三态数字。在 Besiege 里如果 Sum 的三个状态也要用双位编码输出我们仍然需要两根输出线。因此一个三进制半加器的输出为 SH、SL、Cout 三根线。5.3 全加器把半加器的结果与进位组合三进制全加器可以视为“先用半加器把 A 和 B 相加再把结果与进位输入 Cin 相加”。实现方式如下第一级半加器计算 A B得到中间结果 T1 和中间进位 C1第二级半加器把 T1 与 Cin 相加得到最终 Sum 和中间进位 C2最终 Cout 为 C1 或 C2 的 OR 结果这种两级设计的好处是整个电路可以拆成更小、更好验证的模块。每一级都可以在 Besiege 中独立测试。如果直接编写 27 行真值表对应的逻辑很容易接线时漏掉一根线。5.4 Besiege 中的逻辑门接线布局下面给出一个典型的布局规划适合在 Besiege 沙盒模式中实现。整个区域可以分为四个区块输入区放置 A 和 B 的开关组以及 Cin 开关编码区将开关状态对应成双位编码逻辑运算区放置 AND、OR、NOT 门完成加法和进位判断输出显示区放置 LED 灯显示 Sum_H、Sum_L、Cout一个推荐的最小结构如下输入区 A_H 开关 A_L 开关 B_H 开关 B_L 开关 Cin 开关 | | | | | --------- -------- | | | | [编码整理区] [编码整理区] | | | | -------------------------------- | [半加器级 1] | [半加器级 2] | [输出整理区] | -------------------- | | | Sum_H Sum_L Cout | | | LED灯 LED灯 LED灯这里的“编码整理区”不是必须的只要你的开关本身就是按双位编码方式布置的可以直接把开关输出接到逻辑门上。编码整理区主要用于把任意开关组合转换为标准双位编码信号便于调试。5.5 逻辑门组件的具体配置思路由于不同版本组件名称不同这里给出一个组件配置思路对应关系可以按你游戏里的组件翻译调整。假设逻辑门有输入端口和输出端口。配置要点如下AND 门所有输入为 1 时输出 1OR 门任一输入为 1 时输出 1NOT 门输入为 1 时输出 0输入为 0 时输出 1对于需要“检测某一特定组合”的地方使用 AND 门。例如检测 A 为 1、B 为 2 的情况输入1AL输入2BH此时输出为 1代表检测成功。对于需要“多个组合任一成立”的地方使用 OR 门。例如检测 AB 大于等于 3 的所有情况可以把三种组合的检测结果接到一个 OR 门上。实践中发现Besiege 的逻辑门输入端口数量有限可能有些 AND 门只有两个输入端。检测 AH1 且 AL0 且 BH1 且 BL0 这样的条件时可以分多级完成先把 AH 和 BH 送入一个 AND 门再把 AL 接入 NOT 门后送入 OR 门与前一结果合并等等。5.6 输入端开关与双位编码的映射输入区可以使用普通开关作为信号源。为了方便测试建议把 A 和 B 的开关分成两组每组两个开关分别代表高区位和低区位。当你把开关设为高区位 OFF、低区位 OFF表示输入数字 0高区位 OFF、低区位 ON表示输入数字 1高区位 ON、低区位 OFF表示输入数字 2为什么要避免“高区位 ON、低区位 ON”的组合呢因为这不在我们的编码表里会变成未定义状态。设计时用 LED 灯显示这一组合方便快速发现输入设置错误。5.7 游戏内完整测试步骤搭建完成后需要对 27 种输入组合逐一验证。为提升效率建议先测试边界组合再随机抽查中间组合。推荐测试顺序A0B0Cin0预期 Sum0Cout0A2B2Cin1预期 Sum2Cout1A1B2Cin1预期 Sum1Cout1A2B2Cin0预期 Sum1Cout1A2B1Cin0预期 Sum0Cout1测试时记录实际输出和期望输出的差异。如果发现差异先检查输入开关的编码是否有误再沿信号路径逐级查看 LED 灯的亮灭状态定位到具体逻辑门。6. 用 Python 验证三进制全加器逻辑在进入 Besiege 手动接线之前建议先用 Python 把逻辑跑通。这里给出一个可以直接运行的验证脚本它枚举全部 27 种输入组合输出真值表并检查逻辑结果是否符合预期。6.1 完整代码# 文件路径ternary_full_adder.py # 依赖Python 3.6仅使用标准库 def encode(digit): 将一个三进制数字编码为双位布尔信号 (H, L)。 0 - (0, 0) 1 - (0, 1) 2 - (1, 0) if digit 0: return (0, 0) elif digit 1: return (0, 1) elif digit 2: return (1, 0) else: raise ValueError(fInvalid ternary digit: {digit}) def decode(h, l): 将双位布尔信号解码为三进制数字。 (0,0) - 0 (0,1) - 1 (1,0) - 2 其他组合视为错误 if h 0 and l 0: return 0 elif h 0 and l 1: return 1 elif h 1 and l 0: return 2 else: raise ValueError(fInvalid two-bit signal: h{h}, l{l}) def half_adder(a_h, a_l, b_h, b_l): 三进制半加器输入 A、B 的双位编码输出中间结果和进位。 这里返回的是真值表定义的逻辑结果不是最终全加器结果。 a_val decode(a_h, a_l) b_val decode(b_h, b_l) t a_val b_val if t 3: sum_val t - 3 carry 1 else: sum_val t carry 0 sum_h, sum_l encode(sum_val) return sum_h, sum_l, carry def full_adder(a_h, a_l, b_h, b_l, cin): 三进制全加器A、B 为双位编码输入cin 为进位输入。 返回最终和的双位编码与进位输出。 # 第一级半加器A B t_h, t_l, c1 half_adder(a_h, a_l, b_h, b_l) # 第二级半加器中间结果 Cin # 注意中间结果 decode 后为 0~2cin 为 0 或 1 t_val decode(t_h, t_l) s_val t_val cin if s_val 3: sum_val s_val - 3 c2 1 else: sum_val s_val c2 0 sum_h, sum_l encode(sum_val) cout c1 or c2 return sum_h, sum_l, cout def expected_result(a, b, cin): 根据真值表计算期望输出用于校验。 t a b cin if t 3: sum_val t - 3 cout 1 else: sum_val t cout 0 sum_h, sum_l encode(sum_val) return sum_h, sum_l, cout def main(): pass_count 0 total_count 0 for a in range(3): for b in range(3): for cin in range(2): total_count 1 a_h, a_l encode(a) b_h, b_l encode(b) sh, sl, cout full_adder(a_h, a_l, b_h, b_l, cin) ex_sh, ex_sl, ex_cout expected_result(a, b, cin) ok (sh ex_sh and sl ex_sl and cout ex_cout) if not ok: print(fError: A{a}, B{b}, Cin{cin}) print(f FullAdder - Sum({sh},{sl}), Cout{cout}) print(f Expected - Sum({ex_sh},{ex_sl}), Cout{ex_cout}) else: pass_count 1 print(fPassed {pass_count}/{total_count} cases) if __name__ __main__: main()运行这个脚本预期输出Passed 27/27 cases如果出现失败说明逻辑表达式存在边界问题需要回到真值表核对。6.2 Python 脚本如何帮助 Besiege 搭建你可以把这个脚本当成“虚拟原型机”。每次在 Besiege 里改完一组逻辑门回到 Python 里跑一遍对应的小型测试函数确认局部逻辑是否正确。例如如果你只想测试半加器里“A1、B2 时进位为 1”这个条件可以写一个小用例直接调用 half_adder 函数而不是每次都在游戏里手动拨开关。虽然这不能完全替代游戏内的信号测试但可以帮你提前过滤掉大部分逻辑设计错误把精力集中在 Besiege 特有的接线和物理布局问题上。7. 常见问题与排查思路在实际搭建中会遇到不少 Besiege 特有的问题。这里整理几个典型情况。7.1 常见问题表格问题现象常见原因解决思路输入组合正确但输出灯全暗信号线没有连接到目标输入端口检查每根信号线的起点和终点确保连接到组件的正确端口某些组合输出稳定某些组合随机闪烁信号路径过长逻辑门输出延迟缩短信号路径必要时加装缓冲门半加器测试通过全加器输出不正确第二级半加器的输入端接反确认中间结果的 H/L 分别接入了第二级的正确端口进位输出一直为 1C1 和 C2 的 OR 门输入端常高检查进位信号来源排除信号线短路开关状态切换后输出不变开关接的是模拟值逻辑门未收到标准 0/1使用比较器或阈值门把信号规范化组件数量过多导致游戏卡顿每位加法器都使用大量门先搭建一位全加器并验证再复制扩展优化门数量7.2 逻辑门输入悬空问题Besiege 中如果一个逻辑门的输入端口没有连接任何信号线该端口的值可能默认为 0也可能受到附近组件的感应影响不同版本表现不同。为了避免这种不确定性不使用的输入端口务必接一个固定关闭的开关或者通过 NOT 门生成一个固定 1 信号确保所有输入都有确定的逻辑电平。7.3 信号反相问题许多玩家在使用 NOT 门时会忽略逻辑门的默认输入规则。比如想表达“高位为 0 且低位为 1”的情况如果直接接入高位信号忘记加 NOT那么条件就变成了“高位为 1 且低位为 1”结果自然不对。排查时可以这样做在每个 AND 门输入端并联 LED逐个开关拨动观察哪个输入信号亮起确认 LED 亮灭与真值表预期一致只要养成这个习惯信号反相问题会很快暴露。7.4 进位传递链的延迟问题多级全加器串联后进位信号要经过很多级逻辑门才能到达最高位。Besiege 的逻辑门存在内部延迟如果位数较多可能出现暂时错误输出。降低进位传递延迟的方法有两种采用“超前进位”思想提前计算进位条件减少串行传递级数在允许范围内缩短信号路径减少不必要的 NOT 门链不过在单机演示场景下一位全加器的延迟通常不影响最终显示只需要在测试时等信号稳定后再观察 LED 即可。8. 最佳实践与工程建议经过大半个下午的搭线和反复调试我总结出几条比较实用的工程建议供后续扩展时参考。8.1 模块化设计与命名Besiege 没有项目文件系统所有组件都堆放在同一个场景中。如果不做模块分区很快就会被密密麻麻的线缆绕晕。我的做法是用木块或铁块围出不同的功能区块比如输入区、半加器区、全加器区、输出区每个区块用不同颜色的喷漆标记输入端开关旁边放一个小木牌标明“A_H”“A_L”“B_H”“B_L”“Cin”虽然这些标记不能直接影响逻辑但可以显著降低调试时的认知成本。8.2 从半加器开始逐级验证不要一上来就搭建完整全加器。先搭一个三进制半加器用开关输入所有组合确认输出符合预期。然后只添加第二级半加器再把进位 OR 门加上。每一步验证通过后再进入下一步。这样虽然看起来多了几个步骤但实际总耗时比一次性搭完再排查要短得多。8.3 注意存档备份在改动规模较大之前先复制一份存档。Besiege 的撤销功能有时不够细粒度一旦接线错误可能很难复原。多存档是最好的兜底手段。8.4 优化逻辑门数量的思路三进制全加器在双位编码方案下逻辑门数量明显多于二进制全加器。如果追求组件数量优化可以考虑几种方向使用复合逻辑门减少基础门数量调整编码方案让部分输出更贴近现有门结构当连续多位设计时使用“进位选择”结构复用部分逻辑但优化应优先保障可读性团队协作或教学演示场景下清晰比精简更重要。8.5 机械结构稳定性Besiege 中的逻辑组件虽然属于逻辑构建但物理上仍会被碰撞影响。如果输出灯或开关被旁边的机械臂撞到信号就会突变。搭建时建议把逻辑电路高架起来远离活动部件或者单独建一个测试场地不做任何机械驱动。9. 总结与扩展方向至此我已经把 Besiege 中机械三进制全加器从编码设计、真值表推导、Python 脚本验证到游戏内逻辑门搭建和调试的完整流程记录了一遍。核心收获可以总结为三点三进制全加器并不是简单的二进制全加器“加一条线”而是需要重新定义编码和真值表双位编码是 Besiege 中实现三态信号的可行方案且能复用现有逻辑门先用脚本验证逻辑、再进游戏搭建能大幅降低调试成本下一步可以继续做几件事将多个一位全加器级联为三位三进制加法器实现三进制减法器通过转换补码在加法器基础上扩展一个简单的三进制乘法器尝试用机械结构实现存储单元进一步接近“三进制计算机”的目标如果你也在玩 Besiege 且对逻辑电路感兴趣建议先从一位三进制半加器开始练手。真值表、双位编码、模块化布局这些思路不只适用于全加器也适用于后续更复杂的运算部件设计。搭建过程中遇到新的信号传输问题或逻辑门性能瓶颈欢迎在评论区交流。
返回列表