Files

166 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 领域规则:三枚铜币与六爻
> 状态:MVP 强制规范
> 适用范围:所有起卦算法、显示模型、持久化模型和相关测试
本文件定义本项目唯一允许的计算规则。UI 文案、AI 输出和数据源都不能覆盖这些规则。
实现状态:`coin-v1` 纯 Kotlin 核心位于 `app/src/main/java/net/opcapp/flash/domain/casting/`;`.\gradlew.bat :app:testDebugUnitTest` 覆盖 8 种币面、4,096 种六爻、64 模式、已知夹具和 DTO 往返。记录时间与内容版本在确定性计算完成后附加。
## 1. 术语和类型
建议使用有语义的封闭类型,避免裸 `Int` 在层间传播:
```kotlin
enum class CoinSide(val value: Int) {
CHARACTER(2), // 字面
REVERSE(3), // 背面
}
enum class LineValue(val sum: Int, val polarity: Polarity, val moving: Boolean) {
OLD_YIN(6, Polarity.YIN, true),
YOUNG_YANG(7, Polarity.YANG, false),
YOUNG_YIN(8, Polarity.YIN, false),
OLD_YANG(9, Polarity.YANG, true),
}
enum class Polarity { YIN, YANG }
```
代码片段只表达契约,最终实现的包名和细节以[系统架构](architecture.md)为准。
## 2. 铜币计值约定
MVP 固定采用:
- 字面 `CHARACTER` 计 2;
- 背面 `REVERSE` 计 3;
- 每轮三枚相加,结果只可能是 6、7、8、9。
传统资料对“正/反”“阴/阳”与 2/3 的命名存在不同约定,因此界面不得只写含糊的“正面/反面”。首次使用和投币页必须始终显示当前计值:“字 2,背 3”。如果未来允许切换约定,约定必须在第一轮之前锁定并写入结果;不得在六轮中途改变。
## 3. 爻值映射
| 和值 | 名称 | 本卦爻形 | 是否动爻 | 之卦爻形 |
|---:|---|---|---|---|
| 6 | 老阴 | 阴爻,断线 | 是 | 阳爻,实线 |
| 7 | 少阳 | 阳爻,实线 | 否 | 阳爻,实线 |
| 8 | 少阴 | 阴爻,断线 | 否 | 阴爻,断线 |
| 9 | 老阳 | 阳爻,实线 | 是 | 阴爻,断线 |
强制不变量:
- 偶数 6、8 为阴;奇数 7、9 为阳。
- 只有 6 和 9 为动爻。
- 之卦只翻转动爻;静爻保持不变。
- AI、网络、时间、问题文本和设备状态均不得影响上述映射。
## 4. 六次录入与顺序
数组和数据库中的标准顺序一律为 **bottom-up**:
| 数组索引 | 中文位置 | 投币轮次 |
|---:|---|---:|
| 0 | 初爻 | 1 |
| 1 | 二爻 | 2 |
| 2 | 三爻 | 3 |
| 3 | 四爻 | 4 |
| 4 | 五爻 | 5 |
| 5 | 上爻 | 6 |
绘制界面时可以从屏幕顶部先画上爻,但只能在展示适配层反转;领域对象、序列化数据和测试夹具不得改成 top-down。
一个有效投币过程必须满足:
- 恰好六轮;
- 每轮恰好三枚;
- 每枚只能是 `CHARACTER` 或 `REVERSE`;
- 生成结果后保留原始 18 枚输入,便于复核和审计。
## 5. 本卦与之卦
`CastResult` 至少包含:
```text
methodVersion
coinConvention
rounds[6][3]
lineValuesBottomUp[6]
primaryPatternBottomUp[6]
movingLinePositions[] // 1..6,升序
transformedPatternBottomUp[6]
primaryHexagramId // 文王卦序 1..64
transformedHexagramId // 文王卦序 1..64
contentVersion
createdAt // 只用于记录,不参与计算
```
卦号不能通过“二进制数 + 1”推断。文王卦序不是简单二进制顺序,必须使用经过校验的“上卦 × 下卦”映射表或完整六爻模式映射表。
算法顺序:
1. 将每轮三枚计值相加为 `LineValue`。
2. 按录入顺序形成 bottom-up 六爻。
3. 从各爻阴阳形成本卦模式并查得本卦编号。
4. 收集值为 6 或 9 的位置作为动爻。
5. 仅翻转动爻阴阳,形成之卦模式并查得之卦编号。
6. 先将全部原始输入和确定结果构造成不可变 `CastComputation`,再附加 `methodVersion`、`coinConvention`、`contentVersion` 与 `createdAt` 记录元数据,组成不可变 `CastResult`;元数据不得反向影响步骤 1~5。
## 6. 读取内容的产品规则
MVP 采用透明而不过度裁决的显示策略:
- 始终显示本卦卦辞。
- 无动爻:明确显示“无动爻”;之卦与本卦相同,可弱化重复内容。
- 有动爻:按初爻到上爻显示本卦中所有实际动爻的爻辞,并显示之卦。
- 多个动爻:不由算法擅自选出“唯一主爻”,也不隐藏其他动爻。
- 乾卦六爻皆九、坤卦六爻皆六时,若采用的数据版本含“用九/用六”,应作为特殊文本额外展示;是否纳入 MVP 见[决策记录](decisions.md#未决问题)。
AI 可以组织和解释这些材料,但不得改变显示集合。
## 7. 状态机
```text
DRAFT(question)
└─ start → CASTING(confirmedRounds = 0..5)
├─ edit previous → CASTING
├─ confirm sixth → SEALED(CastResult)
└─ cancel → DRAFT
SEALED
├─ request local explanation → EXPLAINED_LOCAL
├─ consent + request AI → EXPLAINING_AI → EXPLAINED_AI | AI_FAILED
└─ explicit restart → DRAFT(newSessionId)
```
禁止从 `EXPLAINING_AI` 或 `EXPLAINED_AI` 回写 `CastResult`。重新起卦必须创建新的会话标识。
## 8. 必须存在的确定性测试
| 输入(初爻到上爻) | 本卦 | 之卦 | 动爻 |
|---|---|---|---|
| `7,7,7,7,7,7` | 乾 1 | 乾 1 | 无 |
| `8,8,8,8,8,8` | 坤 2 | 坤 2 | 无 |
| `9,9,9,9,9,9` | 乾 1 | 坤 2 | 1–6 |
| `6,6,6,6,6,6` | 坤 2 | 乾 1 | 1–6 |
| `7,8,8,8,8,8` | 复 24 | 复 24 | 无 |
| `9,8,8,8,8,8` | 复 24 | 坤 2 | 初爻 |
此外必须:
- 穷举 8 种三枚铜币排列,验证求和与 `LineValue`。
- 穷举 4⁶ = 4,096 种六爻数值组合,验证长度、动爻和变换不变量。
- 验证 64 种阴阳模式恰好映射到 64 个不重复卦号。
- 验证序列化再反序列化不改变任何确定字段。
## 9. 版本规则
- 初始算法版本建议为 `coin-v1`。
- 改变字/背计值、动爻规则、顺序或卦号映射都属于破坏性领域变更,必须新增版本和决策记录,不能静默覆盖历史结果。
- 内容措辞变化只提升 `contentVersion`,不得改变 `methodVersion`。
参考资料仅用于交叉核验;仓库内上述规则才是实现事实源:
- [三枚铜币产生 6/7/8/9,并自下而上记录](https://uaya.org/learn/iching/using-the-oracle/casting-techniques/three-coins/understanding-results/)
- [三枚铜币法的爻值与动爻说明](https://www.ichingonline.net/instruction.php)