饥荒驯牛与三元相图:把游戏设定搬进数学

2824 字
14 分钟
饥荒驯牛与三元相图:把游戏设定搬进数学

前段时间玩《饥荒联机版》,想驯一头牛。驯牛要十几二十天,期间牛会根据你怎么对待它,慢慢偏向战牛、行牛或肥牛三种形态之一,三种都不偏就是普通牛。

我先去查了 wiki 上的驯牛规则。读完之后,脑子里隐约有个三角形。这篇就是顺着这个三角形想下去的过程,后半部分是按游戏代码算出来的驯普通牛方法。

一、wiki 上的规则#

驯化期间,牛身上记着三个数,叫驯势点数,下文分别用三个字母表示:

  • O:战牛(Ornery)驯势点数,骑在牛上打架时涨;
  • R:行牛(Rider)驯势点数,骑着牛时涨;
  • P:肥牛(Pudgy)驯势点数,牛吃得饱时涨。

驯化度到 100% 后再喂一次,形态就锁定了。判定只看三项的占比:

  • 哪一项占到总数的一半,就是哪种牛;
  • 都不到一半,就是普通牛。

下文用小写字母表示占比:

o=OO+R+P,r=RO+R+P,p=PO+R+P,o+r+p=1o = \frac{O}{O+R+P},\quad r = \frac{R}{O+R+P},\quad p = \frac{P}{O+R+P},\qquad o + r + p = 1

比如 O=1, R=2, P=1O = 1,\ R = 2,\ P = 1,占比就是 o=25%, r=50%, p=25%o = 25\%,\ r = 50\%,\ p = 25\%。行牛刚好占一半,这头牛驯完是行牛。

二、从三角形到三元相图#

读完规则,我第一反应是”有点像三角形”:三个东西互相拉扯,像三角形的三个角。但具体哪里像,一开始说不上来。

后来反复琢磨,抓住的是 o+r+p=1o + r + p = 1 这一点。三个数加起来恒为 100%,这和正三角形的一个性质对得上:正三角形内任意一点,到三条边的距离之和恒等于三角形的高。(这叫维维亚尼定理,证明很简单:把这个点和三个顶点连起来,大三角形被分成三个小三角形,三块面积加起来等于总面积,底边又都一样长,所以三个高加起来就是大三角形的高。)

把高当作 100%,三角形里的每个点就对应一组 (o,r,p)(o, r, p)。约定每个占比是这个点到对应顶点对边的距离:越靠近”战牛”那个角,离它对边越远,oo 越大。

正三角形三个顶点分别是战牛 O、行牛 R、肥牛 P,三角形内一点向三条边作垂线,长度分别是 o=20%、r=45%、p=35%,三者之和等于高,也就是 100%
正三角形三个顶点分别是战牛 O、行牛 R、肥牛 P,三角形内一点向三条边作垂线,长度分别是 o=20%、r=45%、p=35%,三者之和等于高,也就是 100%

按这个思路一查,原来这东西早就有名字,叫三元相图(ternary diagram),化学、材料、土壤分类里常用来表示三种成分的配比。

接下来把 wiki 里那个关键的 50% 放进图里。o=50%o = 50\% 意味着点到对边的距离是高的一半,所有这样的点连成一条和对边平行、正好在半高处的线。这条线经过另外两条边的中点,也就是三角形的中位线。

三个角各有一条这样的中位线,各自切下一个角上的小三角,那里是战牛、行牛、肥牛。三条中位线围出来的,是中间一个倒着的小三角:普通牛区。这个小三角的中心,正好是大三角形的中心,也就是 o=r=p=13o = r = p = \frac{1}{3} 那个点。

三元相图:三个角上的小三角分别是行牛、战牛、肥牛区域,中间倒三角是普通牛区,中心点是 1:1;左边中点标出肥牛点数为 0 时普通牛区缩成的那一个点
三元相图:三个角上的小三角分别是行牛、战牛、肥牛区域,中间倒三角是普通牛区,中心点是 1:1;左边中点标出肥牛点数为 0 时普通牛区缩成的那一个点

到这里,游戏里的一条规则就完整变成了一幅几何图。我觉得这个过程挺有意思:wiki 上写的是一句话,搬到数学里,变成了正三角形、垂线和中位线。

三、四块一样大,但均衡很难#

三条中位线把大三角形分成了四个全等的小三角,四种牛的区域面积一模一样,各占四分之一。如果在三角形里随手撒一个点,落进普通牛区的机会和落进任何一个角一样大。

可是放到生活里,这种均衡并不多见。最像的是时间分配:一天就 24 小时,工作、家庭、自己三块加起来恒等于 100%,和 o+r+p=1o + r + p = 1 是一回事。

  • 某一块占到一半以上,生活就偏向一个角:工作占满的人,围着家转的人,只顾自己的人。
  • 三块都不过半,才是常说的”平衡”。

图上平衡区占了四分之一,看起来不小,可现实里能长期待在中间的人并不多。我觉得原因和驯牛是一样的:

  • 状态是攒出来的,不是选出来的。 牛驯完是什么形态,是二十天里每一次骑、每一次喂、每一次打攒出来的;生活偏向哪里,也是每一天的时间一点点攒出来的,没有哪一天专门决定它。
  • 不刻意就会偏。 驯牛时不管它,牛会被你习惯的玩法拉向某个角,爱跑图的得到行牛,爱打架的得到战牛。时间也一样,不主动安排,就会被最急的那件事拉走。

所以四种牛里,普通牛看起来最普通,其实是最需要用心的那一种:三头都得照顾到。

四、驯普通牛:按代码算#

下面的数值都来自游戏脚本(tuning.lua、prefabs/beefalo.lua、components/hunger.lua、components/domesticatable.lua),换算成一天(8 分钟,480 秒):

驯势怎么涨涨多少一天最多
战牛 O骑在牛上时,你攻击一次,或牛挨打一次每次 +0.004看打多少下
行牛 R骑在牛上每秒 +1/24000.2(骑满全天)
肥牛 P饥饿值高于一半时消化食物每消化 1 点饥饿 +1/24000.125(全天都高于一半)

几点说明:

  • 战牛只算骑着的时候。 不骑时牛挨打,不涨战牛点数。

  • 肥牛涨得比行牛慢。 tuning.lua 里两者系数相同,都是 1/(total_day_time*5)。但行牛乘的是骑行秒数;肥牛在 beefalo.lua 里乘的是饥饿值的变化量:

    if data.oldpercent > 0.5 then
    inst.components.domesticatable:DeltaTendency(TENDENCY.PUDGY, TUNING.BEEFALO_PUDGY_WELLFED * -data.delta)
    end

    hunger.lua 里 delta = self.current - old,是饥饿值的绝对点数。牛的饥饿上限 375,每天固定掉 300,所以肥牛一天最多 300/2400=0.125300/2400 = 0.125。灰机 wiki 上写的是”每天 20%“,和代码对不上。

  • “一半”包含等于。 代码里是 maxtendencyval * 2 < tendencysum 才算普通牛,所以恰好占一半也会偏过去。另外总点数不足 0.1 时也是普通牛,不过驯满一次远超这个数。

  • 驯势每天会衰减 1/45(约 0.022)。 这个衰减一直在跑,不只是没人照顾的时候。

普通牛 = 三个点数能拼成一个三角形#

“没有哪一项达到总数一半”,等价于”每一项都小于另外两项之和”:

O<R+P,R<O+P,P<O+RO < R + P,\qquad R < O + P,\qquad P < O + R

这正好是三角形不等式。所以普通牛还有一个很形象的判定:把 O、R、P 当成三根木棍的长度,能拼成一个三角形,就是普通牛。

反过来,如果某一项为 0,比如完全不喂到半饱以上(P=0P = 0),剩下两根木棍无论多长都拼不出三角形。在相图上,这对应战牛和行牛之间的那条边:普通牛区在这条边上只剩一个中点,而按代码,恰好相等也会偏过去,连这一个点都不算。所以三项都得有,少一项就没有容错。

每天打多少下#

骑多久、喂多少一般是自己先定好的,打架次数最好调。把三角形不等式换成战斗点数的范围:

∣R−P∣<O<R+P|R - P| < O < R + P

这个窗口宽 2min⁡(R,P)2\min(R, P),中点是 max⁡(R,P)\max(R, P)。换算成每天的战斗次数(除以 0.004):

每天怎么养R/天P/天每天战斗次数最稳的次数
骑满全天,饥饿值全天高于一半0.20.125约 19~8150
骑 5 分钟,饥饿值全天高于一半0.1250.125约 0~6231
骑满全天,饥饿值只有半天高于一半0.20.0625约 35~6550
骑满全天,从不喂到一半以上0.20无解—

第一种情况画在图上,就是一条从”行牛—肥牛”边出发、往战牛角走的线:打得太少还在行牛区,打得太多进了战牛区,中间那段是普通牛。

骑满全天且饥饿值全天高于一半时,随着每天战斗次数增加,点从行牛和肥牛之间的边出发,穿过普通牛区进入战牛区;进出普通牛区的位置大约是每天 19 次和 81 次,中点是每天 50 次
骑满全天且饥饿值全天高于一半时,随着每天战斗次数增加,点从行牛和肥牛之间的边出发,穿过普通牛区进入战牛区;进出普通牛区的位置大约是每天 19 次和 81 次,中点是每天 50 次

第二种是我最推荐的:每天骑 5 分钟(300 秒 × 1/2400 = 0.125,正好等于全天饱食的肥牛点数),让 R=PR = P,再在骑行时打 31 下左右,三项就落在三角形正中心。这时窗口是 0~62 次,下限直接降到 0。在 R 和 P 总量差不多的方案里,这是最宽的。哪天多打少打十几下,都不会出事。

算上每天约 0.022 的衰减,下限会抬高、上限会压低,各收窄约 6 下:第一种大约 24~76 次,第二种大约 6~57 次。

窗口宽度只取决于 R 和 P 中较小的那个。想要更大的容错,就补短板:肥牛少了就多喂,行牛少了就多骑。每多让 100 点饥饿在半线以上被消化(约一份蒸树枝),P 多 1/24,战斗次数的窗口两边各宽约 10 下。

怎么让饥饿值全天高于一半#

饥饿上限 375,一半是 187.5,每天掉 300,也就是每三分之一天(160 秒)掉 100。

我的做法是:早上把牛喂到 300,之后每三分之一天补 100。 饥饿值会在 200 到 300 之间来回,全天都高于 187.5,一天正好喂 300,大约三份蒸树枝。

喂食时有两个边界值得注意:

  • 喂完超过 300(80%)会放屁。 这只是提醒快饱了,没有惩罚。
  • 饥饿值满 375 时再喂会吐。 代价是驯化度 −1%、顺从度 −30%,同时肥牛点数 +0.02。想要普通牛,要避开这一下。

每次喂食还能 +10% 顺从度,顺带解决骑行时被甩下来的问题。

其他#

  • 驯化度:牛被骑着、或饥饿值大于 0 时,每 10 秒 +1/960,20 天驯满;每天刷一次毛再 +1/60,能缩到 15 天。上面的每日窗口和总天数无关,15 天、20 天都一样用。
  • 骑行时间:没驯完的牛,骑不了多久就会把你甩下来,重新骑上去就行,行牛点数只看骑在牛上的秒数。
  • 舔盐块:把牛留在舔盐块旁,驯化度和驯势都会暂停衰减,适合中途有几天顾不上的时候。

关于这篇文章:游戏数值来自 GitHub 上的游戏脚本镜像 CN-DST-DEVELOPER/scripts,规则描述参考了灰机 wiki 的皮弗娄牛页面和 wiki.gg 的 Beefalo/DST 页面,查阅于 2026 年 10 月。游戏更新后数值可能会变。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

饥荒驯牛与三元相图:把游戏设定搬进数学
https://tracerzzz.cn/posts/dst-beefalo-ternary-diagram/
作者
tracerZzz
发布于
2026-10-25
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
一家多诊室的医院:用类比理解 Go 的 GMP 调度
Go用"医院看病"的类比讲清楚 Go 的 GMP 调度:G、M、P、全局队列、偷任务、抢占、系统调用、netpoller 和 sysmon 各自做什么,顺带回顾用户态和内核态,对比 Python asyncio、Node.js 和 Go 的调度分层,最后用两个小程序亲眼看看调度器在干什么。
2
让 Mac 和 iPhone 指定网站走 eSIM:sing-box 分流实录
工具家里 Wi-Fi 访问一部分境外网站不稳定,iPhone 上的境外 eSIM 访问正常但流量很少。这篇记录怎么让 Mac(通过手机热点)和 iPhone 自己都只把指定网站分给 eSIM,其他流量照常走 Wi-Fi。最后两台设备共用一份 sing-box 配置,用 iCloud 同步,规则全部来自社区规则集,基本不用手动维护。附完整配置、一路补过的漏洞和换设备时的快速清单。
3
给 2016 款 MacBook Pro 装上 macOS Sonoma:OpenCore Legacy Patcher 实录
工具家里有一台 2016 款 15 英寸 MacBook Pro,官方最高只能升到 macOS 12,装不了要求 macOS 13 以上的新软件。用 OpenCore Legacy Patcher 把它升到了 Sonoma 14,这篇记录选版本的考虑、安装步骤,以及装完后遇到的屏幕闪烁、Wi-Fi 消失、指纹失效、iCloud 退不出去、睡眠唤醒死机这些坑。
4
让医生别闲着:用 Python 协程把成本数据产出提速十倍
工作在百度云 CDN 做成本分摊时,日均要对 Palo 发 80 万次以上的查询,大部分时间都花在等结果上。这篇用"医院看病"做类比讲清楚异步非阻塞,顺带对比多进程、多线程、协程三种并发方式,最后讲我先用单进程协程(asyncio + aiomysql)并发查询,再加上多进程、用队列汇总结果,把数据产出时间缩短到原来的十分之一的过程;最后对比浏览器、Node.js 和 Python asyncio 的事件循环调度原理。
5
CDN 新客户接入前的价格测算:用波形拟合算出最低成本价
工作火山 CDN 经营分析平台上的价格测算功能:销售拿到一个新客户的带宽打点,平台把它和大盘拟合,判断客户是否错峰、算出边际超卖倍数,再结合资源诉求的加权成本和包头系数,给出不同计费方式下的最低成本价。这篇讲它的输入、计算过程、示例和后续的优化方向。
随机文章随机推荐

评论区

Profile Image of the Author
tracerZzz
想当木匠的程序员!
分类
标签
站点统计
文章
19
分类
9
标签
48
总字数
79,096
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录