fix(argala): keep equal occupancy unresolved until the two-level tiebreak
Upstream e7301feb, 86935c0a, and 4f1536f2. Ketu reverses the count, the ninth is the fifth's obstruction, and a remaining tie stays strength_comparison_required so the consultation card shows 待定. BUG-1386. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Grok (agent loop)
Cursor
parent
6cbae4ce23
commit
aa87c0f127
+134
-14
@@ -7,7 +7,7 @@ Argala(门闩)行星干预模块 v1.1
|
||||
- 主Argala: 2/4/11宫
|
||||
- Virodhargala: 12/10/3宫分别阻挡2/4/11宫
|
||||
- 特殊Argala: 第3宫有2颗以上凶星
|
||||
- 次级Argala: 5/9宫
|
||||
- 次级:第5宫是 Argala,第9宫是它的 Virodhargala,不再把第9宫另算一条次级 Argala
|
||||
- Argala Rajayoga分类: Poornargala / Tripadargala / Ardhargala / Padargala
|
||||
|
||||
MIT复用来源:
|
||||
@@ -62,9 +62,71 @@ def classify_argala_rajayoga(argala_result: Dict) -> Dict:
|
||||
return {'type': 'None', 'level': 0, 'cn': '无明显Argala'}
|
||||
|
||||
|
||||
def _pair_status(planets, blockers):
|
||||
if not planets:
|
||||
return 'absent'
|
||||
if len(planets) == len(blockers):
|
||||
return 'strength_comparison_required'
|
||||
return 'effective' if len(planets) > len(blockers) else 'obstructed'
|
||||
|
||||
|
||||
def _compare_pair(planets, blockers, sign_idx, block_idx, chart, include_nodes):
|
||||
"""Equal occupancy uses the two Narayana levels already in select_narayana_seed_sign.
|
||||
|
||||
Later strength layers are not ported. A remaining tie stays unresolved.
|
||||
"""
|
||||
status = _pair_status(planets, blockers)
|
||||
if status != 'strength_comparison_required':
|
||||
return status, {'status': 'not_required', 'basis': 'occupant_count'}
|
||||
metadata = dict(
|
||||
profile='rath_argala_role_contacts_primary_lords_v1',
|
||||
source='Rath Jaimini Upadesa Sutra course, source p363',
|
||||
claim_boundary='First two levels only; traditional primary lords; later ties unresolved.',
|
||||
)
|
||||
missing = sorted((NATURAL_BENEFICS | NATURAL_MALEFICS) - set(chart))
|
||||
if include_nodes:
|
||||
missing += sorted(NODES - set(chart))
|
||||
if missing:
|
||||
return status, dict(metadata, status='blocked', reason='missing_classical_planet_signs', missing=missing)
|
||||
try:
|
||||
from scripts.narayana_dasha import select_narayana_seed_sign
|
||||
except ModuleNotFoundError:
|
||||
from narayana_dasha import select_narayana_seed_sign
|
||||
classical = NATURAL_BENEFICS | NATURAL_MALEFICS | (NODES if include_nodes else set())
|
||||
decision = select_narayana_seed_sign(
|
||||
sign_idx,
|
||||
{p: (s % 12) * 30.0 for p, s in chart.items() if p in classical},
|
||||
comparison_sign_idx=block_idx,
|
||||
)
|
||||
decision['implementation_profile'] = decision.pop('profile')
|
||||
decision.update(metadata)
|
||||
if decision['status'] == 'selected':
|
||||
status = 'effective' if decision['selected_sign_idx'] == sign_idx else 'obstructed'
|
||||
else:
|
||||
decision['reason'] = 'later_argala_sign_strength_rules_required'
|
||||
return status, decision
|
||||
|
||||
|
||||
_VERDICT_LABEL = {
|
||||
'strength_comparison_required': '待定',
|
||||
'special_argala_adjudication_required': '待定',
|
||||
'mixed': '吉凶交错',
|
||||
'supported': '支持',
|
||||
'obstructed': '受阻',
|
||||
'neutral': '中性',
|
||||
}
|
||||
|
||||
|
||||
def calc_argala_for_reference(ref_sign_idx: int, planet_sign_indices: Dict[str, int], include_nodes: bool = False) -> Dict:
|
||||
"""计算单一参考点的完整Argala。"""
|
||||
# Ketu in the reference sign reverses counting whether or not nodes count as occupants.
|
||||
direction = -1 if planet_sign_indices.get('Ketu') == ref_sign_idx else 1
|
||||
|
||||
def reference_house(offset):
|
||||
return (ref_sign_idx + direction * (offset - 1)) % 12
|
||||
|
||||
result = {
|
||||
'source_reference_packets': ['science_of_light_v1_argala_reference_points'],
|
||||
'ref_sign': SIGNS[ref_sign_idx],
|
||||
'ref_sign_idx': ref_sign_idx,
|
||||
'primary': {},
|
||||
@@ -73,15 +135,20 @@ def calc_argala_for_reference(ref_sign_idx: int, planet_sign_indices: Dict[str,
|
||||
'argala_count': 0,
|
||||
'virodhargala_count': 0,
|
||||
'net_result': 'neutral',
|
||||
'counting_direction': 'antizodiacal' if direction == -1 else 'zodiacal',
|
||||
'rule_source': 'PVR Integrated Approach §§10.5–10.7, source pages 117–118',
|
||||
}
|
||||
|
||||
for h in sorted(PRIMARY_ARGALA):
|
||||
argala_sign = _house_from(ref_sign_idx, h)
|
||||
argala_sign = reference_house(h)
|
||||
planets = _planets_in_sign(argala_sign, planet_sign_indices, include_nodes)
|
||||
block_h = VIRODHARGALA_MAP[h]
|
||||
block_sign = _house_from(ref_sign_idx, block_h)
|
||||
block_sign = reference_house(block_h)
|
||||
blockers = _planets_in_sign(block_sign, planet_sign_indices, include_nodes)
|
||||
effective = len(planets) > len(blockers)
|
||||
status, comparison = _compare_pair(
|
||||
planets, blockers, argala_sign, block_sign, planet_sign_indices, include_nodes,
|
||||
)
|
||||
effective = None if status == 'strength_comparison_required' else status == 'effective'
|
||||
result['primary'][f'H{h}'] = {
|
||||
'house_from_reference': h,
|
||||
'sign': SIGNS[argala_sign],
|
||||
@@ -91,14 +158,16 @@ def calc_argala_for_reference(ref_sign_idx: int, planet_sign_indices: Dict[str,
|
||||
'blocked_by_sign': SIGNS[block_sign],
|
||||
'blockers': blockers,
|
||||
'effective': effective,
|
||||
'effect': _argala_effect(h, planets, blockers),
|
||||
'comparison_status': status,
|
||||
'strength_comparison': comparison,
|
||||
'effect': _argala_effect(h, planets, blockers, status),
|
||||
}
|
||||
if planets:
|
||||
result['argala_count'] += 1
|
||||
if blockers:
|
||||
result['virodhargala_count'] += 1
|
||||
|
||||
h3_sign = _house_from(ref_sign_idx, 3)
|
||||
h3_sign = reference_house(3)
|
||||
h3_planets = _planets_in_sign(h3_sign, planet_sign_indices, include_nodes)
|
||||
h3_malefics = [p for p in h3_planets if p in NATURAL_MALEFICS]
|
||||
result['specific'] = {
|
||||
@@ -109,25 +178,56 @@ def calc_argala_for_reference(ref_sign_idx: int, planet_sign_indices: Dict[str,
|
||||
'malefics': h3_malefics,
|
||||
'effective': len(h3_malefics) >= 2,
|
||||
'rule': '第3宫有2颗以上天然凶星形成特殊Argala',
|
||||
'method_status': 'parameter_sensitive_two_malefic_threshold',
|
||||
'obstruction_rule': 'Once formed, special third-house Argala has no obstruction (Rath source p363). Formation threshold remains profile-dependent.',
|
||||
}
|
||||
if result['specific']['effective']:
|
||||
result['argala_count'] += 1
|
||||
|
||||
for h in sorted(SECONDARY_ARGALA):
|
||||
sign_idx = _house_from(ref_sign_idx, h)
|
||||
sign_idx = reference_house(h)
|
||||
planets = _planets_in_sign(sign_idx, planet_sign_indices, include_nodes)
|
||||
result['secondary'][f'H{h}'] = {
|
||||
'house_from_reference': h,
|
||||
'sign': SIGNS[sign_idx],
|
||||
'sign_idx': sign_idx,
|
||||
'planets': planets,
|
||||
'role': 'argala' if h == 5 else 'virodhargala',
|
||||
}
|
||||
|
||||
if result['argala_count'] > result['virodhargala_count']:
|
||||
fifth, ninth = result['secondary']['H5'], result['secondary']['H9']
|
||||
secondary_status, comparison = _compare_pair(
|
||||
fifth['planets'], ninth['planets'], fifth['sign_idx'], ninth['sign_idx'],
|
||||
planet_sign_indices, include_nodes,
|
||||
)
|
||||
fifth.update(
|
||||
blocked_by_house=9,
|
||||
blocked_by_sign=ninth['sign'],
|
||||
strength_comparison=comparison,
|
||||
blockers=ninth['planets'],
|
||||
comparison_status=secondary_status,
|
||||
effective=None if secondary_status == 'strength_comparison_required' else secondary_status == 'effective',
|
||||
)
|
||||
ninth['obstructs_house'] = 5
|
||||
|
||||
statuses = [row['comparison_status'] for row in result['primary'].values()]
|
||||
statuses.append(secondary_status)
|
||||
if 'strength_comparison_required' in statuses:
|
||||
result['net_result'] = 'strength_comparison_required'
|
||||
elif 'effective' in statuses and 'obstructed' in statuses:
|
||||
result['net_result'] = 'mixed'
|
||||
elif 'effective' in statuses:
|
||||
result['net_result'] = 'supported'
|
||||
elif result['argala_count'] < result['virodhargala_count']:
|
||||
elif 'obstructed' in statuses:
|
||||
result['net_result'] = 'obstructed'
|
||||
if result['specific']['effective']:
|
||||
result['net_result'] = 'special_argala_adjudication_required'
|
||||
result['verdict_label'] = _VERDICT_LABEL[result['net_result']]
|
||||
result['rajayoga_classification'] = classify_argala_rajayoga(result)
|
||||
result['rajayoga_classification']['method_status'] = 'legacy_occupancy_heuristic'
|
||||
result['rajayoga_classification']['claim_boundary'] = (
|
||||
'Occupancy label only; not an adjudication of obstruction or a verified Rajayoga.'
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@@ -167,24 +267,38 @@ def _summarize_argala(houses: Dict) -> Dict:
|
||||
supported = []
|
||||
obstructed = []
|
||||
strongest = []
|
||||
unresolved = []
|
||||
mixed = []
|
||||
for house_key, data in houses.items():
|
||||
if data['net_result'] == 'supported':
|
||||
supported.append(house_key)
|
||||
elif data['net_result'] == 'obstructed':
|
||||
obstructed.append(house_key)
|
||||
level = data.get('rajayoga_classification', {}).get('level', 0)
|
||||
strongest.append((house_key, level, data.get('rajayoga_classification', {}).get('type')))
|
||||
elif data['net_result'] == 'mixed':
|
||||
mixed.append(house_key)
|
||||
elif str(data['net_result']).endswith('_required'):
|
||||
unresolved.append(house_key)
|
||||
if house_key not in unresolved:
|
||||
level = sum(row.get('effective') is True for row in data['primary'].values())
|
||||
level += int(data['secondary']['H5'].get('effective') is True)
|
||||
strongest.append((house_key, level, 'effective_pair_count'))
|
||||
strongest.sort(key=lambda x: x[1], reverse=True)
|
||||
return {
|
||||
summary = {
|
||||
'supported_houses': supported,
|
||||
'obstructed_houses': obstructed,
|
||||
'top_argala_houses': [{'house': h, 'level': lvl, 'type': typ} for h, lvl, typ in strongest[:5] if lvl > 0],
|
||||
'supported_count': len(supported),
|
||||
'obstructed_count': len(obstructed),
|
||||
'unresolved_houses': unresolved,
|
||||
'mixed_houses': mixed,
|
||||
'ranking_basis': 'effective_pair_count_not_classical_rajayoga_strength',
|
||||
}
|
||||
if unresolved:
|
||||
summary['unresolved_label'] = '待定'
|
||||
return summary
|
||||
|
||||
|
||||
def _argala_effect(house: int, planets: List[str], blockers: List[str]) -> str:
|
||||
def _argala_effect(house: int, planets: List[str], blockers: List[str], status=None) -> str:
|
||||
if not planets:
|
||||
return '无行星形成此类Argala'
|
||||
base = {
|
||||
@@ -192,7 +306,13 @@ def _argala_effect(house: int, planets: List[str], blockers: List[str]) -> str:
|
||||
4: '内在稳定、住所、母亲、教育与幸福感支持',
|
||||
11: '收益、人脉、愿望实现与社会网络支持',
|
||||
}.get(house, '一般支持')
|
||||
if blockers and len(blockers) >= len(planets):
|
||||
if blockers and len(blockers) == len(planets):
|
||||
if status == 'effective':
|
||||
return f'{base},双方数量相等,所选强弱口径判为有效'
|
||||
if status == 'obstructed':
|
||||
return f'{base},双方数量相等,所选强弱口径判为受阻'
|
||||
return f'待定:{base},双方数量相等,须比较强弱后裁决'
|
||||
if blockers and len(blockers) > len(planets):
|
||||
return f'{base},但被{len(blockers)}个Virodhargala阻挡'
|
||||
if blockers:
|
||||
return f'{base},有轻度阻挡但仍可能有效'
|
||||
|
||||
@@ -280,15 +280,21 @@ def _rasi_aspects(source_sign_idx: int, target_sign_idx: int) -> bool:
|
||||
def select_narayana_seed_sign(
|
||||
lagna_sign_idx: int,
|
||||
planet_lons: Dict[str, float],
|
||||
comparison_sign_idx: int | None = None,
|
||||
) -> Dict:
|
||||
"""Select the stronger of Lagna and seventh using the first two rule levels.
|
||||
"""Select the stronger of two signs using the first two rule levels.
|
||||
|
||||
A tie is deliberately returned as blocked: later classical strength rules are
|
||||
not yet implemented and must not be silently substituted with a preference.
|
||||
The default pair is Lagna and the seventh. Argala passes the obstructing
|
||||
sign explicitly and reuses the same two levels. A tie is deliberately
|
||||
returned as blocked: later classical strength rules are not implemented
|
||||
and must not be silently substituted with a preference.
|
||||
"""
|
||||
if not 0 <= lagna_sign_idx < 12:
|
||||
raise ValueError("lagna sign index must be in 0..11")
|
||||
candidate_indices = (lagna_sign_idx, (lagna_sign_idx + 6) % 12)
|
||||
other_sign_idx = (lagna_sign_idx + 6) % 12 if comparison_sign_idx is None else int(comparison_sign_idx)
|
||||
if not 0 <= other_sign_idx < 12 or other_sign_idx == lagna_sign_idx:
|
||||
raise ValueError("comparison sign index must be a different sign in 0..11")
|
||||
candidate_indices = (lagna_sign_idx, other_sign_idx)
|
||||
candidates = {}
|
||||
for sign_idx in candidate_indices:
|
||||
lord = SIGN_LORDS[SIGNS[sign_idx]]
|
||||
@@ -312,7 +318,7 @@ def select_narayana_seed_sign(
|
||||
}
|
||||
|
||||
lagna_candidate = candidates[lagna_sign_idx]
|
||||
seventh_sign_idx = (lagna_sign_idx + 6) % 12
|
||||
seventh_sign_idx = other_sign_idx
|
||||
seventh_candidate = candidates[seventh_sign_idx]
|
||||
lagna_score = (lagna_candidate["occupant_count"], lagna_candidate["aspect_factor_count"])
|
||||
seventh_score = (seventh_candidate["occupant_count"], seventh_candidate["aspect_factor_count"])
|
||||
|
||||
Reference in New Issue
Block a user