# 格局判定 (/zh/docs/go/patterns)

本命与运限的格局命中、PatternConfig 口径、Pattern 常量与错误处理。



格局是「盘上某几颗星按特定方式凑在一起」的模式识别。判定在本命盘与运限盘上共用同一套规则，
共 64 条。什么是格局、每条规则的条件与来源，见[概念页](/zh/docs/guide/concepts/patterns)。

```go
chart, err := iztro.BySolar("1985-5-3", 9, iztro.GenderMale, true, iztro.LanguageZhCN, nil)
hits, err := chart.Patterns(nil)
```

<Callout type="info">
  本页示例统一用 `"zh-CN"` 的本命盘，因此输出里的展示值都是中文。
</Callout>

## 类型 [#类型]

### PatternHit [#patternhit]

| 字段              | 类型              | 说明                            |
| --------------- | --------------- | ----------------------------- |
| `Key`           | `string`        | 语言无关格局标识，取值即 `PatternXxx` 常量  |
| `Name`          | `string`        | 格局名称，按排盘语言翻译                  |
| `Scope`         | `string`        | 判定视角：本命为 `ScopeOrigin`，运限为该层  |
| `PalaceIndex`   | `int`           | 成格所在的宫位索引（0-11，寅宫为 0）         |
| `PalaceName`    | `string`        | 该宫在这个视角下的宫名                   |
| `PalaceNameKey` | `string`        | 宫名标识，取值即 `PalaceXxx` 常量       |
| `Variant`       | `string`        | 多口径格局命中的口径；单口径为空串             |
| `Broken`        | `bool`          | 「破格 / 加杀平常」条件是否触发。成格照报，这里只作标记 |
| `Stars`         | `[]PatternStar` | 参与成格的星与落宫                     |

三个方法：

| 方法                                    | 说明                               |
| ------------------------------------- | -------------------------------- |
| `Is(patternKey string) bool`          | 是否为指定格局，传 `PatternShaPoLang` 等常量 |
| `InPalace(nameKeyOrName string) bool` | 成格宫位是否为指定宫名，传宫位标识或当前语言宫名         |
| `String() string`                     | `格局名(宫名)` 或 `格局名(宫名,口径)`，便于日志与调试 |

### PatternStar [#patternstar]

| 字段                             | 类型       | 说明                          |
| ------------------------------ | -------- | --------------------------- |
| `Key`                          | `string` | 语言无关星耀标识                    |
| `Name`                         | `string` | 星耀名称，按排盘语言翻译                |
| `PalaceIndex`                  | `int`    | 该星**真正待的**宫位索引（借宫时不是借到的那一宫） |
| `Brightness` / `BrightnessKey` | `string` | 亮度显示文本与标识；无亮度为空串            |
| `Mutagen` / `MutagenKey`       | `string` | 判定视角下的四化与标识；无四化为空串          |

### PatternConfig [#patternconfig]

判定口径。凡是「同一格局的多种成立形式」都走 `PatternHit.Variant`，这里只放会改变
**事实判定本身**的数据口径，因此只有三个字段。

```go
type PatternConfig struct {
    BrightnessSource string // BrightnessSourceTable（默认）或 BrightnessSourcePositional
    Borrow           *bool  // 空宫是否借对宫主星参与判定；nil 取内核默认 true
    FlowStars        *bool  // 运限视角下流曜是否等同对应本命辅星；nil 取内核默认 true
}

func Bool(v bool) *bool                    // 布尔字面量取地址的便捷构造
func DefaultPatternConfig() *PatternConfig // 三项全显式的默认口径
```

布尔两项是 `*bool`：`nil` 表示「没说」，由内核取默认值 `true`，
显式关掉写 `iztro.Bool(false)`。`DefaultPatternConfig()` 返回
`{BrightnessSourceTable, Bool(true), Bool(true)}`，与传 `nil` 同义。

`BrightnessSourceTable` 按星盘亮度表（庙旺为明，陷与「不」为暗，与 iztro 逐值一致），
`BrightnessSourcePositional` 按传统位置（太阳寅至午明、酉至丑暗；太阴酉至丑明、卯至未暗）。
两者的取舍见[概念页的说明](/zh/docs/guide/concepts/patterns#日月的明暗按哪张表)。

<Callout type="info" title="零值即默认口径">
  `&iztro.PatternConfig{}` 的三个字段都是零值（空串与 `nil`），语义与传 `nil` 完全一致。
  只改一项直接写字面量，没写的仍取内核默认：

  ```go
  cfg := &iztro.PatternConfig{BrightnessSource: iztro.BrightnessSourcePositional}
  onlyNatal := &iztro.PatternConfig{FlowStars: iztro.Bool(false)}
  ```
</Callout>

### Pattern 常量 [#pattern-常量]

64 个格局的语言无关标识都有具名常量，命名为 `Pattern` 加驼峰拼音：
`PatternShaPoLang`、`PatternFuXiangChaoYuan`、`PatternFengYunJiHui` 等，
取值即 `PatternHit.Key`。判断格局一律用常量，不要比 `Name`——
`Name` 随排盘语言变，`Key` 不变。

***

## Patterns [#patterns]

**用途**　取本命盘的全部格局命中。

**斗数含义**　把这张盘上成立的所有有名字的星耀组合列出来，附上成格的宫位与证据星。

**签名**

```go
func (a *Astrolabe) Patterns(config *PatternConfig) ([]PatternHit, error)
func (a *Astrolabe) PatternsContext(ctx context.Context, config *PatternConfig) ([]PatternHit, error)
```

**参数**

| 参数       | 类型                | 必填          | 默认 | 说明               |
| -------- | ----------------- | ----------- | -- | ---------------- |
| `config` | `*PatternConfig`  | 是           | —  | 判定口径；传 `nil` 取默认 |
| `ctx`    | `context.Context` | Context 版必填 | —  | 用于取消等待 wasm 实例   |

**返回值**　`[]PatternHit`——按来源页条目顺序排列；一条格局也不成立时为空切片。
本命盘上两条行运格（禄衰马困、风云际会）永远不出现。

**错误**

| 情况                            | 错误                                                  |
| ----------------------------- | --------------------------------------------------- |
| 星盘为 `nil`                     | `iztro: patterns: nil astrolabe`                    |
| `BrightnessSource` 不是两个合法取值之一 | `iztro: invalid patternConfig: unknown variant ...` |

两者都属于 `ErrInvalidArgument` 类，可用 `errors.Is` 匹配。

**示例**

```go
chart, _ := iztro.BySolar("1985-5-3", 9, iztro.GenderMale, true, iztro.LanguageZhCN, nil)
hits, _ := chart.Patterns(nil)

for _, h := range hits {
    fmt.Printf("%s %d %s broken=%v\n", h.Name, h.PalaceIndex, h.PalaceName, h.Broken)
}
```

**输出**

```text
武贪同行 11 迁移 broken=false
府相朝垣 5 命宫 broken=false
杀破狼 11 迁移 broken=false
禄马交驰 5 命宫 broken=false
左右夹命 5 命宫 broken=false
文贵文华 11 迁移 broken=false
文星朝命 5 命宫 broken=true
文星暗拱 5 命宫 broken=false
文星暗拱 5 命宫 broken=false
```

取一条命中并读它的证据星：

```go
for _, h := range hits {
    if !h.Is(iztro.PatternFuXiangChaoYuan) {
        continue
    }
    fmt.Println(h, h.Variant, h.InPalace(iztro.PalaceSoul))
    for _, s := range h.Stars {
        fmt.Printf("  %s %d %s %s\n", s.Name, s.PalaceIndex, s.Brightness, s.BrightnessKey)
    }
}
```

```text
府相朝垣(命宫,soul_empty) soul_empty true
  天府 9 得 de
  天相 1 陷 xian
```

按口径判：

```go
chart, _ := iztro.BySolar("1985-1-5", 11, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)

cfg := &iztro.PatternConfig{BrightnessSource: iztro.BrightnessSourcePositional}

a, _ := chart.Patterns(nil)
b, _ := chart.Patterns(cfg)
fmt.Println(names(a))   // names 取每条的 Name
fmt.Println(names(b))
```

```text
[禄马交驰 左右夹命 坐贵向贵]
[日月并明 禄马交驰 左右夹命 坐贵向贵]
```

**边界与陷阱**

<Accordions>
  <Accordion title="PalaceIndex 未必是命宫">
    多数格局成于命宫，但「身命」类格局（武贪同行、杀破狼、石中隐玉…）命宫身宫各判一次，
    `PalaceIndex` 记实际成格的那一宫，两宫都成立就返回两条命中。
    禄马交驰更是任一宫成立即报，一张盘上可能有多条。
    上面那张盘的身宫落在迁移宫，所以三条记的是迁移宫。
  </Accordion>

  <Accordion title="Stars 里的 PalaceIndex 是星真正待的宫">
    空宫借对宫主星时，`PatternStar.PalaceIndex` 记的是那颗星实际落的宫（对宫），
    不是借进来的宫。要知道格局成在哪一宫看 `PatternHit.PalaceIndex`。
  </Accordion>

  <Accordion title="无值的可选字段是空串">
    `Variant`、`Brightness`、`Mutagen` 这些在没有值时是空串而不是缺字段——
    Go 侧解码自 JSON，可选键在 DTO 里省略，解到结构体上就是零值。
    判断有无用 `h.Variant != ""`。
  </Accordion>
</Accordions>

***

## Horoscope.Patterns [#horoscopepatterns]

**用途**　取某个运限层级视角下的格局命中。

**斗数含义**　以该层的命宫为命宫、合并该层的流曜与四化之后重跑全部规则。
「本命有此组合，大限又走到即享其益」就是这么算出来的。

**签名**

```go
func (h *Horoscope) Patterns(scope string, config *PatternConfig) ([]PatternHit, error)
func (h *Horoscope) PatternsContext(ctx context.Context, scope string, config *PatternConfig) ([]PatternHit, error)
```

**参数**

| 参数       | 类型               | 必填 | 默认 | 说明                             |
| -------- | ---------------- | -- | -- | ------------------------------ |
| `scope`  | `string`         | 是  | —  | 判定视角所在的层级，传 `ScopeDecadal` 等常量 |
| `config` | `*PatternConfig` | 是  | —  | 判定口径；传 `nil` 取默认               |

**返回值**　`[]PatternHit`，每条的 `Scope` 即传入的层级。
传 `ScopeOrigin` 时结果与本命盘上直接调 `Patterns(nil)` 完全一致。

**错误**　运限不是由 `Astrolabe.Horoscope` 发起时返回
`iztro: horoscopePatterns: horoscope must be created by Astrolabe.Horoscope`；
`scope` 不是合法层级标识时返回 `unknown scope`。

**示例**

```go
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
h, _ := chart.Horoscope("2025-6-1", 0)

hits, _ := h.Patterns(iztro.ScopeDecadal, nil)
for _, x := range hits {
    fmt.Printf("%s %s %q\n", x.Name, x.Scope, x.Variant)
}
```

**输出**

```text
杀破狼 decadal ""
风云际会 decadal ""
风云际会 decadal "yearly"
```

同一张盘的本命视角只有一条「府相朝垣」——大限换了命宫，杀破狼才在这一层成立。

**边界与陷阱**

<Accordions>
  <Accordion title="运限视角没有身宫">
    身宫是本命概念。运限视角下「身命」类格局只判该层命宫。
  </Accordion>

  <Accordion title="两条行运格只在运限出现">
    禄衰马困按当前视角那一层判（大限视角判大限，流年视角判流年），
    限命宫三方四正又见七杀（古书严口径同时满足）时 `Variant` 为 `"qisha"`；
    风云际会跨层比较「两限同时逢禄马」，只在 `ScopeDecadal` 视角判一次，
    `Variant` 同时记二限组合与「逢」的松紧：大限 + 小限命中为空串（三方四正会照）或
    `"same_palace"`（两限命宫皆本宫坐禄马的严口径），大限 + 流年命中为
    `"yearly"` 或 `"yearly_same_palace"`。两种组合各报一条，最多两条。
  </Accordion>

  <Accordion title="流曜等同本命辅星">
    默认口径下运禄/流禄当禄存看、运昌/流昌当文昌看，其余同理。
    不想要这个行为，传 `&iztro.PatternConfig{FlowStars: iztro.Bool(false)}`。
  </Accordion>

  <Accordion title="接口无状态">
    `Patterns` 不是在已有的星盘对象上做增量计算，而是把排盘上下文（生日、时辰、性别、
    语言、config）送回 wasm 内核重新发起一次判定。因此它不修改星盘，也不缓存结果——
    在循环里反复调用时自己存一下返回值。
  </Accordion>
</Accordions>

***

## 序列化 [#序列化]

`PatternHit` 与 `PatternStar` 的 JSON tag 就是绑定层 DTO 的键名，
`encoding/json` 直接序列化即得与 Rust、Python 两侧一致的结构：

```go
chart, _ := iztro.BySolar("1985-5-3", 9, iztro.GenderMale, true, iztro.LanguageZhCN, nil)
hits, _ := chart.Patterns(nil)

for _, hit := range hits {
    if !hit.Is(iztro.PatternFuXiangChaoYuan) {
        continue
    }
    b, _ := json.MarshalIndent(hit, "", "  ")
    fmt.Println(string(b))
}
```

```json
{
  "key": "fu_xiang_chao_yuan",
  "name": "府相朝垣",
  "scope": "origin",
  "palaceIndex": 5,
  "palaceName": "命宫",
  "palaceNameKey": "soulPalace",
  "variant": "soul_empty",
  "broken": false,
  "stars": [
    {
      "key": "tianfuMaj",
      "name": "天府",
      "palaceIndex": 9,
      "brightness": "得",
      "brightnessKey": "de"
    },
    {
      "key": "tianxiangMaj",
      "name": "天相",
      "palaceIndex": 1,
      "brightness": "陷",
      "brightnessKey": "xian"
    }
  ]
}
```

***

## PatternsToText / Horoscope.PatternsToText [#patternstotext--horoscopepatternstotext]

**用途**　格局命中的语义化文本，每条一行：格局名、命中宫、构成星耀，破格标 `[破格]`。

**签名**

```go
func (a *Astrolabe) PatternsToText(config *PatternConfig) (string, error)
func (h *Horoscope) PatternsToText(scope string, config *PatternConfig) (string, error)
```

各有 `Context` 变体。与 `Patterns` 同一套判定（含重排上下文与判定口径）；
运限版本的宫名按该层重排后的宫名书写，`config` 传 `nil` 取默认口径。

**示例**

```go
text, _ := chart.PatternsToText(nil)
fmt.Print(text)
```

**输出**

```text
- 府相朝垣(命宫): 天府(庙), 天相(庙)
```

本命盘与运限的 `ToText` 已各自带格局节；单独调用适合只要格局摘要的场景。
