近日,我发布了「集」游戏,及其界面源码与训练源码。本文简述其技术要点。
训练
使用 Rust 语言。
智能
智能非常简单:随机出。当然,不是完全随机。
首先,要根据权重随机出。权重根据我方和对方现有的集的数量决定。例如,如果我方没有集而对方有两个,则集的权重比较大。具体的权重,就是算法的参数,也是优化的目标。
其次,提前剔除一定不好的技能。例如,若双方都没有集,则不应出小防;若我方有五个集,则不应出集。以下是定义。
某技能甲,对于任意技能乙,在对方所有技能下甲都不比乙应对更好,且对方出某个技能时乙比甲应对更好,则技能甲一定不好。
技能甲和乙分别对战对方技能。甲赢乙输则甲比乙应对更好,同赢或同输则无好坏之分。若都不分输赢,则花费少者应对更好,花费一样则无好坏之分。
智能实现
权重使用简单的 u8
数组,附带一个映射用于查询数组索引。这使之可以非常方便地存进文件,无需序列化。
若技能出不了(集不够),或一定不好,则不存储。节省空间。
我规定,在我方和对方的集的数量一定的情况下,所有技能的权重之和为 255。于是,可以少存一个权重,加起来用 255 减它就是。
那个映射目前用 EnumMap
实现。我正在写一个库,准备替换掉。本应使用常量,遗憾,不支持。
技能实现
技能是写在代码中的,不可动态配置。但是用 trait 实现,可编译时配置。
对于技能胜负判定,我本想搞缓存。结果一看,发现直接用数组 contains
方法居然更快一点点。应该是编译器优化之故,真神奇。
技能的名字,十分 hack 地用了 Debug
,方便直接 derive。
遗传算法
与十步万度的类似,加入混合而不只是变异。未经优化,十分朴实。懒得优化,能用就行。
界面
使用 Solid.js 框架,Vite 构建。
字体
字体占空间大。发现一个库,font-slice,类似谷歌的字体切片,看着不错,用之。
竖排
不少地滥用了 writing-mode
做临时的竖排。Firefox 支持不佳,我只好手动设置尺寸。
卡片
平时只显示名字,需要时升起,全部显示。如何做到?
我令卡片的顶边为屏幕底部,完全使用 transform
控制位置。我写死名字的高度,平时上移名字的高度,升起时上移 100%。