feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
// 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 0–100 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user