resakemal's picture
Initial push
320ef02
Raw
History Blame Contribute Delete
2.53 kB
"""
scorer.py β€” zone-based, loose, evoked-feeling scoring for puzzle mode.
Compares a user's shape-sentence affect against the target affect. Scoring lives
in GEOMETRY space (so scoring-space == shape-space), on the FORM axes only β€”
scale is excluded (it's compositional, not an evoked feeling).
Loose & celebratory: generous bands, near-misses are wins, one gentle directional
hint drawn from the biggest felt-quality gap.
"""
from .mappings import affect_to_geometry
# form axes that count for scoring (scale deliberately excluded)
SCORED = ["spikiness", "compactness", "segmentability"]
WEIGHTS = {"spikiness": 1.0, "compactness": 0.9, "segmentability": 0.7}
def score(target_affect, attempt_affect):
"""Return dict: {score 0..1, band, hint}. Higher score = closer feel."""
tg = affect_to_geometry(**target_affect)
ag = affect_to_geometry(**attempt_affect)
num = sum(WEIGHTS[k] * abs(tg[k] - ag[k]) for k in SCORED)
den = sum(WEIGHTS.values())
dist = num / den
s = max(0.0, 1.0 - dist)
if s >= 0.85: band = "Uncanny."
elif s >= 0.70: band = "So close!"
elif s >= 0.55: band = "Good feel β€” nearly there"
elif s >= 0.40: band = "Right neighbourhood"
else: band = "Bold choice β€” different vibe"
# directional hint from the biggest scored gap
gaps = {k: tg[k] - ag[k] for k in SCORED}
k = max(gaps, key=lambda x: abs(gaps[x]))
if abs(gaps[k]) < 0.12:
hint = "energy matched"
elif k == "spikiness":
hint = "try sharper, edgier words" if gaps[k] > 0 else "try softer, gentler words"
elif k == "compactness":
hint = "try fuller, rounder words" if gaps[k] > 0 else "try thinner, sparser words"
else: # segmentability
hint = "try busier, more fragmented words" if gaps[k] > 0 else "try simpler, more unified words"
return {"score": round(s, 3), "band": band, "hint": hint}
# threshold for "cleared it" in puzzle mode (single difficulty for now)
PUZZLE_THRESHOLD = 0.70
if __name__ == "__main__":
target = {"valence": 0.10, "arousal": 0.90, "dominance": 0.82} # sharp
for label, att in [
("a calm round pebble", {"valence": 0.85, "arousal": 0.12, "dominance": 0.40}),
("a rough jagged rock", {"valence": 0.30, "arousal": 0.65, "dominance": 0.70}),
("shattered glass shards",{"valence": 0.15, "arousal": 0.92, "dominance": 0.80}),
]:
r = score(target, att)
print(f"{label:26s} {int(r['score']*100):3d}% {r['band']:28s} ({r['hint']})")