日本麻将助手mahjong-helper:牌效计算与防守判断实战解析

简介:这份资源是面向日本麻将(立直麻将)爱好者的开源助手工具,使用 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里包含切牌建议、向听数、有效进张和危険度,对照自己的选择就能看出差异。差异大的地方,往往是你的牌效或者防守判断有系统性偏差。

除了复盘,还有一个用法是练牌效。找一些典型的牌型,比如「一向听但进张很窄」「两向听但进张很宽」,让助手算一遍,然后自己手算一遍,看差在哪。手算能帮你建立直觉,助手能帮你验证直觉。两者结合,比单纯依赖工具或者单纯靠自己摸索都快。

注意:助手给出的建议是基于统计和搜索的,不是绝对正确。日麻里很多局面没有唯一正确答案,助手的建议只是参考。真正要练的是理解它为什么这么建议,而不是记住它的结论。

从那以后我每次打完一局,只要觉得哪里不对劲,都会把牌谱导进去跑一遍逐巡对照。这个习惯帮我改掉了好几个自己没意识到的坏毛病,比如序盘太早放弃门清、终盘防守判断太激进。工具本身不会让你变强,但用它来照自己的盲区,效果比闷头打要好得多。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

双编码器与InfoNCE实战:垂类模型Embedding训练的高质量数据工程指南

双编码器与InfoNCE实战:垂类模型Embedding训练的高质量数据工程指南

作为常年扎在垂类模型一线的人,我对“数据工程”这四个字有很深的体会。很多团队把精力都砸在数据清洗和标注上,结果模型训练效果却总差一口气。直到我们把重心从“数据堆量”转向“数据质量与模型结构的匹配设计”,才真正把性能拉了起来。这…

2026/10/20 4:04:24 阅读更多 →
rea:一个无上下文字符串的合规性说明

rea:一个无上下文字符串的合规性说明

项目标题“rea”目前未提供有效上下文、正文描述、关键词列表或摘要说明,输入内容为空白(仅含标题字符串与空的热词/搜索块)。根据你设定的核心创作原则第一条:“忠于原料,合理演绎”,我必须严格以输入内容…

2026/10/20 4:04:24 阅读更多 →
特殊IP地址全解析:从0.0.0.0到169.254的排障指南

特殊IP地址全解析:从0.0.0.0到169.254的排障指南

1. 从一个“ping 不通”的地址说起刚入行那会儿,我在一台测试机上配了个地址,随手敲了个ping 192.168.1.0,结果死活不通,当时第一反应是网线没插好、交换机挂了、防火墙拦了,折腾了小半天,最后被带我的老哥…

2026/10/20 4:04:24 阅读更多 →

最新新闻

永磁同步电机无感FOC实战:龙贝格观测器与PLL锁相环方案解析

永磁同步电机无感FOC实战:龙贝格观测器与PLL锁相环方案解析

前阵子把一套基于龙贝格观测器的永磁同步电机无感FOC方案从仿真搬到了实机台架,整个过程前后折腾了小两个月。核心思路很直接:用龙贝格观测器在线提取电机反电势,再用PLL锁相环从反电势里解出转子位置和速度,完全甩掉编码器和旋变…

2026/10/20 6:48:42 阅读更多 →
深入理解C++ std::string:存储机制、性能陷阱与避坑指南

深入理解C++ std::string:存储机制、性能陷阱与避坑指南

刚开始用C那会儿,我对std::string的理解就是“一个会自动长大的字符数组”。但真正在工程里写多了之后,我发现自己对它的误解比想象中要多。字符串处理几乎无处不在,无论是解析配置、拼接日志,还是做协议编解码,string…

2026/10/20 6:48:42 阅读更多 →
C++模板元编程实现选择排序

C++模板元编程实现选择排序

前言 模板元编程(template metaprogramming, TMP)是在编译期用类型和常量完成计算的技术。它的"变量"是类型与 static constexpr 值,"函数"是类模板特化,"循环"是递归实例化,"条件…

2026/10/20 6:48:42 阅读更多 →
100行代码手写Hermes式智能体框架并接入飞书机器人

100行代码手写Hermes式智能体框架并接入飞书机器人

1. 为什么我要自己动手写一个智能体框架1.1 从“玩具”到“工具”的转折点市面上开源的智能体框架我几乎都翻过一遍。LangChain 生态庞大,但依赖链太长,一个简单的对话循环要引入几十个包;AutoGen 的多智能体协作很惊艳,可一旦要接…

2026/10/20 6:48:42 阅读更多 →
技能管理系统Skills:用数据化技术画像精准定位自身能力短板

技能管理系统Skills:用数据化技术画像精准定位自身能力短板

从面试被问到“你还会什么”那一刻,我才意识到自己的技能清单有多失真。简历上写着“熟悉微服务”,可真聊起服务拆分和容错设计,脑子里能拿出来的只有几个名词;写了三年业务代码,却说不清自己到底擅长什么、短板在哪里…

2026/10/20 6:48:42 阅读更多 →
国产大模型崛起:榜单前20占6席,技术底座与落地实践全解析

国产大模型崛起:榜单前20占6席,技术底座与落地实践全解析

1. 榜单背后的真实格局:国产大模型凭什么挤进前二十全球AI模型榜单前20名里国产模型占了6个,这个数字放在三年前几乎没人敢想。我翻了一圈榜单的评测维度,发现这次上榜的国产模型覆盖了文本理解、代码生成、多模态推理、长上下文处理几个主要…

2026/10/20 6:47:42 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →