Implementing Outcome and Card Name Generation Function (option_outcome_generator.py)

In each round of the game "Fentasy Realm," after a player makes a choice, two things need to be generated:

  1. Narrative Outcome: A description of the immediate consequences of the choice, enhancing immersion.
  2. Card Information: If a card is obtained (determined by the card acquisition algorithm), generate a card name and a brief description for collection and boss battles.

You need to implement a function generate\_option\_outcome that, based on the current scene description, the player's chosen option text, the plyaer's moral alignment (optional), and the card type (good, evil, neutral, or None if no card is obtained), uses a large language model (Deepseek) to generate the corresponding outcome text and card information.

Module Location and Dependencies

  • File Path: src/game\_logic/option\_outcome\_generator.py
  • Dependencies:
    • llm\_hint: Reuse llm object and check\_api\_key.
    • langchain\_core.prompts: For building prompt templates.
    • Standard libraries: logging, json, re, os, etc.
  • Reusable Resources: Can reuse \_parse\_llm\_response and PROJECT\_ROOT from scene\_generator.py.

Function Design

3.1 Function Signature


import logging
import os
import json
import re
from typing import Optional, Dict, Any
from langchain_core.prompts import ChatPromptTemplate
from .config import config
from .llm_hint import llm, check_api_key

logger = logging.getLogger(__name__)

PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
TEMPLATE_PATH = os.path.join(PROJECT_ROOT, "prompts", "option_outcome_prompt.txt")

# Load template when the module is loaded
try:
    with open(TEMPLATE_PATH, "r", encoding="utf-8") as f:
        template_str = f.read()
    logger.info(f"Option outcome template loaded: {TEMPLATE_PATH}")
except FileNotFoundError:
    logger.error(f"Option outcome template file not found: {TEMPLATE_PATH}")
    template_str = None

def generate_option_outcome(
    scene_description: str,
    option_text: str,
    morality_ratios: Optional[Dict[str, float]] = None,
    card_type: Optional[str] = None  # 'good', 'evil', 'neutral' or None (no card)
) -> Dict[str, Any]:
    """
    Generate narrative outcome and card information.

    Parameters:
        scene_description: Current round's scene description text
        option_text: Player's chosen option text
        morality_ratios: Player's moral ratios, e.g., {'good':0.3, 'neutral':0.2, 'evil':0.5}, optional
        card_type: Type of card obtained, 'good', 'evil', 'neutral' or None (no card)

    Returns:
        Dictionary containing:
        {
            "outcome": str,                     # Narrative outcome text
            "card_name": Optional[str],          # Card name if card_type is not None; otherwise None
            "card_description": Optional[str]    # Card description if card_type is not None; otherwise None
        }
        If generation fails, returns a dictionary with default values.
    """
    # Implementation details follow

3.2 Return Value Description

  • outcome: Required, string, describes the immediate result of the choice, approximately 30-100 characters.
  • card_name: Must be provided if card\_type is not None; otherwise None.
  • card_description: Must be provided if card\_type is not None; otherwise None.

Prompt Template (option_outcome_prompt.txt)

Save the following content as prompts/option\_outcome\_prompt.txt:


You are a master of narrative in the world of cultivation. Based on the given scene, player's choice, and moral alignment, generate a narrative outcome and possibly a card name and description.

Scene Description: {scene_description}
Player Choice: {option_text}
Player Moral Alignment: Good {good_ratio:.0%}, Neutral {neutral_ratio:.0%}, Evil {evil_ratio:.0%}
Card Type Obtained: {card_type_text}

Please generate a JSON object in the following format:
{
  "outcome": "Narrative outcome, about 30-100 characters, describing the immediate result of the choice.",
  "card_name": "If a card is obtained, give it a 2-5 character name, e.g., 'Rescue Heart'; otherwise null",
  "card_description": "If a card is obtained, write a 10-20 character description of its origin; otherwise null"
}

Requirements:
1. The narrative outcome should be vivid and natural, fitting the atmosphere of the cultivation world, closely related to the scene and choice.
2. If a card is obtained, the card name should have a cultivation flavor and be relevant to the event; the description should be concise and elegant.
3. If no card is obtained (card_type is "None"), then card_name and card_description must be null.
4. Moral alignment can be used as a reference to adjust the tone (e.g., more compassionate if good ratio is high, more ruthless if evil ratio is high), but do not force it.
5. Output the JSON directly without additional content.

Note: The {card\_type\_text} in the template needs to be generated in the code based on card\_type, for example:

  • If card\_type is 'good', then card\_type\_text = 'Good'
  • If card\_type is 'evil', then card\_type\_text = 'Evil'
  • If card\_type is 'neutral', then card\_type\_text = 'Neutral'
  • If card\_type is None, then card\_type\_text = 'None'

Default Values Design

When the LLM call fails or parsing errors occur, return default results to ensure the game flow is not affected. Default values should be generated based on card\_type, but for simplicity, a set of default values can be used, ensuring the fields are correct.


DEFAULT_OUTCOME = "Your choice has caused a ripple in the realm."
DEFAULT_CARD_NAMES = {
    'good': 'Good Fate',
    'evil': 'Evil Consequence',
    'neutral': 'Karma'
}
DEFAULT_CARD_DESCRIPTIONS = {
    'good': 'A mark left by a good deed.',
    'evil': 'The karmic result of an evil act.',
    'neutral': 'A witness to a cautious observation.'
}

Fill in the corresponding default values based on card\_type when returning.

Implementation Steps

  1. Check Template: If template\_str is None, log an error and return default results.

  2. Check LLM and API Key: If it fails, return default results.

  3. Prepare Data: ```

         good_ratio = morality_ratios.get('good', 0.0) if morality_ratios else 0.0
         neutral_ratio = morality_ratios.get('neutral', 0.0) if morality_ratios else 0.0
         evil_ratio = morality_ratios.get('evil', 0.0) if morality_ratios else 0.0
         if card_type == 'good':
             card_type_text = 'Good'
         elif card_type == 'evil':
             card_type_text = 'Evil'
         elif card_type == 'neutral':
             card_type_text = 'Neutral'
         else:
             card_type_text = 'None'
    
  4. Format Prompt: ```

         prompt = ChatPromptTemplate.from_template(template_str)
         formatted_prompt = prompt.format(
             scene_description=scene_description,
             option_text=option_text,
             good_ratio=good_ratio,
             neutral_ratio=neutral_ratio,
             evil_ratio=evil_ratio,
             card_type_text=card_type_text
         )
    
  5. Call LLM: Use llm.invoke(formatted\_prompt), retry up to 2 times.

  6. Parse Response: Reuse parse\_llm\_response (can be copied or imported from layer\_event\_generator), remove markdown code blocks, and parse JSON.

  7. Validate Data:

    • Ensure the returned dictionary contains the outcome field.
    • If card\_type is not None, card\_name and card\_description cannot be None.
    • If card\_type is None, card\_name and card\_description should be None.
    • If validation fails, log a warning and return default values.
  8. Return Result.

Testing Suggestions

Create a unit test file tests/test\_option\_outcome\_generator.py covering at least:

  1. Successful Generation: Mock LLM to return valid JSON, verify the structure is correct, and card fields match card\_type.
  2. No Card Case: card\_type=None, verify card\_name and card\_description are None.
  3. Parsing Error: Simulate LLM returning invalid JSON, verify default values are returned.
  4. Missing Fields: Simulate LLM returning JSON with missing fields, verify default values are returned.
  5. Moral Ratios Passed: Verify the prompt includes the correct ratios.
  6. Template Missing: Simulate the template file not existing, verify default values are returned.

Submission Requirements

  • Create src/game\_logic/option\_outcome\_generator.py with the above implementation.
  • Create prompts/option\_outcome\_prompt.txt with the prompt content.
  • Create tests/test\_option\_outcome\_generator.py with unit tests.
  • Code should be well-commented, with clear explanations for key logic.
  • Ensure all tests pass.

Start implementing! Feel free to reach out if you have any questions.

Tags: Deepseek LangChain python JSON Unit Testing

Posted on Fri, 22 May 2026 20:18:23 +0000 by chordsoflife