@@ -129,28 +129,45 @@ def _get_seasonal_score(element: Element, month_branch: EarthlyBranch) -> int:
129129 return 0
130130
131131
132- def _count_roots (chart : "BaZiChart" , day_master_element : Element ) -> int :
133- """Count how many branch hidden stems match the Day Master's element .
132+ def _count_roots (chart : "BaZiChart" , day_master_element : Element ) -> float :
133+ """Calculate weighted root strength from branch hidden stems.
134134
135135 A "root" (根) is when the Day Master's element appears as a hidden
136- stem in any of the four branches. More roots = more grounded and stable.
136+ stem in any of the four branches. Root position matters:
137137
138- The Day Pillar's own branch is especially significant (called 禄/lu
139- or 根/gen depending on the position).
138+ - Day branch (坐下, "sitting beneath"): weight 1.5 — YOUR branch,
139+ the strongest possible root
140+ - Month branch (月支, "command"): weight 1.2 — seasonal authority
141+ - Hour branch (时支): weight 0.8 — children/future pillar
142+ - Year branch (年支): weight 0.6 — ancestors/distant pillar
143+
144+ Within each branch, the hidden stem position also matters:
145+ - Main qi (本气): full weight — primary energy
146+ - Middle qi (中气): 60% weight — secondary
147+ - Residual qi (余气): 30% weight — trace energy
140148
141149 Args:
142150 chart: The BaZi chart
143151 day_master_element: The Day Master's element
144152
145153 Returns:
146- Number of matching hidden stems (0-12 theoretically, usually 0-4 )
154+ Weighted root score (typically 0-6 )
147155 """
148- roots = 0
149- for pillar in chart .pillars :
150- for hidden_stem in pillar .branch .get_hidden_stem_objects ():
156+ # Pillar weights by position (Day is most significant)
157+ pillar_weights = [0.6 , 1.2 , 1.5 , 0.8 ] # year, month, day, hour
158+
159+ # Hidden stem position weights (main > middle > residual)
160+ position_weights = [1.0 , 0.6 , 0.3 ]
161+
162+ total = 0.0
163+ for pillar , p_weight in zip (chart .pillars , pillar_weights , strict = True ):
164+ hidden_stems = pillar .branch .get_hidden_stem_objects ()
165+ for i , hidden_stem in enumerate (hidden_stems ):
151166 if hidden_stem .element == day_master_element :
152- roots += 1
153- return roots
167+ h_weight = position_weights [i ] if i < len (position_weights ) else 0.2
168+ total += p_weight * h_weight
169+
170+ return total
154171
155172
156173def _count_support_drain (
@@ -201,7 +218,7 @@ class StrengthAnalysis:
201218
202219 # Component scores
203220 seasonal_score : int
204- root_count : int
221+ root_count : float
205222 support_count : int
206223 drain_count : int
207224 month_branch : EarthlyBranch
@@ -261,7 +278,7 @@ def to_dict(self) -> dict:
261278 },
262279 "factors" : {
263280 "seasonal_score" : self .seasonal_score ,
264- "root_count" : self .root_count ,
281+ "root_count" : round ( self .root_count , 2 ) ,
265282 "support_count" : self .support_count ,
266283 "drain_count" : self .drain_count ,
267284 },
@@ -282,8 +299,8 @@ def display(self) -> str:
282299 f" Seasonal: { self .seasonal_score :+d} "
283300 f"({ self .day_master_element .english } in "
284301 f"{ self .month_branch .pinyin } month)" ,
285- f" Roots: { self .root_count } "
286- f"(hidden stems matching { self .day_master_element .english } )" ,
302+ f" Roots: { self .root_count :.1f } "
303+ f"(weighted hidden stems matching { self .day_master_element .english } )" ,
287304 f" Support: { self .support_count } (Self + Companion + Resource)" ,
288305 f" Drain: { self .drain_count } (Output + Wealth + Power)" ,
289306 "" ,
0 commit comments