Files
digital-psychology/apps/api/internal/star/synastry/synastry.go
T
jackyu66gitandCursor bd22d9dddd feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:37:53 +08:00

134 lines
3.5 KiB
Go
Raw 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.
// Package synastry computes love/friend/marriage match indices from two natal charts.
package synastry
import (
"fmt"
"github.com/yuxingu/digital-psychology/apps/api/internal/star/natal"
)
// Indices are 0100 match scores.
type Indices struct {
Love int `json:"love_index"`
Friend int `json:"friend_index"`
Marriage int `json:"marriage_index"`
LoveNote string `json:"love_note"`
FriendNote string `json:"friend_note"`
MarriageNote string `json:"marriage_note"`
Summary string `json:"summary"`
}
// Compute returns match indices for charts A and B.
func Compute(a, b natal.Chart, aName, bName string) Indices {
if aName == "" {
aName = "我"
}
if bName == "" {
bName = "TA"
}
sunDiff := natal.AngleDiff(a.Sun.Lon, b.Sun.Lon)
moonDiff := natal.AngleDiff(a.Moon.Lon, b.Moon.Lon)
venA := bodyLon(a, "venus")
venB := bodyLon(b, "venus")
marsA := bodyLon(a, "mars")
marsB := bodyLon(b, "mars")
venDiff := natal.AngleDiff(venA, venB)
marsVen := natal.AngleDiff(marsA, venB)
venMars := natal.AngleDiff(venA, marsB)
// Closer aspects (0/60/120 soft; 90/180 hard but charged) → higher score blended
love := clamp(58+aspectBoost(sunDiff)+aspectBoost(moonDiff)+aspectBoost(venDiff)+aspectBoost(marsVen)/2+aspectBoost(venMars)/2, 42, 98)
friend := clamp(60+aspectBoost(sunDiff)+aspectBoost(moonDiff)*3/2+elemBoost(a.Sun, b.Sun), 45, 97)
marriage := clamp(55+aspectBoost(sunDiff)+aspectBoost(moonDiff)+aspectBoost(venDiff)+houseBoost(a, b), 40, 96)
return Indices{
Love: love, Friend: friend, Marriage: marriage,
LoveNote: fmt.Sprintf("%s与%s恋爱指数 %d:太阳相距约%.0f°,金星互动影响吸引力。", aName, bName, love, sunDiff),
FriendNote: fmt.Sprintf("友情指数 %d:月亮与太阳元素是否合拍,决定轻松感。", friend),
MarriageNote: fmt.Sprintf("婚姻/长期指数 %d:看重稳定与共同节奏,而非一时火花。", marriage),
Summary: fmt.Sprintf("%s × %s:恋爱 %d · 友情 %d · 婚姻 %d", aName, bName, love, friend, marriage),
}
}
func bodyLon(c natal.Chart, key string) float64 {
for _, p := range c.Planets {
if p.Key == key {
return p.Lon
}
}
return 0
}
func aspectBoost(diff float64) int {
// reward conjunction, sextile, trine; mild for square/opposition
targets := []float64{0, 60, 90, 120, 180}
best := 180.0
for _, t := range targets {
d := abs(diff - t)
if d < best {
best = d
}
}
switch {
case best <= 8:
if near(diff, 90) || near(diff, 180) {
return 8
}
return 14
case best <= 12:
return 8
default:
return int(6 - best/30)
}
}
func near(diff, target float64) bool {
return abs(diff-target) <= 10
}
func elemBoost(a, b natal.Body) int {
if a.Element == b.Element {
return 6
}
// fire-air / earth-water traditionally supportive
pair := a.Element + b.Element
if pair == "火风" || pair == "风火" || pair == "土水" || pair == "水土" {
return 4
}
return 0
}
func houseBoost(a, b natal.Chart) int {
// same rising modality → slight boost
if a.Rise.Modality == b.Rise.Modality {
return 4
}
return 0
}
func clamp(v, lo, hi int) int {
if v < lo {
return lo
}
if v > hi {
return hi
}
return v
}
func abs(x float64) float64 {
if x < 0 {
return -x
}
return x
}
// AsMap for JSON.
func (i Indices) AsMap() map[string]any {
return map[string]any{
"love_index": i.Love, "friend_index": i.Friend, "marriage_index": i.Marriage,
"love_note": i.LoveNote, "friend_note": i.FriendNote, "marriage_note": i.MarriageNote,
"summary": i.Summary,
}
}