Spaces:
Sleeping
Sleeping
| """Bidirectional A+B=C Solver — forward read + backward zone targeting. | |
| Forward: text -> VADUGWI (wrapper around compute_vadug) | |
| Backward: given A's state + target zone, solve for what B needs to be | |
| The core idea: emotional states are composable. If someone is in state A | |
| and you say B, the resulting state C = weighted_blend(A, B). | |
| A persists at 60% (emotional inertia — you don't forget how you feel). | |
| B has 40% influence (what was just said shifts the state, doesn't replace it). | |
| The solver can also work backwards: given where A is and where you want C | |
| to land (a target zone), sweep B's valence to find valid ranges. | |
| """ | |
| from typing import List, Optional, Tuple | |
| from .shared import VADUG | |
| from .pendulum import compute_vadug | |
| from .zones import ZONES | |
| # ── Forward ──────────────────────────────────────────────────────── | |
| def forward(text: str) -> VADUG: | |
| """Compute VADUGWI for a text string. Wrapper around compute_vadug.""" | |
| result, _ = compute_vadug(text) | |
| return result | |
| # ── State transition ─────────────────────────────────────────────── | |
| def state_transition( | |
| a_vadug: VADUG, | |
| b_vadug: VADUG, | |
| a_weight: float = 0.6, | |
| ) -> VADUG: | |
| """Compute resulting state C from A (receiver state) + B (the force/message). | |
| Base: C = A * a_weight + B * (1 - a_weight), clamped to 0-255. | |
| FORCE DIRECTION adjustments: | |
| - B with CONTROL intent (I>200) + negative V = attack on receiver. | |
| Receiver's D drops (dominated), W drops (worth under attack). | |
| The force doesn't blend W/D toward B's values -- it PUSHES them down. | |
| - B with CONNECT intent (I>155) + positive V = support/healing. | |
| Receiver's W gets lifted, D stabilized. | |
| - B with WITHDRAW intent (I<40) = the sender is pulling away. | |
| Receiver's G increases (heavier), connection fading. | |
| """ | |
| b_weight = 1.0 - a_weight | |
| CENTER = 128.0 | |
| # Base blend | |
| c_v = a_vadug.v * a_weight + b_vadug.v * b_weight | |
| c_a = a_vadug.a * a_weight + b_vadug.a * b_weight | |
| c_d = a_vadug.d * a_weight + b_vadug.d * b_weight | |
| c_u = a_vadug.u * a_weight + b_vadug.u * b_weight | |
| c_g = a_vadug.g * a_weight + b_vadug.g * b_weight | |
| c_w = a_vadug.w * a_weight + b_vadug.w * b_weight | |
| c_i = a_vadug.i * a_weight + b_vadug.i * b_weight | |
| # Force direction adjustments on the RECEIVER | |
| b_v = b_vadug.v | |
| b_i = b_vadug.i | |
| # Attack: negative V directed outward OR pure CONTROL command | |
| if b_v < 125 and b_i > 60: | |
| # Negative force aimed outward = attack on receiver | |
| attack_strength = (128 - b_v) / 128.0 # 0 to 1 | |
| control_boost = max(0, (b_i - 128)) / 127.0 # 0 to 1 | |
| total = attack_strength * (1.0 + control_boost) | |
| c_d -= total * 15 | |
| c_w -= total * 15 | |
| c_v -= total * 5 | |
| # Pure CONTROL command (high I) -- even if V is only mildly negative | |
| # "shut up" has high D force but the RECEIVER loses D (being commanded) | |
| if b_i > 200: | |
| control_strength = (b_i - 200) / 55.0 # 0 to 1 | |
| c_d -= control_strength * 12 # being controlled drops YOUR D | |
| # Override the D blend -- receiver doesn't gain power from being commanded | |
| if b_vadug.d > CENTER: | |
| # B has high D (commander's power) but that shouldn't transfer to receiver | |
| d_excess = (c_d - a_vadug.d * a_weight) * 0.3 # dampen the D transfer | |
| c_d = a_vadug.d * a_weight + d_excess | |
| # Healing: CONNECT intent + positive V | |
| elif b_v > 135 and b_i > 155: | |
| heal_strength = (b_v - 128) / 127.0 | |
| connect_boost = (b_i - 128) / 127.0 | |
| total = heal_strength * (1.0 + connect_boost * 0.5) | |
| c_w += total * 12 # W lifted (feeling valued) | |
| c_d += total * 8 # D stabilized (someone has their back) | |
| # Withdraw: sender pulling away from receiver | |
| elif b_i < 40: | |
| withdraw_strength = (40 - b_i) / 40.0 | |
| c_g -= withdraw_strength * 10 # heavier (weight of abandonment) | |
| c_d -= withdraw_strength * 8 # loss of relational support | |
| c_w -= withdraw_strength * 5 # worth hit from being left | |
| # Deflect/dismiss: mild negative, disengaged | |
| elif b_i > 60 and b_i < 120 and b_v < 128: | |
| dismiss_strength = (128 - b_v) / 128.0 | |
| c_w -= dismiss_strength * 8 # mild worth hit from being dismissed | |
| return VADUG( | |
| v=int(round(max(0, min(255, c_v)))), | |
| a=int(round(max(0, min(255, c_a)))), | |
| d=int(round(max(0, min(255, c_d)))), | |
| u=int(round(max(0, min(255, c_u)))), | |
| g=int(round(max(0, min(255, c_g)))), | |
| w=int(round(max(0, min(255, c_w)))), | |
| i=int(round(max(0, min(255, c_i)))), | |
| ) | |
| # ── Backward: zone targeting ────────────────────────────────────── | |
| def _in_zone(vadug: VADUG, zone_name: str) -> bool: | |
| """Check if VADUGWI state falls within a zone's radius on V, D, G.""" | |
| zone = ZONES[zone_name] | |
| c = zone["center"] | |
| r = zone["radius"] | |
| return ( | |
| abs(vadug.v - c["v"]) <= r["v"] | |
| and abs(vadug.d - c["d"]) <= r["d"] | |
| and abs(vadug.g - c["g"]) <= r["g"] | |
| ) | |
| def solve_for_b_range( | |
| a_vadug: VADUG, | |
| target_zone: str, | |
| temperature_steps: int = 100, | |
| ) -> List[Tuple[int, int]]: | |
| """Sweep B's valence (0-255), return ranges where C lands in target zone. | |
| For each candidate B valence, construct a synthetic B with neutral A/D/U/G | |
| and compute C = state_transition(A, B). If C falls in the target zone, | |
| include that V value. | |
| Returns list of (start, end) inclusive ranges of valid B valence values. | |
| """ | |
| valid = [] | |
| # Use finer steps for better resolution, but always cover 0-255 | |
| step = max(1, 256 // temperature_steps) | |
| for bv in range(0, 256, step): | |
| # Synthetic B: only V varies, rest neutral | |
| b = VADUG(v=bv, a=128, d=128, u=0, g=128, w=128, i=128) | |
| c = state_transition(a_vadug, b) | |
| if _in_zone(c, target_zone): | |
| valid.append(bv) | |
| # Collapse to contiguous ranges | |
| if not valid: | |
| return [] | |
| ranges = [] | |
| start = valid[0] | |
| prev = valid[0] | |
| for bv in valid[1:]: | |
| if bv - prev > step: | |
| ranges.append((start, prev)) | |
| start = bv | |
| prev = bv | |
| ranges.append((start, prev)) | |
| return ranges | |
| def optimal_b_temperature( | |
| a_vadug: VADUG, | |
| target_zone: str, | |
| ) -> Optional[int]: | |
| """Find the optimal B valence to reach the target zone from A. | |
| Returns the midpoint of the widest valid range, or None if unreachable. | |
| """ | |
| ranges = solve_for_b_range(a_vadug, target_zone, temperature_steps=256) | |
| if not ranges: | |
| return None | |
| # Find widest range | |
| widest = max(ranges, key=lambda r: r[1] - r[0]) | |
| return (widest[0] + widest[1]) // 2 | |