Ajeya95's picture
Deploy LifeChoice Simulator from Codex-built repository
f3afef3 verified
Raw
History Blame Contribute Delete
26.4 kB
from __future__ import annotations
import base64
import html
import json
import mimetypes
from functools import lru_cache
from pathlib import Path
from typing import Any
import gradio as gr
from lifechoice_engine import (
METRICS,
MODEL_ID,
SimulationSession,
choose,
current_node,
character_expression,
environment_image,
environment_state,
prefetch_node,
split_dilemma,
start_session,
)
ROOT = Path(__file__).parent
PERSONA_ASSETS = {
"Family": ROOT / "assets" / "personas" / "family.webp",
"Friend": ROOT / "assets" / "personas" / "friend.webp",
"Mentor": ROOT / "assets" / "personas" / "mentor.webp",
"Partner": ROOT / "assets" / "personas" / "friend.webp",
"Inner Voice": ROOT / "assets" / "personas" / "mentor.webp",
}
CSS = """
:root {
--night: #070a12;
--panel: rgba(9, 13, 24, .90);
--line: rgba(255, 255, 255, .14);
--cream: #fff7df;
--muted: #bbb7aa;
--gold: #ffc857;
--coral: #ff6b5f;
--cyan: #5ce1e6;
--green: #74e59b;
}
* { box-sizing: border-box; }
body, .gradio-container {
background:
radial-gradient(circle at 12% -8%, rgba(65, 71, 150, .34), transparent 32rem),
linear-gradient(180deg, #11162a 0%, var(--night) 48%, #05070c 100%) !important;
color: var(--cream) !important;
}
.gradio-container {
max-width: 1180px !important;
padding: 22px 22px 34px !important;
font-family: Inter, ui-sans-serif, system-ui, sans-serif !important;
}
footer { display: none !important; }
.app-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 24px;
margin: 5px 2px 18px;
}
.brand-lockup h1 {
color: #fff !important;
font-family: "Arial Black", Impact, sans-serif;
font-size: clamp(2rem, 6vw, 4.7rem);
line-height: .84;
letter-spacing: -.075em;
margin: 7px 0 9px;
text-transform: uppercase;
}
.brand-lockup h1 span { color: var(--gold); }
.brand-lockup p { color: var(--muted) !important; margin: 0; max-width: 670px; }
.eyebrow {
color: var(--cyan) !important;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: .72rem;
font-weight: 800;
letter-spacing: .14em;
text-transform: uppercase;
}
.model-chip {
border: 1px solid rgba(92, 225, 230, .4);
border-radius: 999px;
color: var(--cyan);
flex: 0 0 auto;
font: 700 .72rem ui-monospace, monospace;
padding: 9px 12px;
}
.setup-card {
background: linear-gradient(145deg, rgba(22, 27, 48, .98), rgba(8, 11, 21, .98));
border: 1px solid var(--line);
border-radius: 22px;
box-shadow: 0 24px 80px rgba(0, 0, 0, .36);
overflow: hidden;
padding: 10px;
}
.setup-intro {
min-height: 100%;
padding: 26px 22px;
background:
linear-gradient(180deg, rgba(7, 10, 18, .08), rgba(7, 10, 18, .92)),
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Cpath d='M0 40h80M40 0v80' stroke='%23ffffff' stroke-opacity='.035'/%3E%3C/svg%3E");
border-radius: 15px;
}
.setup-intro h2 {
color: #fff !important;
font-family: "Arial Black", Impact, sans-serif;
font-size: clamp(1.8rem, 4vw, 3.2rem);
letter-spacing: -.05em;
line-height: .95;
margin: 24px 0 14px;
text-transform: uppercase;
}
.setup-intro p { color: var(--muted) !important; line-height: 1.65; }
.feature-list { display: grid; gap: 11px; margin-top: 26px; }
.feature {
align-items: center;
border-top: 1px solid rgba(255,255,255,.09);
color: #ddd8ca;
display: flex;
font-size: .88rem;
gap: 10px;
padding-top: 11px;
}
.feature b { color: var(--gold); font: 800 .72rem ui-monospace, monospace; }
.form-panel { padding: 18px 14px 12px; }
.gradio-container label span { color: #d9d5c8 !important; font-weight: 700 !important; }
.gradio-container textarea,
.gradio-container input {
color: #fff !important;
}
.gradio-container .block {
border-color: rgba(255,255,255,.12) !important;
}
#begin-button, #commit-button {
background: linear-gradient(135deg, var(--gold), #ff9f43) !important;
border: 0 !important;
box-shadow: 0 10px 30px rgba(255, 159, 67, .22);
color: #17120a !important;
font-family: "Arial Black", Impact, sans-serif !important;
letter-spacing: .04em;
min-height: 50px;
text-transform: uppercase;
}
#begin-button:hover, #commit-button:hover { filter: brightness(1.08); transform: translateY(-1px); }
.microcopy { color: #8f8b80 !important; font-size: .75rem; line-height: 1.5; margin: 9px 2px 0; }
.game-frame {
aspect-ratio: 16 / 9;
background: #111;
border: 1px solid rgba(255,255,255,.18);
border-radius: 22px;
box-shadow: 0 26px 90px rgba(0,0,0,.52);
isolation: isolate;
min-height: 530px;
overflow: hidden;
position: relative;
}
.world-bg {
background-position: center;
background-size: cover;
filter: saturate(.9) contrast(1.04);
inset: 0;
position: absolute;
transform: scale(1.015);
z-index: -3;
}
.world-shade {
background:
linear-gradient(90deg, rgba(3,5,10,.88) 0%, rgba(3,5,10,.34) 57%, rgba(3,5,10,.72) 100%),
linear-gradient(0deg, rgba(3,5,10,.94) 0%, transparent 38%, rgba(3,5,10,.25) 100%);
inset: 0;
position: absolute;
z-index: -2;
}
.scanlines {
background: repeating-linear-gradient(0deg, transparent 0, transparent 3px, rgba(0,0,0,.08) 4px);
inset: 0;
pointer-events: none;
position: absolute;
z-index: 5;
}
.stage-top {
align-items: flex-start;
display: flex;
justify-content: space-between;
padding: clamp(16px, 3vw, 28px);
}
.scenario-card {
backdrop-filter: blur(12px);
background: var(--panel);
border: 1px solid rgba(255,255,255,.17);
border-left: 4px solid var(--gold);
border-radius: 6px 16px 16px 6px;
box-shadow: 0 18px 55px rgba(0,0,0,.34);
max-width: 64%;
padding: clamp(15px, 2.5vw, 25px);
}
.scene-meta {
color: var(--cyan);
font: 800 .68rem ui-monospace, monospace;
letter-spacing: .12em;
text-transform: uppercase;
}
.scenario-card h2 {
color: #fff !important;
font-family: "Arial Black", Impact, sans-serif;
font-size: clamp(1.25rem, 3vw, 2.15rem);
letter-spacing: -.035em;
line-height: 1;
margin: 9px 0 12px;
text-transform: uppercase;
}
.scenario-card p { color: #f0ecdf !important; font-size: clamp(.87rem, 1.5vw, 1rem); line-height: 1.58; margin: 0; }
.source-line { color: #8f8b80; display: block; font: .64rem ui-monospace, monospace; margin-top: 14px; }
.hud {
backdrop-filter: blur(10px);
background: rgba(7,10,18,.84);
border: 1px solid rgba(255,255,255,.14);
border-radius: 14px;
min-width: 210px;
padding: 13px;
width: 25%;
}
.hud-title { color: var(--gold); font: 900 .68rem ui-monospace, monospace; letter-spacing: .12em; margin-bottom: 10px; }
.metric-row { margin: 8px 0; }
.metric-label { color: #d6d2c7; display: flex; font: 700 .62rem ui-monospace, monospace; justify-content: space-between; text-transform: uppercase; }
.metric-track { background: rgba(255,255,255,.11); height: 5px; margin-top: 5px; overflow: hidden; }
.metric-fill { background: linear-gradient(90deg, var(--cyan), var(--green)); height: 100%; }
.metric-row.stress .metric-fill { background: linear-gradient(90deg, #ffb347, var(--coral)); }
.stage-bottom { bottom: 0; display: flex; justify-content: space-between; left: 0; padding: 0 27px 24px; position: absolute; right: 0; }
.persona-bubble {
align-self: flex-end;
backdrop-filter: blur(10px);
background: rgba(10,13,22,.91);
border: 1px solid rgba(255,255,255,.16);
border-radius: 14px;
display: flex;
gap: 12px;
max-width: 58%;
padding: 10px 13px;
}
.persona-bubble img { border: 2px solid var(--cyan); border-radius: 8px; height: 58px; object-fit: cover; width: 58px; }
.persona-copy b { color: var(--cyan); display: block; font: 800 .68rem ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; }
.persona-copy p { color: #eee9dc !important; font-size: .8rem; line-height: 1.4; margin: 5px 0 0; }
.player-wrap { align-items: flex-end; display: flex; gap: 12px; }
.state-pill {
background: rgba(7,10,18,.82);
border: 1px solid rgba(255,255,255,.15);
border-radius: 999px;
color: var(--cream);
font: 800 .63rem ui-monospace, monospace;
margin-bottom: 8px;
padding: 7px 10px;
text-transform: uppercase;
}
.player-sprite {
background-repeat: no-repeat;
background-size: 400% 100%;
filter: drop-shadow(0 16px 9px rgba(0,0,0,.55));
height: 142px;
image-rendering: pixelated;
width: 126px;
}
.cascade-banner {
animation: slide-in .28s ease-out;
background: linear-gradient(90deg, rgba(255,107,95,.96), rgba(118,52,83,.94));
border: 1px solid rgba(255,255,255,.2);
box-shadow: 0 10px 35px rgba(0,0,0,.35);
color: #fff;
left: 50%;
max-width: 70%;
padding: 10px 18px;
position: absolute;
text-align: center;
top: 49%;
transform: translate(-50%, -50%);
z-index: 4;
}
.loading-stage {
align-items: center;
background:
radial-gradient(circle at 50% 35%, rgba(92,225,230,.10), transparent 30%),
linear-gradient(145deg, #11182a, #070a12);
display: flex;
flex-direction: column;
gap: 14px;
justify-content: center;
min-height: 530px;
text-align: center;
}
.loading-orbit {
animation: orbit 1.05s linear infinite;
border: 3px solid rgba(255,255,255,.11);
border-radius: 50%;
border-top-color: var(--gold);
height: 52px;
width: 52px;
}
.loading-stage h2 { color: #fff !important; font: 1.7rem "Arial Black", Impact, sans-serif; margin: 0; text-transform: uppercase; }
.loading-stage p { color: var(--muted) !important; margin: 0; max-width: 460px; }
.loading-steps { color: var(--cyan); font: 700 .67rem ui-monospace, monospace; letter-spacing: .09em; text-transform: uppercase; }
.cascade-banner b { display: block; font: 900 .7rem ui-monospace, monospace; letter-spacing: .12em; text-transform: uppercase; }
.cascade-banner span { font-size: .76rem; }
.report-card {
backdrop-filter: blur(15px);
background: rgba(7,10,18,.94);
border: 1px solid rgba(255,255,255,.18);
border-top: 4px solid var(--gold);
border-radius: 16px;
inset: 6%;
overflow: auto;
padding: clamp(20px, 4vw, 42px);
position: absolute;
z-index: 4;
}
.report-card h2 { color: var(--gold) !important; font: clamp(1.8rem, 5vw, 3.7rem)/.95 "Arial Black", Impact, sans-serif; letter-spacing: -.055em; margin: 8px 0 18px; text-transform: uppercase; }
.report-card p { color: #e7e2d5 !important; line-height: 1.62; max-width: 850px; }
.report-card details { color: #aaa59a; margin-top: 18px; }
.report-card pre { white-space: pre-wrap; }
.choices-shell {
background: rgba(8,11,20,.78);
border: 1px solid rgba(255,255,255,.12);
border-radius: 18px;
margin-top: 14px;
padding: 14px;
}
.choice-heading { align-items: center; display: flex; justify-content: space-between; margin: 2px 2px 11px; }
.choice-heading b { color: #fff; font-family: "Arial Black", Impact, sans-serif; letter-spacing: .02em; text-transform: uppercase; }
.choice-heading span { color: #827e74; font: .66rem ui-monospace, monospace; }
#choice-cards label {
background: rgba(255,255,255,.035) !important;
border: 1px solid rgba(255,255,255,.12) !important;
border-radius: 10px !important;
margin: 6px 0 !important;
padding: 11px 12px !important;
transition: .16s ease;
}
#choice-cards label:hover { background: rgba(255,200,87,.08) !important; border-color: rgba(255,200,87,.55) !important; transform: translateX(3px); }
#choice-cards label:has(input:checked) { background: rgba(255,200,87,.13) !important; border-color: var(--gold) !important; }
#path-options label, #persona-options label {
background: rgba(255,255,255,.045) !important;
border: 1px solid rgba(255,255,255,.11) !important;
color: #e6e1d4 !important;
}
#path-options label:has(input:checked), #persona-options label:has(input:checked) {
background: rgba(255,200,87,.12) !important;
border-color: rgba(255,200,87,.7) !important;
}
#detected-paths { color: var(--cyan) !important; opacity: .74; }
#reset-button {
background: transparent !important;
border: 1px solid rgba(255,255,255,.15) !important;
color: #aaa59a !important;
margin-top: 11px;
}
#reset-button:hover { border-color: rgba(255,200,87,.45) !important; color: var(--gold) !important; }
@keyframes slide-in { from { opacity: 0; transform: translate(-50%, -42%); } to { opacity: 1; transform: translate(-50%, -50%); } }
@keyframes orbit { to { transform: rotate(360deg); } }
@media (max-width: 760px) {
.gradio-container { padding: 12px !important; }
.app-header { align-items: flex-start; flex-direction: column; }
.game-frame { aspect-ratio: auto; min-height: 660px; }
.stage-top { display: block; }
.scenario-card { max-width: 100%; }
.hud { margin-top: 10px; width: 100%; }
.metric-row { display: inline-block; margin: 6px 1.2%; width: 46%; }
.stage-bottom { padding: 0 15px 17px; }
.persona-bubble { max-width: 74%; }
.player-sprite { height: 105px; width: 93px; }
.state-pill { display: none; }
}
"""
@lru_cache(maxsize=32)
def image_data_uri(path_text: str) -> str:
path = Path(path_text)
mime = mimetypes.guess_type(path.name)[0] or "image/png"
encoded = base64.b64encode(path.read_bytes()).decode("ascii")
return f"data:{mime};base64,{encoded}"
def parse_paths(dilemma: str) -> tuple[gr.Radio, str]:
path_a, path_b = split_dilemma(dilemma)
return (
gr.Radio(choices=[(path_a, "A"), (path_b, "B")], value="A", label="Choose the future to enter"),
f"PATHS DETECTED: {path_a.upper()} / {path_b.upper()}",
)
def begin(dilemma: str, chosen_key: str, calibration: str, persona: str):
if len(dilemma.strip()) < 5:
raise gr.Error("Enter a real dilemma first.")
if not calibration.strip():
raise gr.Error("Add one concrete pressure, constraint, or proof point.")
yield (
None,
loading_html(dilemma, calibration),
gr.Radio(choices=[], value=None),
"",
gr.Group(visible=False),
gr.Column(visible=False),
gr.Group(visible=False),
gr.Group(visible=True),
gr.Button(interactive=False, value="Building your world..."),
)
session = start_session(dilemma, chosen_key or "A", calibration, persona, prefetch=False)
node = current_node(session)
yield (
session,
game_html(session, node),
gr.Radio(choices=_choice_options(node), value=None, label="Choose your move"),
"",
gr.Group(visible=False),
gr.Column(visible=True),
gr.Group(visible=False),
gr.Group(visible=True),
gr.Button(interactive=True, value="Commit choice"),
)
prefetch_node(session, 1)
def make_choice(session: SimulationSession, choice_value: str | None, custom_choice: str):
if session is None:
raise gr.Error("Start a simulation first.")
custom = custom_choice.strip()
if not custom and choice_value is None:
raise gr.Error("Choose an option or write your own move.")
result = choose(session, int(choice_value or 0), custom)
if result["complete"]:
return (
session,
game_html(
session,
reaction=result.get("reaction", ""),
cascade=result.get("cascade"),
report=result["report"],
),
gr.Radio(choices=[], value=None),
"",
gr.Group(visible=True),
gr.Column(visible=False),
gr.Group(visible=False),
gr.Button(interactive=False),
)
node = result["node"]
return (
session,
game_html(
session,
node,
reaction=result.get("reaction", ""),
cascade=result.get("cascade"),
),
gr.Radio(choices=_choice_options(node), value=None, label="Choose your move"),
"",
gr.Group(visible=False),
gr.Column(visible=True),
gr.Group(visible=False),
gr.Button(interactive=True),
)
def reset():
return (
None,
"",
gr.Radio(choices=[], value=None),
"",
gr.Group(visible=False),
gr.Column(visible=False),
gr.Group(visible=True),
gr.Group(visible=False),
gr.Button(interactive=True),
)
def loading_html(dilemma: str, calibration: str) -> str:
path_a, path_b = split_dilemma(dilemma)
detail = " ".join(calibration.split())[:150]
return f"""
<div class="game-frame loading-stage">
<div class="loading-orbit"></div>
<div class="eyebrow">Building a personalized causal world</div>
<h2>{html.escape(path_a)} / {html.escape(path_b)}</h2>
<p>Connecting your real constraint to the opening scene: {html.escape(detail)}</p>
<div class="loading-steps">Calibrating state / placing consequences / preparing choices</div>
</div>
"""
def game_html(
session: SimulationSession,
node: dict[str, Any] | None = None,
reaction: str = "",
cascade: dict[str, str] | None = None,
report: dict[str, Any] | None = None,
) -> str:
state = environment_state(session.world_state)
expression = character_expression(session.world_state)
background = image_data_uri(environment_image(session))
sprite = image_data_uri(str(ROOT / "assets" / "characters" / "character.png"))
portrait = image_data_uri(str(PERSONA_ASSETS[session.persona]))
sprite_position = {"neutral": "0%", "stressed": "33.333%", "confident": "66.666%"}[expression]
node = node or {
"month_label": "Future report",
"node_theme": "reflection",
"scenario": "",
"generation_source": "causal ledger",
}
cascade_markup = ""
if cascade:
cascade_markup = f"""
<div class="cascade-banner">
<b>{html.escape(cascade['label'])}</b>
<span>{html.escape(cascade['memory'])} {html.escape(cascade['effect'])}</span>
</div>
"""
persona_text = reaction or (
f"I'll stay with you through this version of {session.chosen_path}. "
"Pay attention to what each choice costs, not only what it promises."
)
persona_markup = f"""
<div class="persona-bubble">
<img src="{portrait}" alt="{html.escape(session.persona)}">
<div class="persona-copy">
<b>{html.escape(session.persona)}</b>
<p>{html.escape(persona_text)}</p>
</div>
</div>
"""
report_markup = report_html(report) if report else ""
return f"""
<div class="game-frame">
<div class="world-bg" style="background-image:url('{background}')"></div>
<div class="world-shade"></div>
<div class="stage-top">
<section class="scenario-card">
<div class="scene-meta">{html.escape(node['month_label'])} / {html.escape(node['node_theme'])}</div>
<h2>{html.escape(session.chosen_path)}</h2>
<p>{html.escape(node['scenario'])}</p>
<small class="source-line">SOURCE: {html.escape(node.get('generation_source', 'deterministic')).upper()}</small>
</section>
{metrics_html(session)}
</div>
{cascade_markup}
<div class="stage-bottom">
{persona_markup}
<div class="player-wrap">
<span class="state-pill">World: {state} / You: {expression}</span>
<div class="player-sprite" style="background-image:url('{sprite}');background-position:{sprite_position} 0"></div>
</div>
</div>
{report_markup}
<div class="scanlines"></div>
</div>
"""
def metrics_html(session: SimulationSession) -> str:
labels = {
"financial_security": "Financial",
"creative_fulfillment": "Fulfillment",
"social_validation": "Validation",
"stress": "Stress",
"family_satisfaction": "Family",
}
rows = []
for key in METRICS:
value = session.world_state[key]
rows.append(
f"""
<div class="metric-row {'stress' if key == 'stress' else ''}">
<div class="metric-label"><span>{labels[key]}</span><span>{value}</span></div>
<div class="metric-track"><div class="metric-fill" style="width:{value}%"></div></div>
</div>
"""
)
return f"<aside class='hud'><div class='hud-title'>LIVE WORLD STATE</div>{''.join(rows)}</aside>"
def report_html(report: dict[str, Any]) -> str:
ledger = html.escape(
json.dumps(
{"facts": report["facts"], "obligations": report["obligations"]},
indent=2,
)
)
return f"""
<section class="report-card">
<div class="eyebrow">Simulation complete / causal reflection</div>
<h2>{html.escape(report['archetype'])}</h2>
<p>{html.escape(report['summary'])}</p>
<p>{html.escape(report['honest_mirror'])}</p>
<details><summary>Open the causal ledger</summary><pre>{ledger}</pre></details>
</section>
"""
def _choice_options(node: dict[str, Any]) -> list[tuple[str, str]]:
return [(choice["text"], str(index)) for index, choice in enumerate(node["choices"])]
theme = gr.themes.Base(
primary_hue="orange",
neutral_hue="slate",
font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui"],
).set(
body_background_fill="#070a12",
block_background_fill="#0d1120",
block_border_color="rgba(255,255,255,.12)",
input_background_fill="#080b15",
input_border_color="rgba(255,255,255,.14)",
)
with gr.Blocks(css=CSS, theme=theme, title="LifeChoice Simulator") as demo:
session_state = gr.State()
gr.HTML(
f"""
<header class="app-header">
<div class="brand-lockup">
<div class="eyebrow">A causal future simulator</div>
<h1>Life<span>Choice</span></h1>
<p>Enter one road not taken. Make decisions inside it. Watch the world remember.</p>
</div>
<div class="model-chip">{MODEL_ID} / 7B</div>
</header>
"""
)
with gr.Group(elem_classes="setup-card") as setup_group:
with gr.Row(equal_height=True):
with gr.Column(scale=5):
gr.HTML(
"""
<section class="setup-intro">
<div class="eyebrow">New simulation</div>
<h2>Try the future<br>before it happens.</h2>
<p>This is not a chatbot. It is an eight-node causal simulation with persistent metrics, delayed consequences, and a world that changes with your decisions.</p>
<div class="feature-list">
<div class="feature"><b>01</b> Immediate first scene</div>
<div class="feature"><b>02</b> Choices produce distinct state changes</div>
<div class="feature"><b>03</b> Your persona remembers what happened</div>
</div>
</section>
"""
)
with gr.Column(scale=6, elem_classes="form-panel"):
dilemma = gr.Textbox(
label="The fork in the road",
placeholder="Artist vs Software Engineer",
value="MTech vs Software Job",
)
path_choice = gr.Radio(
choices=[("MTech", "A"), ("Software Job", "B")],
value="A",
label="Choose the future to enter",
elem_id="path-options",
)
detected = gr.Markdown(
"PATHS DETECTED: MTECH / SOFTWARE JOB",
elem_classes="eyebrow",
elem_id="detected-paths",
)
calibration = gr.Textbox(
label="What makes this decision real?",
placeholder="A pressure, deadline, responsibility, or proof point...",
value="My family needs income soon, but a professor is already interested in my research.",
lines=2,
)
persona = gr.Radio(
choices=["Family", "Friend", "Mentor", "Partner", "Inner Voice"],
value="Mentor",
label="Choose the voice that follows you",
elem_id="persona-options",
)
begin_btn = gr.Button("Enter this future", variant="primary", elem_id="begin-button")
gr.HTML(
"<p class='microcopy'>Reflective simulation only. Not medical, legal, financial, or professional advice. The first scene starts immediately while later scenes prepare in the background.</p>"
)
with gr.Group(visible=False) as game_group:
game_board = gr.HTML()
with gr.Column(visible=False, elem_classes="choices-shell") as play_column:
gr.HTML("<div class='choice-heading'><b>What do you do?</b><span>YOUR DECISION CHANGES THE STATE</span></div>")
choices = gr.Radio(choices=[], show_label=False, elem_id="choice-cards")
custom = gr.Textbox(
label="Or make your own move",
placeholder="Describe one specific action...",
lines=1,
)
choose_btn = gr.Button("Commit choice", variant="primary", elem_id="commit-button")
with gr.Group(visible=False) as report_group:
gr.Markdown(
"The report reflects patterns in your causal ledger. It does not predict your future or tell you what to do."
)
reset_btn = gr.Button("Start a different simulation", variant="secondary", elem_id="reset-button")
dilemma.change(parse_paths, dilemma, [path_choice, detected], show_progress="hidden")
begin_btn.click(
begin,
[dilemma, path_choice, calibration, persona],
[session_state, game_board, choices, custom, report_group, play_column, setup_group, game_group, choose_btn],
show_progress="hidden",
)
choose_btn.click(
make_choice,
[session_state, choices, custom],
[session_state, game_board, choices, custom, report_group, play_column, setup_group, choose_btn],
)
reset_btn.click(
reset,
outputs=[session_state, game_board, choices, custom, report_group, play_column, setup_group, game_group, choose_btn],
show_progress="hidden",
)
if __name__ == "__main__":
demo.queue(default_concurrency_limit=8).launch()