Spaces:
Sleeping
Sleeping
File size: 6,988 Bytes
ecca2a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | """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
|