Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# app.py
|
| 2 |
-
# @title Beer Game Final Version (
|
| 3 |
|
| 4 |
# -----------------------------------------------------------------------------
|
| 5 |
# 1. Import Libraries
|
|
@@ -129,7 +129,7 @@ def init_game_state(llm_personality: str, info_sharing: str, participant_id: str
|
|
| 129 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 130 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
| 131 |
}
|
| 132 |
-
st.info(f"New game started for **{participant_id}**! You are the **{human_role}**.")
|
| 133 |
|
| 134 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 135 |
if not client: return 8, "NO_API_KEY_DEFAULT"
|
|
@@ -331,7 +331,7 @@ def plot_results(df: pd.DataFrame, title: str, human_role: str):
|
|
| 331 |
inventory_pivot = plot_df.pivot(index='week', columns='echelon', values='inventory').reindex(columns=echelons)
|
| 332 |
inventory_pivot.plot(ax=axes[0], kind='line', marker='o', markersize=4); axes[0].set_title('Inventory Levels (End of Week)'); axes[0].grid(True, linestyle='--'); axes[0].set_ylabel('Stock (Units)')
|
| 333 |
order_pivot = plot_df.pivot(index='week', columns='echelon', values='order_placed').reindex(columns=echelons)
|
| 334 |
-
order_pivot.plot(ax=axes[1], style='--'); axes[1].plot(range(1, WEEKS + 1), [get_customer_demand(
|
| 335 |
total_costs = plot_df.loc[plot_df.groupby('echelon')['week'].idxmax()]
|
| 336 |
total_costs = total_costs.set_index('echelon')['total_cost'].reindex(echelons, fill_value=0)
|
| 337 |
total_costs.plot(kind='bar', ax=axes[2], rot=0); axes[2].set_title('Total Cumulative Cost'); axes[2].set_ylabel('Cost ($)')
|
|
@@ -485,13 +485,23 @@ if 'comprehension_passed' not in st.session_state: st.session_state['comprehensi
|
|
| 485 |
|
| 486 |
if st.session_state.get('initialization_error'):
|
| 487 |
st.error(st.session_state.initialization_error)
|
|
|
|
| 488 |
elif not st.session_state['consent_given']:
|
| 489 |
st.header("📝 Participant Consent Form")
|
| 490 |
-
st.markdown("""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
with st.form("consent_form"):
|
| 492 |
-
|
| 493 |
if st.form_submit_button("Continue"):
|
| 494 |
-
if
|
| 495 |
st.session_state['consent_given'] = True
|
| 496 |
st.session_state['consent_timestamp'] = datetime.utcnow().isoformat() + "Z"
|
| 497 |
st.rerun()
|
|
@@ -503,13 +513,16 @@ elif not st.session_state['comprehension_passed']:
|
|
| 503 |
user_answers = {}
|
| 504 |
for i, q_data in enumerate(COMPREHENSION_QUESTIONS):
|
| 505 |
st.subheader(q_data['q'])
|
| 506 |
-
user_answers[i] = st.radio("Select:", q_data['options'], key=f"comp_q_{i}", index=None)
|
|
|
|
| 507 |
if st.form_submit_button("Submit Answers"):
|
| 508 |
all_correct = True
|
| 509 |
for i, q_data in enumerate(COMPREHENSION_QUESTIONS):
|
| 510 |
if user_answers.get(i) != q_data['options'][q_data['correct_index']]:
|
| 511 |
all_correct = False
|
| 512 |
-
if all_correct:
|
|
|
|
|
|
|
| 513 |
else: st.error("Incorrect answers.")
|
| 514 |
|
| 515 |
else:
|
|
@@ -522,7 +535,8 @@ else:
|
|
| 522 |
state = st.session_state.game_state
|
| 523 |
participant_id = state['participant_id']
|
| 524 |
url = f"{QUALTRICS_BASE_URL}?{PID_FIELD_NAME}={participant_id}"
|
| 525 |
-
st.
|
|
|
|
| 526 |
try:
|
| 527 |
logs_df = pd.json_normalize(state['logs'])
|
| 528 |
st.pyplot(plot_results(logs_df, f"Beer Game (Human: {state['human_role']})", state['human_role']))
|
|
@@ -557,10 +571,9 @@ else:
|
|
| 557 |
c1, c2, c3 = st.columns(3)
|
| 558 |
with c1: st.metric("Inventory", e['inventory']); st.metric("Backlog", e['backlog'])
|
| 559 |
with c2: st.write(f"**Incoming Order:**\n# {e['incoming_order']}")
|
| 560 |
-
with c3: st.write(f"**Arriving Next:**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 561 |
|
| 562 |
st.markdown("---")
|
| 563 |
-
st.header("Your Decision")
|
| 564 |
if state['decision_step'] == 'initial_order':
|
| 565 |
with st.form(key="initial_order_form"):
|
| 566 |
initial_order = st.number_input("Your Initial Order Quantity:", min_value=0, step=1, value=None)
|
|
@@ -581,11 +594,16 @@ else:
|
|
| 581 |
step_game(int(final_order) if final_order is not None else 0, state['human_initial_order'], ai_suggestion)
|
| 582 |
st.rerun()
|
| 583 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 584 |
else:
|
| 585 |
st.header("⚙️ Game Configuration")
|
| 586 |
participant_id = st.text_input("Enter Your Name or Team ID:", key="participant_id_input")
|
| 587 |
|
| 588 |
-
# ---
|
| 589 |
c1, c2 = st.columns(2)
|
| 590 |
with c1:
|
| 591 |
llm_personality = st.selectbox("AI Agent Personality", ('human_like', 'perfect_rational'), format_func=lambda x: x.replace('_', ' ').title())
|
|
@@ -596,11 +614,11 @@ else:
|
|
| 596 |
if participant_id:
|
| 597 |
init_game_state(llm_personality, info_sharing, participant_id)
|
| 598 |
st.rerun()
|
| 599 |
-
else: st.error("Please enter
|
| 600 |
show_leaderboard_ui()
|
| 601 |
|
| 602 |
# --- Instructor Zone ---
|
| 603 |
st.sidebar.markdown("---")
|
| 604 |
with st.sidebar.expander("🔐 Instructor Zone"):
|
| 605 |
-
if st.text_input("Admin Password:", type="password") == ADMIN_PASSWORD:
|
| 606 |
if st.checkbox("Show Global Leaderboard"): show_leaderboard_ui()
|
|
|
|
| 1 |
# app.py
|
| 2 |
+
# @title Beer Game Final Version (v4.27 - STRICT VERBATIM RESTORE - MANUAL SELECTION)
|
| 3 |
|
| 4 |
# -----------------------------------------------------------------------------
|
| 5 |
# 1. Import Libraries
|
|
|
|
| 129 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 130 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
| 131 |
}
|
| 132 |
+
st.info(f"New game started for **{participant_id}**! AI Mode: **{llm_personality} / {info_sharing}**. You are the **{human_role}**.")
|
| 133 |
|
| 134 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 135 |
if not client: return 8, "NO_API_KEY_DEFAULT"
|
|
|
|
| 331 |
inventory_pivot = plot_df.pivot(index='week', columns='echelon', values='inventory').reindex(columns=echelons)
|
| 332 |
inventory_pivot.plot(ax=axes[0], kind='line', marker='o', markersize=4); axes[0].set_title('Inventory Levels (End of Week)'); axes[0].grid(True, linestyle='--'); axes[0].set_ylabel('Stock (Units)')
|
| 333 |
order_pivot = plot_df.pivot(index='week', columns='echelon', values='order_placed').reindex(columns=echelons)
|
| 334 |
+
order_pivot.plot(ax=axes[1], style='--'); axes[1].plot(range(1, WEEKS + 1), [get_customer_demand(w) for w in range(1, WEEKS + 1)], label='Customer Demand', color='black', lw=2.5); axes[1].set_title('Order Quantities / Production Decisions'); axes[1].grid(True, linestyle='--'); axes[1].legend(); axes[1].set_ylabel('Ordered/Produced (Units)')
|
| 335 |
total_costs = plot_df.loc[plot_df.groupby('echelon')['week'].idxmax()]
|
| 336 |
total_costs = total_costs.set_index('echelon')['total_cost'].reindex(echelons, fill_value=0)
|
| 337 |
total_costs.plot(kind='bar', ax=axes[2], rot=0); axes[2].set_title('Total Cumulative Cost'); axes[2].set_ylabel('Cost ($)')
|
|
|
|
| 485 |
|
| 486 |
if st.session_state.get('initialization_error'):
|
| 487 |
st.error(st.session_state.initialization_error)
|
| 488 |
+
|
| 489 |
elif not st.session_state['consent_given']:
|
| 490 |
st.header("📝 Participant Consent Form")
|
| 491 |
+
st.markdown("""
|
| 492 |
+
**Lead Researcher:** Xinyu Li
|
| 493 |
+
**Supervisor:** Professor Li Ding & Dr Yanlu Zhao
|
| 494 |
+
**Contact Email:** xinyu.li3@durham.ac.uk
|
| 495 |
+
|
| 496 |
+
Please read the following statements carefully. By agreeing to participate, you are confirming that you understand all of the following:
|
| 497 |
+
- Your participation is voluntary.
|
| 498 |
+
- Your participation is anonymous.
|
| 499 |
+
- You confirm that you are **18 years of age or older**.
|
| 500 |
+
""")
|
| 501 |
with st.form("consent_form"):
|
| 502 |
+
choice = st.radio("**Do you agree to take part in this study?**", ('Yes, I agree to participate in this study.', 'No, I do not agree to participate in this study.'), index=None)
|
| 503 |
if st.form_submit_button("Continue"):
|
| 504 |
+
if choice == 'Yes, I agree to participate in this study.':
|
| 505 |
st.session_state['consent_given'] = True
|
| 506 |
st.session_state['consent_timestamp'] = datetime.utcnow().isoformat() + "Z"
|
| 507 |
st.rerun()
|
|
|
|
| 513 |
user_answers = {}
|
| 514 |
for i, q_data in enumerate(COMPREHENSION_QUESTIONS):
|
| 515 |
st.subheader(q_data['q'])
|
| 516 |
+
user_answers[i] = st.radio("Select an option:", q_data['options'], key=f"comp_q_{i}", index=None, label_visibility="collapsed")
|
| 517 |
+
st.markdown("---")
|
| 518 |
if st.form_submit_button("Submit Answers"):
|
| 519 |
all_correct = True
|
| 520 |
for i, q_data in enumerate(COMPREHENSION_QUESTIONS):
|
| 521 |
if user_answers.get(i) != q_data['options'][q_data['correct_index']]:
|
| 522 |
all_correct = False
|
| 523 |
+
if all_correct:
|
| 524 |
+
st.session_state['comprehension_passed'] = True
|
| 525 |
+
st.rerun()
|
| 526 |
else: st.error("Incorrect answers.")
|
| 527 |
|
| 528 |
else:
|
|
|
|
| 535 |
state = st.session_state.game_state
|
| 536 |
participant_id = state['participant_id']
|
| 537 |
url = f"{QUALTRICS_BASE_URL}?{PID_FIELD_NAME}={participant_id}"
|
| 538 |
+
st.warning(f"Your ID: **{participant_id}**. Please click below to start the survey.")
|
| 539 |
+
st.markdown(f'<a href="{url}" target="_blank"><button style="background-color: #4CAF50; color: white; padding: 15px 32px; font-size: 16px; cursor: pointer; border: none; border-radius: 8px;">Click Here to Start the Survey</button></a>', unsafe_allow_html=True)
|
| 540 |
try:
|
| 541 |
logs_df = pd.json_normalize(state['logs'])
|
| 542 |
st.pyplot(plot_results(logs_df, f"Beer Game (Human: {state['human_role']})", state['human_role']))
|
|
|
|
| 571 |
c1, c2, c3 = st.columns(3)
|
| 572 |
with c1: st.metric("Inventory", e['inventory']); st.metric("Backlog", e['backlog'])
|
| 573 |
with c2: st.write(f"**Incoming Order:**\n# {e['incoming_order']}")
|
| 574 |
+
with c3: st.write(f"**Shipment Arriving Next:**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 575 |
|
| 576 |
st.markdown("---")
|
|
|
|
| 577 |
if state['decision_step'] == 'initial_order':
|
| 578 |
with st.form(key="initial_order_form"):
|
| 579 |
initial_order = st.number_input("Your Initial Order Quantity:", min_value=0, step=1, value=None)
|
|
|
|
| 594 |
step_game(int(final_order) if final_order is not None else 0, state['human_initial_order'], ai_suggestion)
|
| 595 |
st.rerun()
|
| 596 |
|
| 597 |
+
if Path(IMAGE_PATH).exists(): st.sidebar.image(IMAGE_PATH, use_column_width=True)
|
| 598 |
+
if st.sidebar.button("🔄 Reset Game"):
|
| 599 |
+
if 'game_state' in st.session_state: del st.session_state.game_state
|
| 600 |
+
st.rerun()
|
| 601 |
+
|
| 602 |
else:
|
| 603 |
st.header("⚙️ Game Configuration")
|
| 604 |
participant_id = st.text_input("Enter Your Name or Team ID:", key="participant_id_input")
|
| 605 |
|
| 606 |
+
# --- MANUAL SELECTION ADDED BACK ---
|
| 607 |
c1, c2 = st.columns(2)
|
| 608 |
with c1:
|
| 609 |
llm_personality = st.selectbox("AI Agent Personality", ('human_like', 'perfect_rational'), format_func=lambda x: x.replace('_', ' ').title())
|
|
|
|
| 614 |
if participant_id:
|
| 615 |
init_game_state(llm_personality, info_sharing, participant_id)
|
| 616 |
st.rerun()
|
| 617 |
+
else: st.error("Please enter a Name or Team ID.")
|
| 618 |
show_leaderboard_ui()
|
| 619 |
|
| 620 |
# --- Instructor Zone ---
|
| 621 |
st.sidebar.markdown("---")
|
| 622 |
with st.sidebar.expander("🔐 Instructor Zone"):
|
| 623 |
+
if st.text_input("Admin Password:", type="password", key="admin_pwd") == ADMIN_PASSWORD:
|
| 624 |
if st.checkbox("Show Global Leaderboard"): show_leaderboard_ui()
|