| import json |
| import os |
| import uuid |
| import time |
| import re |
| from game.model_config import get_background_model |
| |
| |
| |
| |
|
|
| def evaluate_alignment(client, user_input, last_context=""): |
| """ |
| Evaluates the player action and returns four integer shift values in one API call: |
| ld_shift : Light/Dark (-5 to +5) |
| oc_shift : Order/Chaos (-5 to +5) |
| not_shift : Trouble Level (-5 to +5) |
| rep_shift : Reputation (-5 to +5) |
| |
| Model choice is always the fast background model from model_config. |
| """ |
| model_name = get_background_model() |
| print(f"--- DEBUG INFO [ALIGNMENT]: Model: '{model_name}' | Evaluating → '{user_input}' ---") |
|
|
| eval_prompt = f"""You are scoring player actions in a Harry Potter school RPG. |
| |
| Story context: "{last_context[-200:]}" |
| Player action: "{user_input}" |
| |
| Return EXACTLY 4 integers separated by commas. No other text. |
| |
| AXIS 1 — Light/Dark (-5 to +5): |
| +score: compassion, helping others, healing spells, self-sacrifice |
| -score: cruelty, Unforgivable Curses, deliberate bullying, harming someone |
| 0: everything else |
| |
| AXIS 2 — Order/Chaos (-5 to +5): |
| +score: ONLY IF the player explicitly obeys a direct teacher order, performs prefect duty, or reports a rule-breaker to authority |
| -score: ONLY IF the player explicitly does one of these specific acts: sneaks out AFTER CURFEW, physically enters the Restricted Section, lies directly to a professor's face, cheats on an exam, or casts a forbidden spell in class |
| 0: ALL other actions — following an NPC, exploring hallways during the day, having conversations, attending class, looking around, creating a small distraction, choosing a story option, going somewhere |
| |
| AXIS 3 — Trouble Level (-5 to +5): |
| +score: being caught breaking a rule, causing loud public disruption, casting forbidden magic where witnesses are present |
| -score: using an Invisibility Cloak, actively hiding from authority, magically silencing footsteps |
| 0: everything else |
| |
| AXIS 4 — Reputation / Social Standing (-5 to +5): |
| Reputation tracks what OTHER PEOPLE observe and judge — not inner morality, but public perception. |
| +score: visible acts of kindness (others watching), standing up to a bully publicly, |
| saving someone in front of others, winning a public duel with good sportsmanship, |
| helping a struggling classmate in a shared space |
| -score: being caught breaking rules where others see, insulting classmates or teachers openly, |
| cheating visibly, spreading rumours about someone, using dark magic where witnessed, |
| publicly humiliating another student |
| 0: private actions without witnesses, studying alone, exploring quietly, |
| attending class normally, casting spells in solo practice |
| |
| RULE: Most player actions score 0, 0, 0, 0. Only score non-zero when the action EXPLICITLY |
| and UNAMBIGUOUSLY matches the description. When in doubt → 0. |
| Axis 4 (Reputation) ONLY shifts when there is clear social visibility — someone present |
| can observe and judge. Private heroism = 0. Witnessed kindness = +score. |
| |
| Examples: |
| "Follow the prefect down the corridor" → 0, 0, 0, 0 |
| "Ask Hermione for advice on the map" → 0, 0, 0, 0 |
| "Attend Potions class" → 0, 0, 0, 0 |
| "Study quietly in the library" → 0, 0, 0, 0 |
| "Help a crying first-year (hallway, others watching)"→ 2, 0, 0, 2 |
| "Help a friend with homework (just the two of you)" → 1, 0, 0, 0 |
| "Win a Quidditch match for your house" → 1, 0, 0, 3 |
| "Sneak into Restricted Section at midnight" → 0, -3, 2, 0 |
| "Restricted Section — Filch catches you" → 0, -3, 2, -2 |
| "Lie to Professor Snape's face" → -1, -2, 1, -2 |
| "Stand up to Malfoy bullying a first-year (crowd)" → 2, 0, 0, 3 |
| "Cast Crucio on a Slytherin student (witnessed)" → -5, -4, 4, -5 |
| "Spread a rumour about a Hufflepuff student" → -2, 0, 0, -3 |
| """ |
| |
| try: |
| |
| response = client.chat.completions.create( |
| model=model_name, |
| messages=[{"role": "user", "content": eval_prompt}], |
| max_tokens=15, |
| temperature=0.0 |
| ) |
| result = response.choices[0].message.content.strip() |
| print(f"--- DEBUG INFO [ALIGNMENT]: Raw result → {result} ---") |
|
|
| parts = result.split(',') |
| ld_shift, oc_shift, not_shift, rep_shift = (int(p.strip()) for p in parts) |
| return ld_shift, oc_shift, not_shift, rep_shift |
| except Exception as e: |
| print(f"--- DEBUG ERROR [ALIGNMENT]: Parsing failed: {e} ---") |
| return 0, 0, 0, 0 |