"""Config Analysis & Improvement Tab - Merged Analysis Dashboard.
This module combines Config Health, Config Improvement, and Prompts & Examples
into a unified workflow with shared config input.
"""
import gradio as gr
import json
import spaces
from typing import Dict, Any, List, Tuple
from config_linting import (
get_config_health_summary,
generate_mermaid_diagram,
generate_story_flags
)
# Import prompt templates and examples
from prompts_and_outputs import (
WFStage2prompt,
WFStage2prompt2,
SimpleStructureTimeline1stPrompt,
Outputof1stSimpleStructurePrompt,
MoreRobustTimeline1stPrompt,
Outputof1stMoreRobustPrompt,
TimeRelatedConceptsForIdeaGeneration,
Nonlinearprogressionideas,
Adjectivebasedcombinationideatexts,
Adjectivebasedcombinationideatextsv2,
)
from story_elements import (
sterotypicalstoryendings,
InterpersonalConflictIdeasBetraylacksympathyunfairexpectations,
)
from game_configs import (
originalconfigatbeinningofthisspace,
claude3_5_06072024configtips,
tipsupdatedconfigatbeinningofthisspace,
)
# ==================== STARTING POINT DETECTION & CHECKLISTS ====================
STARTING_POINT_CHECKLISTS = {
"world_geography": {
"name": "World Facts / Geography",
"icon": "🌍",
"checks": [
("Location connectivity", "Are all locations reachable? Check for orphaned areas."),
("Spatial descriptions", "Do descriptions mention directions, distances, landmarks?"),
("Exploration rewards", "Are there secrets or discoveries in each location?"),
("Environmental variety", "Different biomes, weather, or atmospheres across locations?"),
("Travel consequences", "Does moving between locations have meaningful effects?"),
("Map coherence", "Would this make sense as a physical map?"),
]
},
"video_writer": {
"name": "Video/Film Ideas (Writer)",
"icon": "🎬",
"checks": [
("Beat structure", "Does the narrative follow clear act breaks (setup, confrontation, resolution)?"),
("Pacing", "Are high-tension moments balanced with breathing room?"),
("Character arcs", "Do characters change from beginning to end?"),
("Dramatic question", "Is there a central question driving the story?"),
("Scene purpose", "Does each state advance plot, reveal character, or both?"),
("Climax placement", "Is there a clear climactic moment near the end?"),
]
},
"video_videographer": {
"name": "Video/Film Ideas (Videographer)",
"icon": "📹",
"checks": [
("Visual descriptions", "Do descriptions paint visual scenes (framing, lighting, composition)?"),
("Camera notes", "Are there implied camera movements or angles in the text?"),
("Transition flow", "Do scene changes have visual logic (cuts, fades, match cuts)?"),
("Shot variety", "Mix of wide establishing shots, medium, and close-ups implied?"),
("Visual motifs", "Are there recurring visual elements for thematic connection?"),
("Media prompts", "Do media fields contain cinematographic direction?"),
]
},
"song_lyrics": {
"name": "Song / Lyric Ideas",
"icon": "🎵",
"checks": [
("Emotional arc", "Does the mood progress through verses/states?"),
("Verse-to-state mapping", "Does each state correspond to a lyrical section?"),
("Refrain/chorus", "Are there recurring states that echo the chorus?"),
("Rhythm in text", "Does the prose have musicality or poetic flow?"),
("Sensory language", "Rich use of sound, texture, and feeling words?"),
("Thematic unity", "Does everything connect to the song's central theme?"),
]
},
"timeline_events": {
"name": "Timeline Events",
"icon": "⏱️",
"checks": [
("Temporal markers", "Do descriptions include time references (morning, later, after)?"),
("Cause-effect chains", "Do earlier choices affect later states?"),
("Event sequencing", "Is the order of events logical and clear?"),
("Parallel timelines", "If branching, do timelines stay internally consistent?"),
("Urgency mechanics", "Are there time-sensitive choices or deadlines?"),
("Flashback/forward", "If non-linear, are jumps clearly marked?"),
]
},
"existing_structures": {
"name": "Existing Structures (D&D, Templates)",
"icon": "📚",
"checks": [
("Source fidelity", "Does the adaptation honor the source material?"),
("Mechanical translation", "Are game mechanics (stats, rolls) represented in choices?"),
("Lore consistency", "Do names, places, and terms match the source?"),
("Balance", "Are encounters/challenges appropriately difficult?"),
("Completeness", "Are all key elements from the source included?"),
("Adaptation creativity", "What new elements make this unique?"),
]
},
"character_psychology": {
"name": "Character Psychology",
"icon": "🧠",
"checks": [
("Motivation clarity", "Are character wants and needs clear?"),
("Psychological depth", "Do characters have flaws, fears, contradictions?"),
("Dialogue authenticity", "Does each character have a distinct voice?"),
("Relationship dynamics", "Are interpersonal tensions and alliances present?"),
("Character agency", "Do NPCs make choices independent of player?"),
("Internal conflict", "Do characters struggle with themselves, not just external?"),
]
},
"mystery_depth": {
"name": "Mystery / Hidden Depth",
"icon": "🔮",
"checks": [
("Information layering", "Is truth revealed gradually across layers?"),
("Red herrings", "Are there misleading clues that feel fair?"),
("Reveal pacing", "Are revelations spaced for maximum impact?"),
("Unreliable elements", "Can the player trust what they're told?"),
("Hidden connections", "Do seemingly unrelated elements connect?"),
("Core truth", "Is there a satisfying 'abyss' truth worth discovering?"),
]
},
"faction_politics": {
"name": "Faction Politics",
"icon": "⚔️",
"checks": [
("Faction definition", "Are factions clearly distinct with different goals?"),
("Reputation tracking", "Do choices affect standing with groups?"),
("Alliance consequences", "Does joining one faction affect others?"),
("Political complexity", "Are there nuanced positions, not just good/evil?"),
("Power dynamics", "Is there a balance of power that can shift?"),
("Betrayal potential", "Can allegiances change or be tested?"),
]
},
}
# ==================== IMPROVEMENT WORKFLOWS ====================
IMPROVEMENT_WORKFLOWS = {
"branching_first": {
"name": "Branching First",
"description": "Best for linear configs that need more player agency",
"steps": [
{"round": 1, "focus": "choices", "instruction": "Add 3-4 choices per state"},
{"round": 2, "focus": "choices", "instruction": "Add conditional/hidden choices"},
{"round": 3, "focus": "detail", "instruction": "Flesh out descriptions"},
{"round": 4, "focus": "characters", "instruction": "Add NPC motivations"},
]
},
"detail_first": {
"name": "Detail First",
"description": "Best for skeleton configs with basic structure",
"steps": [
{"round": 1, "focus": "detail", "instruction": "Rich descriptions (3-4 sentences)"},
{"round": 2, "focus": "environment", "instruction": "Environmental storytelling"},
{"round": 3, "focus": "choices", "instruction": "Meaningful branches"},
{"round": 4, "focus": "tension", "instruction": "Add stakes and pressure"},
]
},
"non_branching": {
"name": "Non-Branching Depth",
"description": "Best for configs where you want depth, not breadth",
"steps": [
{"round": 1, "focus": "detail", "instruction": "Deepen existing paths"},
{"round": 2, "focus": "characters", "instruction": "NPC dialogue and reactions"},
{"round": 3, "focus": "environment", "instruction": "Discoverable lore"},
{"round": 4, "focus": "tension", "instruction": "Emotional beats"},
]
},
"mystery_deepening": {
"name": "Mystery Deepening",
"description": "Best for mystery/investigation configs",
"steps": [
{"round": 1, "focus": "environment", "instruction": "Plant clues in descriptions"},
{"round": 2, "focus": "characters", "instruction": "Hidden motivations"},
{"round": 3, "focus": "choices", "instruction": "Investigation options"},
{"round": 4, "focus": "tension", "instruction": "Red herrings and reveals"},
]
},
}
# Focus area definitions for prompt generation
FOCUS_AREAS = {
"balanced": {
"name": "Balanced",
"instruction": """Improve the config with balanced attention to:
- Richer descriptions (2-3 sentences per state)
- 3-4 meaningful choices per state where appropriate
- Clear character motivations
- Environmental details that support the mood"""
},
"detail": {
"name": "Add Detail",
"instruction": """Focus on ADDING DETAIL to descriptions:
- Expand each description to 3-4 vivid sentences
- Include sensory details (sight, sound, smell, touch)
- Add atmospheric elements (weather, lighting, ambient sounds)
- Include small environmental storytelling details
- Make locations feel lived-in and real"""
},
"choices": {
"name": "Add Choices",
"instruction": """Focus on ADDING MORE CHOICES and branches:
- Ensure each state has 3-4 distinct choices
- Add choices that reflect different playstyles (cautious, bold, clever)
- Include hidden or conditional choices
- Create meaningful branches that reconverge later
- Add optional side paths that reward exploration"""
},
"characters": {
"name": "Deepen Characters",
"instruction": """Focus on DEEPENING CHARACTER MOTIVATIONS:
- Give NPCs clear wants, fears, and secrets
- Add dialogue that reveals personality
- Create relationship dynamics between characters
- Include character-specific choices and reactions
- Show character growth through the narrative"""
},
"environment": {
"name": "Environmental Storytelling",
"instruction": """Focus on ENVIRONMENTAL STORYTELLING:
- Add details that imply history without exposition
- Include objects that tell stories (old letters, worn paths, abandoned items)
- Use weather and time of day to set mood
- Create spaces that feel connected to events
- Add discoverable lore through exploration"""
},
"tension": {
"name": "Increase Tension",
"instruction": """Focus on INCREASING DRAMATIC TENSION:
- Add time pressure or urgency where appropriate
- Include moments of difficult moral choice
- Create setbacks that raise stakes
- Add foreshadowing of dangers
- Include moments of false safety before reveals"""
},
}
# Config type instructions for prompt context
CONFIG_TYPE_INSTRUCTIONS = {
"world": """This is a WORLD/GEOGRAPHY exploration game config. The story unfolds through location discovery.
Key aspects: Location atmosphere, natural connections, hidden areas, NPC placement, environmental storytelling.""",
"timeline": """This is a TIMELINE/CHRONOLOGICAL narrative config. Events unfold in time sequence.
Key aspects: Time-based mood, cause-effect, pacing, flashbacks/forwards, time pressure.""",
"song": """This is an EMOTIONAL JOURNEY config based on musical/lyrical structure.
Key aspects: Emotional transitions, verse/chorus structure, perspective shifts, sensory details.""",
"mystery": """This is a MYSTERY/INVESTIGATION config using the iceberg model.
Key aspects: Layered clues, red herrings, reveal pacing, unreliable elements, core truth.""",
"dnd": """This is a D&D/FANTASY ADVENTURE config with encounters and exploration.
Key aspects: Combat variety, social encounters, treasure, boss encounters, resource management.""",
"film": """This is a FILM/VIDEO SCENES config structured around visual storytelling.
Key aspects: Visual hooks, camera direction, transitions, dialogue subtext, visual motifs.""",
"beats": """This is a STORY BEATS config following professional screenplay structure.
Key aspects: Beat turns, character-driven plot, escalating stakes, emotional weight, payoffs.""",
}
def detect_starting_point(config_json: str) -> Tuple[str, Dict[str, float]]:
"""
Analyze config structure to guess the starting point type.
Returns (best_guess, confidence_scores).
"""
try:
config = json.loads(config_json)
except json.JSONDecodeError:
return "unknown", {}
scores = {
"world_geography": 0.0,
"video_writer": 0.0,
"video_videographer": 0.0,
"song_lyrics": 0.0,
"timeline_events": 0.0,
"existing_structures": 0.0,
"character_psychology": 0.0,
"mystery_depth": 0.0,
"faction_politics": 0.0,
}
all_text = ""
location_count = 0
state_count = 0
for location_key, location_data in config.items():
if isinstance(location_data, dict):
location_count += 1
for state_key, state_data in location_data.items():
if isinstance(state_data, dict):
state_count += 1
desc = state_data.get("description", "")
all_text += f" {desc} "
for choice in state_data.get("choices", []):
if isinstance(choice, dict):
all_text += f" {choice.get('text', '')} "
all_text_lower = all_text.lower()
# World/Geography signals
geo_keywords = ["north", "south", "east", "west", "path", "road", "village", "city", "forest", "mountain", "cave", "door", "corridor", "room", "building", "outside", "inside", "enter", "exit", "travel", "journey"]
scores["world_geography"] = sum(1 for k in geo_keywords if k in all_text_lower) / len(geo_keywords)
if location_count >= 5:
scores["world_geography"] += 0.2
# Video Writer signals (dramatic structure)
drama_keywords = ["suddenly", "meanwhile", "finally", "climax", "tension", "reveal", "twist", "confrontation", "resolution", "act", "scene", "dramatic", "stakes"]
scores["video_writer"] = sum(1 for k in drama_keywords if k in all_text_lower) / len(drama_keywords)
# Video Videographer signals (visual)
visual_keywords = ["camera", "shot", "frame", "close-up", "wide", "pan", "zoom", "angle", "lighting", "shadow", "silhouette", "composition", "visual", "fade", "cut to"]
scores["video_videographer"] = sum(1 for k in visual_keywords if k in all_text_lower) / len(visual_keywords)
# Song/Lyrics signals (emotional, poetic)
song_keywords = ["verse", "chorus", "rhythm", "melody", "heart", "soul", "feeling", "dream", "echo", "silence", "cry", "whisper", "voice", "song", "music"]
scores["song_lyrics"] = sum(1 for k in song_keywords if k in all_text_lower) / len(song_keywords)
# Timeline signals
time_keywords = ["morning", "evening", "night", "day", "hour", "later", "before", "after", "then", "now", "yesterday", "tomorrow", "clock", "time", "wait", "moment"]
scores["timeline_events"] = sum(1 for k in time_keywords if k in all_text_lower) / len(time_keywords)
# D&D/Existing structures signals
dnd_keywords = ["hp", "damage", "attack", "spell", "magic", "roll", "dice", "armor", "weapon", "potion", "dungeon", "dragon", "goblin", "quest", "level"]
scores["existing_structures"] = sum(1 for k in dnd_keywords if k in all_text_lower) / len(dnd_keywords)
# Character Psychology signals
psych_keywords = ["feel", "think", "believe", "fear", "hope", "want", "need", "memory", "emotion", "trust", "betray", "guilt", "regret", "desire", "conflict"]
scores["character_psychology"] = sum(1 for k in psych_keywords if k in all_text_lower) / len(psych_keywords)
# Mystery/Depth signals
mystery_keywords = ["secret", "hidden", "clue", "mystery", "discover", "reveal", "truth", "lie", "suspect", "evidence", "investigate", "puzzle", "unknown", "shadow"]
scores["mystery_depth"] = sum(1 for k in mystery_keywords if k in all_text_lower) / len(mystery_keywords)
# Faction signals
faction_keywords = ["faction", "alliance", "reputation", "loyalty", "betray", "guild", "clan", "tribe", "kingdom", "empire", "rebel", "join", "side", "enemy", "ally"]
scores["faction_politics"] = sum(1 for k in faction_keywords if k in all_text_lower) / len(faction_keywords)
# Normalize and find best match
max_score = max(scores.values()) if scores.values() else 0
if max_score > 0:
scores = {k: round(v / max_score, 2) for k, v in scores.items()}
best_guess = max(scores, key=scores.get) if max_score > 0 else "world_geography"
return best_guess, scores
def format_starting_point_checklist(starting_point: str) -> str:
"""Format the checklist for a starting point as markdown."""
if starting_point not in STARTING_POINT_CHECKLISTS:
return "Unknown starting point selected."
checklist = STARTING_POINT_CHECKLISTS[starting_point]
lines = [f"## {checklist['icon']} {checklist['name']} Checklist\n"]
lines.append("Review these criteria based on your creative approach:\n")
for i, (check_name, check_desc) in enumerate(checklist["checks"], 1):
lines.append(f"- [ ] **{check_name}**: {check_desc}")
return "\n".join(lines)
def format_detection_results(config_json: str) -> Tuple[str, str, str]:
"""
Detect starting point and return formatted results.
Returns (detected_type, confidence_display, checklist).
"""
if not config_json or not config_json.strip():
return "world_geography", "*Paste a config to auto-detect*", ""
detected, scores = detect_starting_point(config_json)
# Format confidence display
confidence_lines = ["**Detection Confidence:**\n"]
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
for sp_key, score in sorted_scores[:5]: # Top 5
sp_info = STARTING_POINT_CHECKLISTS.get(sp_key, {})
icon = sp_info.get("icon", "")
name = sp_info.get("name", sp_key)
bar = "█" * int(score * 10) + "░" * (10 - int(score * 10))
confidence_lines.append(f"{icon} {name}: {bar} ({int(score*100)}%)")
checklist = format_starting_point_checklist(detected)
return detected, "\n".join(confidence_lines), checklist
# ==================== METRICS FUNCTIONS ====================
def compute_config_metrics(config_json: str) -> Dict[str, Any]:
"""Compute quality metrics from a game config."""
try:
config = json.loads(config_json)
except json.JSONDecodeError:
return {"error": "Invalid JSON"}
# Count states and locations
total_states = 0
total_locations = 0
all_choices = []
all_descriptions = []
states_with_consequences = 0
states_with_media = 0
for location_key, location_data in config.items():
if isinstance(location_data, dict):
total_locations += 1
for state_key, state_data in location_data.items():
if isinstance(state_data, dict):
total_states += 1
# Count choices
choices = state_data.get("choices", [])
if isinstance(choices, list):
all_choices.append(len(choices))
# Count description length
desc = state_data.get("description", "")
if desc:
all_descriptions.append(len(desc.split()))
# Check for consequences
if state_data.get("consequences") or state_data.get("on_enter"):
states_with_consequences += 1
# Check for media
if state_data.get("media") and len(state_data.get("media", [])) > 0:
states_with_media += 1
# Calculate averages
avg_choices = sum(all_choices) / len(all_choices) if all_choices else 0
avg_desc_length = sum(all_descriptions) / len(all_descriptions) if all_descriptions else 0
media_coverage = states_with_media / total_states if total_states > 0 else 0
return {
"total_states": total_states,
"total_locations": total_locations,
"avg_choices_per_state": round(avg_choices, 2),
"states_with_consequences": states_with_consequences,
"avg_description_length": round(avg_desc_length, 1),
"media_coverage": round(media_coverage * 100, 1),
"states_with_media": states_with_media,
}
def compute_improvement_suggestions(config_json: str) -> str:
"""Generate improvement suggestions based on computed metrics."""
metrics = compute_config_metrics(config_json)
if "error" in metrics:
return f"**Error:** {metrics['error']}"
suggestions = []
# Check state variety
if metrics["total_states"] < 10:
suggestions.append(f"**Limited content**: Only {metrics['total_states']} states. Consider adding more locations and states for variety.")
# Check choice variety
if metrics["avg_choices_per_state"] < 2.5:
suggestions.append(f"**Low choice variety**: Average {metrics['avg_choices_per_state']} choices/state. Add more choices to reduce linearity.")
# Check media coverage
if metrics["media_coverage"] < 20:
suggestions.append(f"**Limited media**: Only {metrics['media_coverage']}% of states have media. Add images/sounds for engagement.")
# Check description length
if metrics["avg_description_length"] < 15:
suggestions.append(f"**Short descriptions**: Average {metrics['avg_description_length']} words. Expand for richer narrative.")
# Check consequences
consequence_ratio = metrics["states_with_consequences"] / metrics["total_states"] if metrics["total_states"] > 0 else 0
if consequence_ratio < 0.3:
suggestions.append(f"**Few consequences**: Only {metrics['states_with_consequences']} states have consequences. Add more for player agency.")
if not suggestions:
return "**Config looks good!** No major issues detected based on structural metrics."
return "## Improvement Suggestions\n\n" + "\n\n".join(suggestions)
def format_metrics_display(config_json: str) -> str:
"""Format metrics as a readable markdown display."""
metrics = compute_config_metrics(config_json)
if "error" in metrics:
return f"**Error:** {metrics['error']}"
return f"""## Config Metrics
| Metric | Value | Target |
|--------|-------|--------|
| Total States | {metrics['total_states']} | 15+ |
| Total Locations | {metrics['total_locations']} | 5+ |
| Avg Choices/State | {metrics['avg_choices_per_state']} | 2.5+ |
| States with Consequences | {metrics['states_with_consequences']} | 30%+ |
| Avg Description Length | {metrics['avg_description_length']} words | 15+ |
| Media Coverage | {metrics['media_coverage']}% | 50%+ |
"""
# ==================== ZERO GPU GENERATION ====================
# Lazy model loading (shared with D&D GM tab)
_analysis_model = None
_analysis_tokenizer = None
_analysis_model_name = None
def _ensure_torch():
"""Lazy import torch."""
import torch
return torch
def _ensure_transformers():
"""Lazy import transformers."""
import transformers
return transformers
def _ensure_analysis_model(model_name: str = "Qwen/Qwen3-4B"):
"""Lazy load model for config generation."""
global _analysis_model, _analysis_tokenizer, _analysis_model_name
if _analysis_model is None or _analysis_model_name != model_name:
tf = _ensure_transformers()
print(f"Loading config generation model: {model_name}")
if _analysis_model is not None:
del _analysis_model
del _analysis_tokenizer
_torch = _ensure_torch()
if _torch.cuda.is_available():
_torch.cuda.empty_cache()
_analysis_tokenizer = tf.AutoTokenizer.from_pretrained(model_name)
_analysis_model = tf.AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype="auto", device_map="auto"
)
_analysis_model_name = model_name
return _analysis_model, _analysis_tokenizer
@spaces.GPU
def generate_config_from_theme(theme: str, prompt_template: str) -> str:
"""Generate a game config from a theme using Zero GPU."""
if not theme.strip():
return "Please enter a theme."
_torch = _ensure_torch()
model, tokenizer = _ensure_analysis_model()
zero = _torch.Tensor([0]).cuda()
model.to(zero.device)
# Build the prompt
full_prompt = f"{prompt_template}\n\nTheme: {theme}\n\nGenerate a complete game config JSON:"
messages = [{"role": "user", "content": full_prompt}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer([text], return_tensors="pt").to(zero.device)
with _torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=2048,
temperature=0.7,
do_sample=True,
pad_token_id=tokenizer.eos_token_id,
)
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
# Extract JSON from response
if "```json" in generated:
start = generated.find("```json") + 7
end = generated.find("```", start)
if end > start:
return generated[start:end].strip()
if "{" in generated:
start = generated.find("{")
# Find matching closing brace
depth = 0
for i, char in enumerate(generated[start:], start):
if char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0:
return generated[start:i+1]
return generated
# ==================== TAB CREATION ====================
def create_config_analysis_tab():
"""Create the Config Analysis & Improvement tab."""
with gr.Tab("Config Analysis & Improvement"):
gr.Markdown("## Config Analysis & Improvement")
gr.Markdown("Analyze, improve, and generate game configs. Paste your config below or generate a new one.")
# Shared config input at top
with gr.Row():
config_input = gr.Textbox(
label="Config JSON",
lines=12,
placeholder='{"location": {"state": {"description": "...", "choices": [...]}}}',
scale=3
)
with gr.Column(scale=1):
analyze_btn = gr.Button("Analyze Config", variant="primary", size="lg")
clear_btn = gr.Button("Clear", size="sm")
with gr.Tabs():
# ==================== SUB-TAB 1: ANALYZE ====================
with gr.TabItem("Analyze"):
gr.Markdown("### Health Report & Metrics")
with gr.Row():
with gr.Column(scale=1):
health_report_output = gr.Markdown(value="*Paste config and click Analyze*")
with gr.Column(scale=1):
metrics_output = gr.Markdown(value="*Metrics will appear here*")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### State Machine Diagram")
gr.Markdown("*Copy to [mermaid.live](https://mermaid.live) to visualize*")
mermaid_output = gr.Code(label="Mermaid Code", language=None, lines=12)
mermaid_btn = gr.Button("Generate Diagram")
with gr.Column(scale=1):
gr.Markdown("### Story Flags")
gr.Markdown("*Potential narrative issues*")
flags_output = gr.Dataframe(
headers=["Severity", "State", "Issue"],
label="Story Flags",
wrap=True
)
with gr.Accordion("Legend", open=False):
gr.Markdown("""
**Diagram Colors:**
- Green: Start state
- Pink: End state
- Yellow: Dead-end (no exits)
- Red: Orphaned (unreachable)
**Severity Levels:**
- Warning: Should probably fix
- Info: Consider reviewing
**Common Issues:**
- *Disconnected choices*: Description doesn't mention the available choices
- *Sparse content*: Very short descriptions
- *Length outlier*: Description much longer/shorter than average
- *Inconsistent media*: Some states have media, others don't
""")
# ==================== SUB-TAB 2: IMPROVEMENT GUIDE ====================
with gr.TabItem("Improvement Guide"):
gr.Markdown("### Improvement Suggestions")
suggestions_output = gr.Markdown(value="*Analyze a config to see suggestions*")
# ==================== STARTING POINT CONTEXT ====================
with gr.Accordion("Starting Point Context (Tailored Checklists)", open=True):
gr.Markdown("*Different creative approaches need different quality checks. Select or auto-detect your starting point.*")
with gr.Row():
with gr.Column(scale=1):
detect_btn = gr.Button("🔍 Auto-Detect from Config", variant="secondary")
starting_point_dropdown = gr.Dropdown(
choices=[
("🌍 World Facts / Geography", "world_geography"),
("🎬 Video/Film (Writer)", "video_writer"),
("📹 Video/Film (Videographer)", "video_videographer"),
("🎵 Song / Lyric Ideas", "song_lyrics"),
("⏱️ Timeline Events", "timeline_events"),
("📚 Existing Structures (D&D)", "existing_structures"),
("🧠 Character Psychology", "character_psychology"),
("🔮 Mystery / Hidden Depth", "mystery_depth"),
("⚔️ Faction Politics", "faction_politics"),
],
value="world_geography",
label="Select Starting Point",
interactive=True
)
with gr.Column(scale=1):
detection_confidence = gr.Markdown(value="*Click 'Auto-Detect' to analyze your config*")
checklist_output = gr.Markdown(value="*Select a starting point or auto-detect to see tailored checklist*")
with gr.Accordion("Quick Evaluation Method", open=False):
gr.Markdown("""
**Prompt to use with any SOTA LLM:**
> This config is for a basic text based game engine. I don't have any structural metrics to assess the quality of the config. What JSON things can we look at to see if it may be too bland for a person testing the game?
Then paste your config with the prompt.
""")
gr.Markdown("""
**What to look for (Claude 3.5 analysis):**
- **State variety**: More locations and states = more diversity
- **Average choices per state**: Higher = less linear feel
- **Consequences**: More = greater sense of agency
- **Description length**: Longer = richer narrative
- **Media usage**: Images/sounds = more engagement
- **Narrative branching**: More paths = better replayability
""")
with gr.Accordion("Before/After Example", open=False):
gr.Markdown("Example of config improvement using LLM suggestions:")
display_original = originalconfigatbeinningofthisspace.replace(' ', ' ').replace('\n', '
')
display_tips = claude3_5_06072024configtips.replace(' ', ' ').replace('\n', '
')
display_improved = tipsupdatedconfigatbeinningofthisspace.replace(' ', ' ').replace('\n', '
')
gr.HTML(f"""