简介:这份资源是面向日本麻将(立直麻将)爱好者的开源助手工具,使用 Go 语言开发,适合希望提升牌效判断与防守能力的进阶玩家,也适合对麻将算法感兴趣的开发者研究。它能在对局中自动分析手牌,综合进张与打点给出推荐舍牌;当有人立直或多副露时,显示各张牌的危险度,并记录他家手切与摸切,还支持牌谱复盘,逐打推荐进攻或防守选择,兼容四人麻将与三人麻将,可对接雀魂网页版和天凤平台。压缩包共138个文件,约1.47MB,以87个go源码文件为核心,辅以33个png界面素材、6个js脚本,以及proto、json、html、makefile等配置与构建文件,结构清晰便于二次开发。目前已有14172人学习下载,读者可借此理解牌效计算、危险度评估与记牌逻辑,并参考其模块划分搭建自己的麻将分析工具。1. 牌桌上的第二双眼睛:这个日麻助手到底在算什么打日麻的人大概都经历过这种时刻:手里一副牌,进张看着挺多,真打起来却总是慢人一步;明明觉得自己在防守,结果点了个满贯;牌河翻来覆去看了三遍,还是记不清对家到底切过几张关键牌。日本麻将(立直麻将)的复杂度不在于规则本身,而在于每一打背后同时压着牌效、防守、读牌三条线,人脑在限时对局里很难三条线都算清楚。mahjong-helper 就是冲着这个痛点来的。它是一个用 Go 写的日本麻将助手,核心能力是三块:牌效计算(向听数、有效进张、切牌推荐)、防守判断(基于对手舍牌和副露的危険度评估)、记牌辅助(剩余牌张统计与可视化)。它支持雀魂(Majsoul)和天凤(Tenhou)两个平台的对局数据接入,也就是说你在这些平台上打牌时,它能把当前手牌和场况读进去,实时给出建议。适合谁用?如果你已经过了「知道什么是役、什么是振听」的入门阶段,正在被「为什么我总是先手却和不了」「为什么我总在终盘点炮」这类问题困扰,那这个工具值得拆开看看。它不替你打牌,但它能把你自己算不清的那部分摊开给你看。下面我从它是怎么组织的、怎么跑起来、参数怎么调、哪里容易翻车,一层层拆。2. 拆开看结构:牌效引擎、防守模块和平台适配层怎么咬合2.1 牌效计算的核心:向听数与有效进张是怎么算出来的日麻牌效的底层问题是:给定 13 张手牌,距离和牌还差几步,每一步有哪些牌能推进。这个「几步」就是向听数(shanten),「哪些牌能推进」就是有效进张。mahjong-helper 的牌效引擎本质上是一个搜索器,它把手牌拆成面子(顺子、刻子)和搭子(对子、两面、嵌张、边张)的组合,然后枚举所有可能的拆分方式,取向听数最小的那一种。听起来简单,但实现上有几个关键决策。第一是是否区分鸣牌(副露)后的手牌,因为副露会改变向听数的计算基准——副露算一个面子,手牌张数变少,搜索空间也变了。第二是是否考虑七对子和国士无双这类特殊牌型,它们的向听数算法和普通型完全不同。第三是性能,一副牌的可能拆分方式是指数级的,必须靠剪枝和缓存把单次计算压到毫秒级,否则实时对局里根本跟不上。我一般会先确认引擎是否把普通型、七对子、国士分开算,再取最小值作为最终向听。有效进张则是在向听数确定后,枚举所有可能摸到的牌,看哪些能让向听数减一。这里有个容易忽略的点:有效进张要区分「自摸进张」和「鸣牌进张」,两者的价值不一样,前者不消耗巡目,后者会暴露信息并可能破坏门清。// 伪代码示意:向听数计算的主循环结构 func calcShanten(hand []int, melds int) int { // hand 是 34 长度的计数数组,melds 是已副露面子数 best := 8 // 向听数上限 // 分别计算普通型、七对子、国士 best = min(best, normalShanten(hand, melds)) if melds == 0 { best = min(best, chiitoiShanten(hand)) best = min(best, kokushiShanten(hand)) } return best }这段结构说明的是:向听数不是单一算法,而是多个牌型算法取最优。hand用 34 长度数组而不是切片,是为了避免频繁分配内存,这在实时计算里很关键。melds参数决定是否跳过七对和国士,因为副露后这两种牌型不可能成立。如果你自己改代码,最容易出错的地方就是忘了在副露后禁用特殊牌型判断,导致算出不可能的向听数。2.2 防守模块:危険度是怎么从舍牌和副露里推出来的防守比牌效更难做,因为它涉及对对手手牌的推测,而推测本身是不确定的。mahjong-helper 的防守模块走的是统计路线:根据对手的舍牌顺序、副露内容、立直宣言,给每张可能打出的牌打一个危険度分数。具体来说,它会看几个信号。一是筋牌(suji),对手立直后,某些牌因为筋的关系相对安全,比如对手切过 4 和 7,那 1 和 4 的危険度会下降。二是壁(壁),某张牌四张里已经出现了三张甚至四张,那相关的顺子就做不成了,相邻牌的危険度下降。三是对手的副露方向,如果对手碰了一堆万子,那万子的危険度整体上升。四是巡目,终盘的危険度权重比序盘高得多。这些信号单独看都不新鲜,难的是怎么加权。不同工具的权重设计不一样,有的偏保守,有的偏激进。mahjong-helper 的做法是把这些信号归一化后加权求和,输出一个 0 到 1 之间的危険度值。你在实际用的时候会发现,它的防守建议有时候偏保守,序盘就会让你切一些看起来还能用的牌,这是权重设计的取舍,不是 bug。// 伪代码示意:单张牌的危険度评估 func dangerScore(tile int, opponent OpponentInfo, turn int) float64 { score := 0.0 score += sujiFactor(tile, opponent.discards) // 筋牌因子 score += wallFactor(tile, opponent.discards) // 壁因子 score += meldFactor(tile, opponent.melds) // 副露因子 score *= turnWeight(turn) // 巡目权重 return clamp(score, 0, 1) }这里turnWeight是个乘数而不是加数,意味着巡目越晚,所有信号的危険度都被放大。clamp保证输出在 0 到 1 之间,方便后续排序。如果你觉得它太保守,可以调turnWeight的曲线,让序盘的权重更低;如果觉得太激进,就反过来。但要注意,改权重会同时影响所有牌型,不能只针对某一种情况调。2.3 平台适配层:雀魂和天凤的数据是怎么接进来的mahjong-helper 支持雀魂和天凤,但这两个平台的接入方式完全不同。天凤有公开的牌谱格式和协议文档,社区里有很多现成的解析工具,接入相对标准化。雀魂没有官方协议文档,社区靠的是抓包分析和逆向,稳定性会差一些,平台更新后可能失效。适配层的职责是把平台的对局数据转成内部统一的数据结构,包括手牌、场况(自风、场风、巡目、剩余牌数)、对手信息(舍牌、副露、立直状态)。这个转换层是整条链路里最脆弱的一环,因为平台一改协议,这里就得跟着改。我一般会先把适配层单独跑通,确认能稳定读到一局完整对局的数据,再去调牌效和防守的参数,否则你根本分不清是算法问题还是数据问题。# 常见做法:先单独测试平台适配层,确认数据能读进来 # 以天凤为例,用社区解析库读一份牌谱,打印手牌和场况 go run ./cmd/parse-tenhou -file test_hanchan.json -verbose # 输出应包含每巡的手牌、摸牌、舍牌、副露事件这个步骤的目的是隔离问题。如果你直接跑完整助手,发现建议不对,可能是适配层读错了手牌,也可能是牌效算错了,混在一起很难排查。先确认数据层没问题,后面的调试才有意义。-verbose会打印每一巡的原始数据,方便你对照牌谱确认。3. 跑起来:从编译到实时对局接入的完整步骤3.1 环境准备与编译:Go 版本和依赖怎么选mahjong-helper 是 Go 项目,编译前先确认 Go 版本。日麻计算对性能敏感,建议用较新的稳定版,太老的版本可能在并发和内存分配上有差距。依赖管理用的是 Go Modules,拉下来之后go mod download把依赖装齐。# 确认 Go 版本,建议 1.20 以上 go version # 克隆项目后进入目录,下载依赖 cd mahjong-helper go mod download # 编译主程序,输出到 bin 目录 go build -o bin/mahjong-helper ./cmd/helper编译本身不复杂,但有几个点要注意。一是如果项目里有 cgo 依赖(某些平台适配可能用到),交叉编译会麻烦,建议在目标平台上直接编译。二是编译产物的大小和启动速度,Go 的静态编译产物通常几 MB 到十几 MB,启动很快,适合常驻。三是如果你要改代码,先跑一遍go test ./...确认基线测试通过,再动手,否则改出问题分不清是谁的锅。3.2 配置参数:哪些值该调,哪些别乱动跑起来之后第一件事是看配置文件。mahjong-helper 的配置一般分几块:平台接入配置(选雀魂还是天凤、端口、认证信息)、牌效参数(是否启用七对、国士、是否考虑鸣牌)、防守参数(危険度权重、巡目曲线)、输出配置(显示方式、刷新频率)。参数类别典型参数建议值说明平台接入platformtenhou / majsoul按你实际打的平台选牌效enable_chiitoitrue门清时启用七对判断牌效enable_kokushitrue门清时启用国士判断牌效consider_meldtrue是否把鸣牌进张算进有效进张防守danger_weight0.5危険度在综合建议里的权重防守turn_curveaggressive巡目权重曲线,可选 conservative输出refresh_ms200界面刷新间隔,太低会吃 CPU调参的原则是:先别动,用默认值打几局,记录哪些建议你觉得不对,再针对性调。最常见的调整是danger_weight,如果你发现自己总是因为太保守而错过和牌,就调低;如果总是点炮,就调高。turn_curve影响的是序盘和终盘的权重分配,aggressive在序盘更敢进攻,conservative更早转入防守。提示:改配置后建议先用牌谱回放验证,别直接上实战。回放能让你对照已知结果判断参数改得对不对,实战里你没法确认建议是对是错。3.3 实时对局接入:数据流是怎么走的实时接入的链路是:平台客户端 → 适配层读取对局数据 → 转成内部结构 → 牌效引擎和防守模块并行计算 → 输出层渲染建议。这条链路里,延迟主要来自适配层的读取频率和计算层的耗时。# 启动助手,指定平台和输出方式 ./bin/mahjong-helper -platform tenhou -output console -refresh 200 # 如果平台需要额外认证,通过环境变量传入,不要写进配置文件 export MAHJONG_TOKEN=your_token_here ./bin/mahjong-helper -platform majsoul -output web -port 8080-refresh 200表示每 200 毫秒刷新一次建议,这个值太低会吃 CPU,太高会感觉建议滞后。-output支持 console 和 web 两种,console 适合本地快速看,web 适合多窗口或者想看得更清楚。认证信息用环境变量传,不要写进配置文件,避免泄露。接入之后先别急着实战,找一局牌谱回放,看助手的建议和你自己的判断差在哪。如果差得离谱,先检查适配层读到的数据对不对;如果只是细节不同,那是权重和策略的差异,属于正常范围。4. 避坑与排查:那些让我重新跑一遍日志的瞬间4.1 向听数算出来是负数或者明显不对现象:手牌明明还没听牌,助手显示向听数是 -1 或者 0。原因通常是手牌张数不对,比如适配层多读了一张或者少读了一张,导致引擎按错误的张数计算。也可能是副露数没传对,引擎以为还有门清状态,把七对或国士算进去了。解决:先在适配层打印原始手牌,数一遍张数是不是 13(或者副露后的正确张数)。再检查melds参数有没有正确传递。如果数据没问题,那就是引擎的特殊牌型判断有 bug,临时关掉enable_chiitoi和enable_kokushi看是否恢复正常,能定位到具体是哪个算法的问题。4.2 防守建议在序盘就让你切掉好牌现象:开局没几巡,助手就建议切掉看起来很有用的牌,理由是危険度高。原因通常是turn_curve设成了conservative,或者danger_weight太高,导致序盘的危険度权重被放大。另一个可能是对手的副露被误读,比如把正常的舍牌当成了副露信号。解决:先确认turn_curve和danger_weight的值,序盘阶段危険度不应该主导决策。如果参数没问题,检查对手信息里副露数据是否正确。我一般会在序盘把danger_weight临时调到 0.2 以下,确认牌效建议是否合理,再逐步调回去。4.3 平台更新后助手读不到数据现象:之前能正常跑,平台更新后助手没反应,或者读到的数据全是空的。原因基本可以确定是平台协议变了,适配层的解析逻辑对不上。雀魂这类没有官方协议的平台,更新后适配层失效是常态。解决:先看适配层有没有报错日志,确认是连接问题还是解析问题。如果是解析问题,需要对照新的数据格式改解析逻辑。这时候社区通常也会有人跟进,可以看看有没有更新的适配层代码。在适配层修好之前,助手是没法用的,别在算法层浪费时间。4.4 计算延迟高,建议跟不上对局节奏现象:对局里助手的建议明显滞后,你打完一张牌它才更新。原因可能是refresh_ms设得太低导致 CPU 抢占,也可能是牌效引擎的搜索没有剪枝,单次计算太慢。还有一种可能是适配层读取频率太高,把 IO 堵住了。解决:先把refresh_ms调到 300 到 500 试试,看延迟是否改善。如果没改善,用 profiling 工具看计算层的耗时分布,确认是哪个环节慢。牌效引擎如果没做缓存,同一手牌反复算会浪费很多时间,加一层手牌哈希缓存通常能明显改善。4.5 回放正常但实战不对现象:用牌谱回放时建议都合理,一上实战就不对。原因通常是实战的数据流和回放不一样,回放是一次性读完整局,实战是流式读取,中间可能有事件丢失或者顺序错乱。也可能是实战里对手信息更新不及时,导致防守判断基于过时数据。解决:在实战模式下打印每一巡的原始事件,和回放对比,看是否有事件丢失或顺序问题。流式读取要确保事件按顺序处理,不能并行处理同一局的事件。如果是对手信息更新问题,检查适配层的事件推送频率,必要时降低刷新间隔。5. 进阶:把助手当训练工具而不是拐杖用了一段时间之后,我最大的体会是:这个工具的价值不在于对局里替你做决定,而在于对局后帮你复盘。实战里你来不及细想每一打的得失,但回放可以。我一般会这么用:打完一局,把牌谱导进去,让助手逐巡给出建议,然后对照我自己的选择,看差在哪。差得多的那几巡,就是我要重点想的地方。具体做法是写一个小脚本,把牌谱里的每一巡抽出来,分别跑牌效和防守计算,输出一个对照表。# 伪代码:逐巡对照助手建议和自己的选择 import subprocess, json def review_hanchan(record_path): turns = load_record(record_path) # 加载牌谱,返回每巡数据 for turn in turns: # 调用助手计算当前巡的建议 result = subprocess.run( ["./bin/mahjong-helper", "-replay", "-turn", str(turn.index)], capture_output=True, text=True ) suggestion = json.loads(result.stdout) # 对照自己的选择和助手建议 if suggestion["discard"] != turn.my_discard: print(f"第{turn.index}巡: 我切{turn.my_discard}, 助手建议{suggestion['discard']}") print(f" 牌效: {suggestion['shanten']}向听, 有效进张{suggestion['ukeire']}") print(f" 危険度: {suggestion['danger']}")这个脚本的关键是-replay和-turn参数,让助手在指定巡上做一次计算而不是实时跑。suggestion里包含切牌建议、向听数、有效进张和危険度,对照自己的选择就能看出差异。差异大的地方,往往是你的牌效或者防守判断有系统性偏差。除了复盘,还有一个用法是练牌效。找一些典型的牌型,比如「一向听但进张很窄」「两向听但进张很宽」,让助手算一遍,然后自己手算一遍,看差在哪。手算能帮你建立直觉,助手能帮你验证直觉。两者结合,比单纯依赖工具或者单纯靠自己摸索都快。注意:助手给出的建议是基于统计和搜索的,不是绝对正确。日麻里很多局面没有唯一正确答案,助手的建议只是参考。真正要练的是理解它为什么这么建议,而不是记住它的结论。从那以后我每次打完一局,只要觉得哪里不对劲,都会把牌谱导进去跑一遍逐巡对照。这个习惯帮我改掉了好几个自己没意识到的坏毛病,比如序盘太早放弃门清、终盘防守判断太激进。工具本身不会让你变强,但用它来照自己的盲区,效果比闷头打要好得多。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源是面向日本麻将(立直麻将)爱好者的开源助手工具,使用 Go 语言开发,适合希望提升牌效判断与防守能力的进阶玩家,也适合对麻将算法感兴趣的开发者研究。它能在对局中自动分析手牌,综合进张与打点给出推荐舍牌;当有人立直或多副露时,显示各张牌的危险度,并记录他家手切与摸切,还支持牌谱复盘,逐打推荐进攻或防守选择,兼容四人麻将与三人麻将,可对接雀魂网页版和天凤平台。压缩包共138个文件,约1.47MB,以87个go源码文件为核心,辅以33个png界面素材、6个js脚本,以及proto、json、html、makefile等配置与构建文件,结构清晰便于二次开发。目前已有14172人学习下载,读者可借此理解牌效计算、危险度评估与记牌逻辑,并参考其模块划分搭建自己的麻将分析工具。1. 牌桌上的第二双眼睛:这个日麻助手到底在算什么打日麻的人大概都经历过这种时刻:手里一副牌,进张看着挺多,真打起来却总是慢人一步;明明觉得自己在防守,结果点了个满贯;牌河翻来覆去看了三遍,还是记不清对家到底切过几张关键牌。日本麻将(立直麻将)的复杂度不在于规则本身,而在于每一打背后同时压着牌效、防守、读牌三条线,人脑在限时对局里很难三条线都算清楚。mahjong-helper 就是冲着这个痛点来的。它是一个用 Go 写的日本麻将助手,核心能力是三块:牌效计算(向听数、有效进张、切牌推荐)、防守判断(基于对手舍牌和副露的危険度评估)、记牌辅助(剩余牌张统计与可视化)。它支持雀魂(Majsoul)和天凤(Tenhou)两个平台的对局数据接入,也就是说你在这些平台上打牌时,它能把当前手牌和场况读进去,实时给出建议。适合谁用?如果你已经过了「知道什么是役、什么是振听」的入门阶段,正在被「为什么我总是先手却和不了」「为什么我总在终盘点炮」这类问题困扰,那这个工具值得拆开看看。它不替你打牌,但它能把你自己算不清的那部分摊开给你看。下面我从它是怎么组织的、怎么跑起来、参数怎么调、哪里容易翻车,一层层拆。2. 拆开看结构:牌效引擎、防守模块和平台适配层怎么咬合2.1 牌效计算的核心:向听数与有效进张是怎么算出来的日麻牌效的底层问题是:给定 13 张手牌,距离和牌还差几步,每一步有哪些牌能推进。这个「几步」就是向听数(shanten),「哪些牌能推进」就是有效进张。mahjong-helper 的牌效引擎本质上是一个搜索器,它把手牌拆成面子(顺子、刻子)和搭子(对子、两面、嵌张、边张)的组合,然后枚举所有可能的拆分方式,取向听数最小的那一种。听起来简单,但实现上有几个关键决策。第一是是否区分鸣牌(副露)后的手牌,因为副露会改变向听数的计算基准——副露算一个面子,手牌张数变少,搜索空间也变了。第二是是否考虑七对子和国士无双这类特殊牌型,它们的向听数算法和普通型完全不同。第三是性能,一副牌的可能拆分方式是指数级的,必须靠剪枝和缓存把单次计算压到毫秒级,否则实时对局里根本跟不上。我一般会先确认引擎是否把普通型、七对子、国士分开算,再取最小值作为最终向听。有效进张则是在向听数确定后,枚举所有可能摸到的牌,看哪些能让向听数减一。这里有个容易忽略的点:有效进张要区分「自摸进张」和「鸣牌进张」,两者的价值不一样,前者不消耗巡目,后者会暴露信息并可能破坏门清。// 伪代码示意:向听数计算的主循环结构 func calcShanten(hand []int, melds int) int { // hand 是 34 长度的计数数组,melds 是已副露面子数 best := 8 // 向听数上限 // 分别计算普通型、七对子、国士 best = min(best, normalShanten(hand, melds)) if melds == 0 { best = min(best, chiitoiShanten(hand)) best = min(best, kokushiShanten(hand)) } return best }这段结构说明的是:向听数不是单一算法,而是多个牌型算法取最优。hand用 34 长度数组而不是切片,是为了避免频繁分配内存,这在实时计算里很关键。melds参数决定是否跳过七对和国士,因为副露后这两种牌型不可能成立。如果你自己改代码,最容易出错的地方就是忘了在副露后禁用特殊牌型判断,导致算出不可能的向听数。2.2 防守模块:危険度是怎么从舍牌和副露里推出来的防守比牌效更难做,因为它涉及对对手手牌的推测,而推测本身是不确定的。mahjong-helper 的防守模块走的是统计路线:根据对手的舍牌顺序、副露内容、立直宣言,给每张可能打出的牌打一个危険度分数。具体来说,它会看几个信号。一是筋牌(suji),对手立直后,某些牌因为筋的关系相对安全,比如对手切过 4 和 7,那 1 和 4 的危険度会下降。二是壁(壁),某张牌四张里已经出现了三张甚至四张,那相关的顺子就做不成了,相邻牌的危険度下降。三是对手的副露方向,如果对手碰了一堆万子,那万子的危険度整体上升。四是巡目,终盘的危険度权重比序盘高得多。这些信号单独看都不新鲜,难的是怎么加权。不同工具的权重设计不一样,有的偏保守,有的偏激进。mahjong-helper 的做法是把这些信号归一化后加权求和,输出一个 0 到 1 之间的危険度值。你在实际用的时候会发现,它的防守建议有时候偏保守,序盘就会让你切一些看起来还能用的牌,这是权重设计的取舍,不是 bug。// 伪代码示意:单张牌的危険度评估 func dangerScore(tile int, opponent OpponentInfo, turn int) float64 { score := 0.0 score += sujiFactor(tile, opponent.discards) // 筋牌因子 score += wallFactor(tile, opponent.discards) // 壁因子 score += meldFactor(tile, opponent.melds) // 副露因子 score *= turnWeight(turn) // 巡目权重 return clamp(score, 0, 1) }这里turnWeight是个乘数而不是加数,意味着巡目越晚,所有信号的危険度都被放大。clamp保证输出在 0 到 1 之间,方便后续排序。如果你觉得它太保守,可以调turnWeight的曲线,让序盘的权重更低;如果觉得太激进,就反过来。但要注意,改权重会同时影响所有牌型,不能只针对某一种情况调。2.3 平台适配层:雀魂和天凤的数据是怎么接进来的mahjong-helper 支持雀魂和天凤,但这两个平台的接入方式完全不同。天凤有公开的牌谱格式和协议文档,社区里有很多现成的解析工具,接入相对标准化。雀魂没有官方协议文档,社区靠的是抓包分析和逆向,稳定性会差一些,平台更新后可能失效。适配层的职责是把平台的对局数据转成内部统一的数据结构,包括手牌、场况(自风、场风、巡目、剩余牌数)、对手信息(舍牌、副露、立直状态)。这个转换层是整条链路里最脆弱的一环,因为平台一改协议,这里就得跟着改。我一般会先把适配层单独跑通,确认能稳定读到一局完整对局的数据,再去调牌效和防守的参数,否则你根本分不清是算法问题还是数据问题。# 常见做法:先单独测试平台适配层,确认数据能读进来 # 以天凤为例,用社区解析库读一份牌谱,打印手牌和场况 go run ./cmd/parse-tenhou -file test_hanchan.json -verbose # 输出应包含每巡的手牌、摸牌、舍牌、副露事件这个步骤的目的是隔离问题。如果你直接跑完整助手,发现建议不对,可能是适配层读错了手牌,也可能是牌效算错了,混在一起很难排查。先确认数据层没问题,后面的调试才有意义。-verbose会打印每一巡的原始数据,方便你对照牌谱确认。3. 跑起来:从编译到实时对局接入的完整步骤3.1 环境准备与编译:Go 版本和依赖怎么选mahjong-helper 是 Go 项目,编译前先确认 Go 版本。日麻计算对性能敏感,建议用较新的稳定版,太老的版本可能在并发和内存分配上有差距。依赖管理用的是 Go Modules,拉下来之后go mod download把依赖装齐。# 确认 Go 版本,建议 1.20 以上 go version # 克隆项目后进入目录,下载依赖 cd mahjong-helper go mod download # 编译主程序,输出到 bin 目录 go build -o bin/mahjong-helper ./cmd/helper编译本身不复杂,但有几个点要注意。一是如果项目里有 cgo 依赖(某些平台适配可能用到),交叉编译会麻烦,建议在目标平台上直接编译。二是编译产物的大小和启动速度,Go 的静态编译产物通常几 MB 到十几 MB,启动很快,适合常驻。三是如果你要改代码,先跑一遍go test ./...确认基线测试通过,再动手,否则改出问题分不清是谁的锅。3.2 配置参数:哪些值该调,哪些别乱动跑起来之后第一件事是看配置文件。mahjong-helper 的配置一般分几块:平台接入配置(选雀魂还是天凤、端口、认证信息)、牌效参数(是否启用七对、国士、是否考虑鸣牌)、防守参数(危険度权重、巡目曲线)、输出配置(显示方式、刷新频率)。参数类别典型参数建议值说明平台接入platformtenhou / majsoul按你实际打的平台选牌效enable_chiitoitrue门清时启用七对判断牌效enable_kokushitrue门清时启用国士判断牌效consider_meldtrue是否把鸣牌进张算进有效进张防守danger_weight0.5危険度在综合建议里的权重防守turn_curveaggressive巡目权重曲线,可选 conservative输出refresh_ms200界面刷新间隔,太低会吃 CPU调参的原则是:先别动,用默认值打几局,记录哪些建议你觉得不对,再针对性调。最常见的调整是danger_weight,如果你发现自己总是因为太保守而错过和牌,就调低;如果总是点炮,就调高。turn_curve影响的是序盘和终盘的权重分配,aggressive在序盘更敢进攻,conservative更早转入防守。提示:改配置后建议先用牌谱回放验证,别直接上实战。回放能让你对照已知结果判断参数改得对不对,实战里你没法确认建议是对是错。3.3 实时对局接入:数据流是怎么走的实时接入的链路是:平台客户端 → 适配层读取对局数据 → 转成内部结构 → 牌效引擎和防守模块并行计算 → 输出层渲染建议。这条链路里,延迟主要来自适配层的读取频率和计算层的耗时。# 启动助手,指定平台和输出方式 ./bin/mahjong-helper -platform tenhou -output console -refresh 200 # 如果平台需要额外认证,通过环境变量传入,不要写进配置文件 export MAHJONG_TOKEN=your_token_here ./bin/mahjong-helper -platform majsoul -output web -port 8080-refresh 200表示每 200 毫秒刷新一次建议,这个值太低会吃 CPU,太高会感觉建议滞后。-output支持 console 和 web 两种,console 适合本地快速看,web 适合多窗口或者想看得更清楚。认证信息用环境变量传,不要写进配置文件,避免泄露。接入之后先别急着实战,找一局牌谱回放,看助手的建议和你自己的判断差在哪。如果差得离谱,先检查适配层读到的数据对不对;如果只是细节不同,那是权重和策略的差异,属于正常范围。4. 避坑与排查:那些让我重新跑一遍日志的瞬间4.1 向听数算出来是负数或者明显不对现象:手牌明明还没听牌,助手显示向听数是 -1 或者 0。原因通常是手牌张数不对,比如适配层多读了一张或者少读了一张,导致引擎按错误的张数计算。也可能是副露数没传对,引擎以为还有门清状态,把七对或国士算进去了。解决:先在适配层打印原始手牌,数一遍张数是不是 13(或者副露后的正确张数)。再检查melds参数有没有正确传递。如果数据没问题,那就是引擎的特殊牌型判断有 bug,临时关掉enable_chiitoi和enable_kokushi看是否恢复正常,能定位到具体是哪个算法的问题。4.2 防守建议在序盘就让你切掉好牌现象:开局没几巡,助手就建议切掉看起来很有用的牌,理由是危険度高。原因通常是turn_curve设成了conservative,或者danger_weight太高,导致序盘的危険度权重被放大。另一个可能是对手的副露被误读,比如把正常的舍牌当成了副露信号。解决:先确认turn_curve和danger_weight的值,序盘阶段危険度不应该主导决策。如果参数没问题,检查对手信息里副露数据是否正确。我一般会在序盘把danger_weight临时调到 0.2 以下,确认牌效建议是否合理,再逐步调回去。4.3 平台更新后助手读不到数据现象:之前能正常跑,平台更新后助手没反应,或者读到的数据全是空的。原因基本可以确定是平台协议变了,适配层的解析逻辑对不上。雀魂这类没有官方协议的平台,更新后适配层失效是常态。解决:先看适配层有没有报错日志,确认是连接问题还是解析问题。如果是解析问题,需要对照新的数据格式改解析逻辑。这时候社区通常也会有人跟进,可以看看有没有更新的适配层代码。在适配层修好之前,助手是没法用的,别在算法层浪费时间。4.4 计算延迟高,建议跟不上对局节奏现象:对局里助手的建议明显滞后,你打完一张牌它才更新。原因可能是refresh_ms设得太低导致 CPU 抢占,也可能是牌效引擎的搜索没有剪枝,单次计算太慢。还有一种可能是适配层读取频率太高,把 IO 堵住了。解决:先把refresh_ms调到 300 到 500 试试,看延迟是否改善。如果没改善,用 profiling 工具看计算层的耗时分布,确认是哪个环节慢。牌效引擎如果没做缓存,同一手牌反复算会浪费很多时间,加一层手牌哈希缓存通常能明显改善。4.5 回放正常但实战不对现象:用牌谱回放时建议都合理,一上实战就不对。原因通常是实战的数据流和回放不一样,回放是一次性读完整局,实战是流式读取,中间可能有事件丢失或者顺序错乱。也可能是实战里对手信息更新不及时,导致防守判断基于过时数据。解决:在实战模式下打印每一巡的原始事件,和回放对比,看是否有事件丢失或顺序问题。流式读取要确保事件按顺序处理,不能并行处理同一局的事件。如果是对手信息更新问题,检查适配层的事件推送频率,必要时降低刷新间隔。5. 进阶:把助手当训练工具而不是拐杖用了一段时间之后,我最大的体会是:这个工具的价值不在于对局里替你做决定,而在于对局后帮你复盘。实战里你来不及细想每一打的得失,但回放可以。我一般会这么用:打完一局,把牌谱导进去,让助手逐巡给出建议,然后对照我自己的选择,看差在哪。差得多的那几巡,就是我要重点想的地方。具体做法是写一个小脚本,把牌谱里的每一巡抽出来,分别跑牌效和防守计算,输出一个对照表。# 伪代码:逐巡对照助手建议和自己的选择 import subprocess, json def review_hanchan(record_path): turns = load_record(record_path) # 加载牌谱,返回每巡数据 for turn in turns: # 调用助手计算当前巡的建议 result = subprocess.run( ["./bin/mahjong-helper", "-replay", "-turn", str(turn.index)], capture_output=True, text=True ) suggestion = json.loads(result.stdout) # 对照自己的选择和助手建议 if suggestion["discard"] != turn.my_discard: print(f"第{turn.index}巡: 我切{turn.my_discard}, 助手建议{suggestion['discard']}") print(f" 牌效: {suggestion['shanten']}向听, 有效进张{suggestion['ukeire']}") print(f" 危険度: {suggestion['danger']}")这个脚本的关键是-replay和-turn参数,让助手在指定巡上做一次计算而不是实时跑。suggestion里包含切牌建议、向听数、有效进张和危険度,对照自己的选择就能看出差异。差异大的地方,往往是你的牌效或者防守判断有系统性偏差。除了复盘,还有一个用法是练牌效。找一些典型的牌型,比如「一向听但进张很窄」「两向听但进张很宽」,让助手算一遍,然后自己手算一遍,看差在哪。手算能帮你建立直觉,助手能帮你验证直觉。两者结合,比单纯依赖工具或者单纯靠自己摸索都快。注意:助手给出的建议是基于统计和搜索的,不是绝对正确。日麻里很多局面没有唯一正确答案,助手的建议只是参考。真正要练的是理解它为什么这么建议,而不是记住它的结论。从那以后我每次打完一局,只要觉得哪里不对劲,都会把牌谱导进去跑一遍逐巡对照。这个习惯帮我改掉了好几个自己没意识到的坏毛病,比如序盘太早放弃门清、终盘防守判断太激进。工具本身不会让你变强,但用它来照自己的盲区,效果比闷头打要好得多。希望帮到你。本文还有配套的精品资源,点击获取