Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -100,7 +100,8 @@ def init_game_state(llm_personality: str, info_sharing: str, participant_id: str
|
|
| 100 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 101 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
| 102 |
}
|
| 103 |
-
|
|
|
|
| 104 |
# ==============================================================================
|
| 105 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 106 |
# This function remains correct.
|
|
@@ -614,7 +615,8 @@ else:
|
|
| 614 |
# 默认值,如果未输入ID
|
| 615 |
llm_personality, info_sharing = ('human_like', 'local')
|
| 616 |
|
| 617 |
-
#
|
|
|
|
| 618 |
# =================================================================
|
| 619 |
|
| 620 |
# 移除原有的 c1, c2 和 selectbox
|
|
@@ -655,174 +657,172 @@ else:
|
|
| 655 |
week, human_role, echelons, info_sharing = state['week'], state['human_role'], state['echelons'], state['info_sharing']
|
| 656 |
echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"] # Define here for UI
|
| 657 |
|
| 658 |
-
#
|
| 659 |
-
col_main, col_sidebar_image = st.columns([4, 1])
|
| 660 |
|
| 661 |
-
with col_main:
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
st.metric("Inventory (Opening)", e['inventory'])
|
| 682 |
-
st.metric("Backlog (Opening)", e['backlog'])
|
| 683 |
-
|
| 684 |
-
current_incoming_order = 0
|
| 685 |
-
if name == "Retailer":
|
| 686 |
-
current_incoming_order = get_customer_demand(week)
|
| 687 |
-
else:
|
| 688 |
-
downstream_name = e['downstream_name']
|
| 689 |
-
if downstream_name:
|
| 690 |
-
current_incoming_order = state['last_week_orders'].get(downstream_name, 0)
|
| 691 |
-
st.write(f"Incoming Order (This Week): **{current_incoming_order}**")
|
| 692 |
-
|
| 693 |
-
if name == "Factory":
|
| 694 |
-
prod_completing_next = state['last_week_orders'].get("Distributor", 0)
|
| 695 |
-
st.write(f"Completing Next Week: **{prod_completing_next}**")
|
| 696 |
-
else:
|
| 697 |
-
arriving_next = 0
|
| 698 |
-
q = e['incoming_shipments']
|
| 699 |
-
if q: arriving_next = list(q)[0] # Read W+1
|
| 700 |
-
st.write(f"Arriving Next Week: **{arriving_next}**")
|
| 701 |
-
else: # Local Info Mode
|
| 702 |
-
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 703 |
-
e = echelons[human_role] # Distributor
|
| 704 |
-
st.markdown(f"### 👤 **<span style='color:#FF4B4B;'>{human_role} (Your Dashboard - Start of Week State)</span>**", unsafe_allow_html=True)
|
| 705 |
-
col1, col2, col3 = st.columns(3)
|
| 706 |
-
with col1:
|
| 707 |
st.metric("Inventory (Opening)", e['inventory'])
|
| 708 |
st.metric("Backlog (Opening)", e['backlog'])
|
| 709 |
-
|
| 710 |
-
with col2:
|
| 711 |
current_incoming_order = 0
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
if
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
inv_after_arrival = e_curr['inventory'] + arrived
|
| 744 |
-
backlog_after_new_order = e_curr['backlog'] + inc_order_this_week
|
| 745 |
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
if state['decision_step'] == 'initial_order':
|
| 754 |
-
with st.form(key="initial_order_form"):
|
| 755 |
-
st.markdown("#### **Step 4a:** Based on the dashboard, submit your **initial** order to the Factory.")
|
| 756 |
-
initial_order = st.number_input("Your Initial Order Quantity:", min_value=0, step=1, value=None) # Start blank
|
| 757 |
-
if st.form_submit_button("Submit Initial Order & See AI Suggestion", type="primary"):
|
| 758 |
-
state['human_initial_order'] = int(initial_order) if initial_order is not None else 0
|
| 759 |
-
state['decision_step'] = 'final_order'
|
| 760 |
-
prompt_sugg = get_llm_prompt(human_echelon_state_for_prompt, week, state['llm_personality'], state['info_sharing'], all_decision_point_states)
|
| 761 |
-
ai_suggestion, _ = get_llm_order_decision(prompt_sugg, f"{human_role} (Suggestion)")
|
| 762 |
-
state['current_ai_suggestion'] = ai_suggestion # Store it
|
| 763 |
-
st.rerun()
|
| 764 |
-
elif state['decision_step'] == 'final_order':
|
| 765 |
-
st.success(f"Your initial order was: **{state['human_initial_order']}** units.")
|
| 766 |
-
ai_suggestion = state.get('current_ai_suggestion', 4) # Read stored value
|
| 767 |
-
with st.form(key="final_order_form"):
|
| 768 |
-
st.markdown(f"#### **Step 4b:** The AI suggests ordering **{ai_suggestion}** units.")
|
| 769 |
-
st.markdown("Considering the AI's advice, submit your **final** order to end the week. (This order will arrive in 3 weeks).")
|
| 770 |
-
st.number_input("Your Final Order Quantity:", min_value=0, step=1, key='final_order_input', value=None) # Start blank
|
| 771 |
-
|
| 772 |
-
if st.form_submit_button("Submit Final Order & Advance to Next Week"):
|
| 773 |
-
final_order_value = st.session_state.get('final_order_input', 0)
|
| 774 |
-
final_order_value = int(final_order_value) if final_order_value is not None else 0
|
| 775 |
-
|
| 776 |
-
step_game(final_order_value, state['human_initial_order'], ai_suggestion)
|
| 777 |
-
|
| 778 |
-
if 'final_order_input' in st.session_state: del st.session_state.final_order_input
|
| 779 |
-
st.rerun()
|
| 780 |
-
|
| 781 |
-
st.markdown("---")
|
| 782 |
-
with st.expander("📖 Your Weekly Decision Log", expanded=False):
|
| 783 |
-
if not state.get('logs'):
|
| 784 |
-
st.write("Your weekly history will be displayed here after you complete the first week.")
|
| 785 |
-
else:
|
| 786 |
-
try:
|
| 787 |
-
history_df = pd.json_normalize(state['logs'])
|
| 788 |
-
human_cols = {
|
| 789 |
-
'week': 'Week', f'{human_role}.opening_inventory': 'Opening Inv.',
|
| 790 |
-
f'{human_role}.opening_backlog': 'Opening Backlog',
|
| 791 |
-
f'{human_role}.incoming_order': 'Incoming Order', f'{human_role}.initial_order': 'Your Initial Order',
|
| 792 |
-
f'{human_role}.ai_suggestion': 'AI Suggestion', f'{human_role}.order_placed': 'Your Final Order',
|
| 793 |
-
f'{human_role}.arriving_next_week': 'Arriving Next Week', f'{human_role}.weekly_cost': 'Weekly Cost',
|
| 794 |
-
}
|
| 795 |
-
ordered_display_cols_keys = [
|
| 796 |
-
'week', f'{human_role}.opening_inventory', f'{human_role}.opening_backlog',
|
| 797 |
-
f'{human_role}.incoming_order',
|
| 798 |
-
f'{human_role}.initial_order', f'{human_role}.ai_suggestion', f'{human_role}.order_placed',
|
| 799 |
-
f'{human_role}.arriving_next_week', f'{human_role}.weekly_cost'
|
| 800 |
-
]
|
| 801 |
-
final_cols_to_display = [col for col in ordered_display_cols_keys if col in history_df.columns]
|
| 802 |
-
if not final_cols_to_display:
|
| 803 |
-
st.write("No data columns available to display.")
|
| 804 |
-
else:
|
| 805 |
-
display_df = history_df[final_cols_to_display].rename(columns=human_cols)
|
| 806 |
-
if 'Weekly Cost' in display_df.columns:
|
| 807 |
-
display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}" if isinstance(x, (int, float)) else "")
|
| 808 |
-
st.dataframe(display_df.sort_values(by="Week", ascending=False), hide_index=True, use_container_width=True)
|
| 809 |
-
except Exception as e:
|
| 810 |
-
st.error(f"Error displaying weekly log: {e}")
|
| 811 |
|
| 812 |
-
#
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
if
|
| 822 |
-
if '
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
|
| 827 |
# --- Game Over Interface ---
|
| 828 |
if 'game_state' in st.session_state and not st.session_state.game_state.get('game_running', False) and st.session_state.game_state['week'] > WEEKS:
|
|
@@ -832,7 +832,8 @@ else:
|
|
| 832 |
logs_df = pd.json_normalize(state['logs'])
|
| 833 |
fig = plot_results(
|
| 834 |
logs_df,
|
| 835 |
-
|
|
|
|
| 836 |
state['human_role']
|
| 837 |
)
|
| 838 |
st.pyplot(fig)
|
|
|
|
| 100 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 101 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
| 102 |
}
|
| 103 |
+
# [修改点 2.1]: 移除启动时显示实验条件的提示
|
| 104 |
+
st.info(f"New game started for **{participant_id}**! You are the **{human_role}**.")
|
| 105 |
# ==============================================================================
|
| 106 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 107 |
# This function remains correct.
|
|
|
|
| 615 |
# 默认值,如果未输入ID
|
| 616 |
llm_personality, info_sharing = ('human_like', 'local')
|
| 617 |
|
| 618 |
+
# [修改点 2.2]: 移除启动时显示实验条件的提示
|
| 619 |
+
# st.info(f"You will be automatically assigned to the condition: **{llm_personality.replace('_', ' ').title()} / {info_sharing.title()}**.")
|
| 620 |
# =================================================================
|
| 621 |
|
| 622 |
# 移除原有的 c1, c2 和 selectbox
|
|
|
|
| 657 |
week, human_role, echelons, info_sharing = state['week'], state['human_role'], state['echelons'], state['info_sharing']
|
| 658 |
echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"] # Define here for UI
|
| 659 |
|
| 660 |
+
# [修改点 1.1]: 移除 st.columns,恢复主内容到左侧,侧边栏不变
|
| 661 |
+
# col_main, col_sidebar_image = st.columns([4, 1]) 移除
|
| 662 |
|
| 663 |
+
# with col_main: 移除
|
| 664 |
+
st.header(f"Week {week} / {WEEKS}")
|
| 665 |
+
# [修改点 2.3]: 移除 subheader 中 AI Mode 和 Information 的��示
|
| 666 |
+
st.subheader(f"Your Role: **{human_role}** ({state['participant_id']})")
|
| 667 |
+
st.markdown("---")
|
| 668 |
+
st.subheader("Supply Chain Status (Start of Week State)")
|
| 669 |
+
|
| 670 |
+
# --- MODIFIED UI LOGIC (v12) ---
|
| 671 |
+
if info_sharing == 'full':
|
| 672 |
+
cols = st.columns(4)
|
| 673 |
+
for i, name in enumerate(echelon_order):
|
| 674 |
+
with cols[i]:
|
| 675 |
+
e = echelons[name]
|
| 676 |
+
icon = "👤" if name == human_role else "🤖"
|
| 677 |
+
if name == human_role:
|
| 678 |
+
st.markdown(f"##### **<span style='border: 1px solid #FF4B4B; padding: 2px 5px; border-radius: 3px;'>{icon} {name} (You)</span>**", unsafe_allow_html=True)
|
| 679 |
+
else:
|
| 680 |
+
st.markdown(f"##### {icon} {name}")
|
| 681 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
st.metric("Inventory (Opening)", e['inventory'])
|
| 683 |
st.metric("Backlog (Opening)", e['backlog'])
|
| 684 |
+
|
|
|
|
| 685 |
current_incoming_order = 0
|
| 686 |
+
if name == "Retailer":
|
| 687 |
+
current_incoming_order = get_customer_demand(week)
|
| 688 |
+
else:
|
| 689 |
+
downstream_name = e['downstream_name']
|
| 690 |
+
if downstream_name:
|
| 691 |
+
current_incoming_order = state['last_week_orders'].get(downstream_name, 0)
|
| 692 |
+
st.write(f"Incoming Order (This Week): **{current_incoming_order}**")
|
| 693 |
+
|
| 694 |
+
if name == "Factory":
|
| 695 |
+
prod_completing_next = state['last_week_orders'].get("Distributor", 0)
|
| 696 |
+
st.write(f"Completing Next Week: **{prod_completing_next}**")
|
| 697 |
+
else:
|
| 698 |
+
arriving_next = 0
|
| 699 |
+
q = e['incoming_shipments']
|
| 700 |
+
if q: arriving_next = list(q)[0] # Read W+1
|
| 701 |
+
st.write(f"Arriving Next Week: **{arriving_next}**")
|
| 702 |
+
else: # Local Info Mode
|
| 703 |
+
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 704 |
+
e = echelons[human_role] # Distributor
|
| 705 |
+
st.markdown(f"### 👤 **<span style='color:#FF4B4B;'>{human_role} (Your Dashboard - Start of Week State)</span>**", unsafe_allow_html=True)
|
| 706 |
+
col1, col2, col3 = st.columns(3)
|
| 707 |
+
with col1:
|
| 708 |
+
st.metric("Inventory (Opening)", e['inventory'])
|
| 709 |
+
st.metric("Backlog (Opening)", e['backlog'])
|
| 710 |
+
|
| 711 |
+
with col2:
|
| 712 |
+
current_incoming_order = 0
|
| 713 |
+
downstream_name = e['downstream_name'] # Wholesaler
|
| 714 |
+
if downstream_name:
|
| 715 |
+
current_incoming_order = state['last_week_orders'].get(downstream_name, 0)
|
| 716 |
+
st.write(f"**Incoming Order (This Week):**\n# {current_incoming_order}")
|
|
|
|
|
|
|
| 717 |
|
| 718 |
+
with col3:
|
| 719 |
+
arriving_next = 0
|
| 720 |
+
q = e['incoming_shipments']
|
| 721 |
+
if q:
|
| 722 |
+
arriving_next = list(q)[0]
|
| 723 |
+
st.write(f"**Shipment Arriving (Next Week):**\n# {arriving_next}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
|
| 725 |
+
# --- Decision Logic (Remainging the Same) ---
|
| 726 |
+
st.markdown("---")
|
| 727 |
+
st.header("Your Decision (Step 4)")
|
| 728 |
+
|
| 729 |
+
# Prepare state snapshot for the AI prompt (logic remains identical)
|
| 730 |
+
all_decision_point_states = {}
|
| 731 |
+
for name in echelon_order:
|
| 732 |
+
e_curr = echelons[name]
|
| 733 |
+
arrived = 0
|
| 734 |
+
if name == "Factory":
|
| 735 |
+
if state['factory_production_pipeline']: arrived = list(state['factory_production_pipeline'])[0]
|
| 736 |
+
else:
|
| 737 |
+
if e_curr['incoming_shipments']: arrived = list(e_curr['incoming_shipments'])[0]
|
| 738 |
+
|
| 739 |
+
inc_order_this_week = 0
|
| 740 |
+
if name == "Retailer": inc_order_this_week = get_customer_demand(week)
|
| 741 |
+
else:
|
| 742 |
+
ds_name = e_curr['downstream_name']
|
| 743 |
+
if ds_name: inc_order_this_week = state['last_week_orders'].get(ds_name, 0)
|
| 744 |
+
inv_after_arrival = e_curr['inventory'] + arrived
|
| 745 |
+
backlog_after_new_order = e_curr['backlog'] + inc_order_this_week
|
| 746 |
+
|
| 747 |
+
all_decision_point_states[name] = {
|
| 748 |
+
'name': name, 'inventory': inv_after_arrival, 'backlog': backlog_after_new_order,
|
| 749 |
+
'incoming_order': inc_order_this_week,
|
| 750 |
+
'incoming_shipments': e_curr['incoming_shipments'].copy() if name != "Factory" else deque()
|
| 751 |
+
}
|
| 752 |
+
human_echelon_state_for_prompt = all_decision_point_states[human_role]
|
| 753 |
+
|
| 754 |
+
if state['decision_step'] == 'initial_order':
|
| 755 |
+
with st.form(key="initial_order_form"):
|
| 756 |
+
st.markdown("#### **Step 4a:** Based on the dashboard, submit your **initial** order to the Factory.")
|
| 757 |
+
initial_order = st.number_input("Your Initial Order Quantity:", min_value=0, step=1, value=None) # Start blank
|
| 758 |
+
if st.form_submit_button("Submit Initial Order & See AI Suggestion", type="primary"):
|
| 759 |
+
state['human_initial_order'] = int(initial_order) if initial_order is not None else 0
|
| 760 |
+
state['decision_step'] = 'final_order'
|
| 761 |
+
prompt_sugg = get_llm_prompt(human_echelon_state_for_prompt, week, state['llm_personality'], state['info_sharing'], all_decision_point_states)
|
| 762 |
+
ai_suggestion, _ = get_llm_order_decision(prompt_sugg, f"{human_role} (Suggestion)")
|
| 763 |
+
state['current_ai_suggestion'] = ai_suggestion # Store it
|
| 764 |
+
st.rerun()
|
| 765 |
+
elif state['decision_step'] == 'final_order':
|
| 766 |
+
st.success(f"Your initial order was: **{state['human_initial_order']}** units.")
|
| 767 |
+
ai_suggestion = state.get('current_ai_suggestion', 4) # Read stored value
|
| 768 |
+
with st.form(key="final_order_form"):
|
| 769 |
+
st.markdown(f"#### **Step 4b:** The AI suggests ordering **{ai_suggestion}** units.")
|
| 770 |
+
st.markdown("Considering the AI's advice, submit your **final** order to end the week. (This order will arrive in 3 weeks).")
|
| 771 |
+
st.number_input("Your Final Order Quantity:", min_value=0, step=1, key='final_order_input', value=None) # Start blank
|
| 772 |
+
|
| 773 |
+
if st.form_submit_button("Submit Final Order & Advance to Next Week"):
|
| 774 |
+
final_order_value = st.session_state.get('final_order_input', 0)
|
| 775 |
+
final_order_value = int(final_order_value) if final_order_value is not None else 0
|
| 776 |
+
|
| 777 |
+
step_game(final_order_value, state['human_initial_order'], ai_suggestion)
|
| 778 |
+
|
| 779 |
+
if 'final_order_input' in st.session_state: del st.session_state.final_order_input
|
| 780 |
+
st.rerun()
|
| 781 |
+
|
| 782 |
+
st.markdown("---")
|
| 783 |
+
with st.expander("📖 Your Weekly Decision Log", expanded=False):
|
| 784 |
+
if not state.get('logs'):
|
| 785 |
+
st.write("Your weekly history will be displayed here after you complete the first week.")
|
| 786 |
+
else:
|
| 787 |
+
try:
|
| 788 |
+
history_df = pd.json_normalize(state['logs'])
|
| 789 |
+
human_cols = {
|
| 790 |
+
'week': 'Week', f'{human_role}.opening_inventory': 'Opening Inv.',
|
| 791 |
+
f'{human_role}.opening_backlog': 'Opening Backlog',
|
| 792 |
+
f'{human_role}.incoming_order': 'Incoming Order', f'{human_role}.initial_order': 'Your Initial Order',
|
| 793 |
+
f'{human_role}.ai_suggestion': 'AI Suggestion', f'{human_role}.order_placed': 'Your Final Order',
|
| 794 |
+
f'{human_role}.arriving_next_week': 'Arriving Next Week', f'{human_role}.weekly_cost': 'Weekly Cost',
|
| 795 |
+
}
|
| 796 |
+
ordered_display_cols_keys = [
|
| 797 |
+
'week', f'{human_role}.opening_inventory', f'{human_role}.opening_backlog',
|
| 798 |
+
f'{human_role}.incoming_order',
|
| 799 |
+
f'{human_role}.initial_order', f'{human_role}.ai_suggestion', f'{human_role}.order_placed',
|
| 800 |
+
f'{human_role}.arriving_next_week', f'{human_role}.weekly_cost'
|
| 801 |
+
]
|
| 802 |
+
final_cols_to_display = [col for col in ordered_display_cols_keys if col in history_df.columns]
|
| 803 |
+
if not final_cols_to_display:
|
| 804 |
+
st.write("No data columns available to display.")
|
| 805 |
+
else:
|
| 806 |
+
display_df = history_df[final_cols_to_display].rename(columns=human_cols)
|
| 807 |
+
if 'Weekly Cost' in display_df.columns:
|
| 808 |
+
display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}" if isinstance(x, (int, float)) else "")
|
| 809 |
+
st.dataframe(display_df.sort_values(by="Week", ascending=False), hide_index=True, use_container_width=True)
|
| 810 |
+
except Exception as e:
|
| 811 |
+
st.error(f"Error displaying weekly log: {e}")
|
| 812 |
+
|
| 813 |
+
# [修改点 1.2]: 恢复 Game Info 和图片到 st.sidebar
|
| 814 |
+
# 移除 with col_sidebar_image:
|
| 815 |
+
st.sidebar.header("Game Info")
|
| 816 |
+
st.sidebar.markdown(f"**Game ID**: `{state['participant_id']}`\n\n**Current Week**: {week}")
|
| 817 |
+
# [修改点 1.3]: 恢复 st.sidebar.image 但使用 use_column_width=True 保持图片放大
|
| 818 |
+
try: st.sidebar.image(IMAGE_PATH, caption="Supply Chain Reference", use_column_width=True)
|
| 819 |
+
except FileNotFoundError: st.sidebar.warning("Image file not found.")
|
| 820 |
+
|
| 821 |
+
if st.sidebar.button("🔄 Reset Game"):
|
| 822 |
+
if 'final_order_input' in st.session_state: del st.session_state.final_order_input
|
| 823 |
+
if 'current_ai_suggestion' in st.session_state.game_state: del st.session_state.game_state['current_ai_suggestion']
|
| 824 |
+
del st.session_state.game_state
|
| 825 |
+
st.rerun()
|
| 826 |
|
| 827 |
# --- Game Over Interface ---
|
| 828 |
if 'game_state' in st.session_state and not st.session_state.game_state.get('game_running', False) and st.session_state.game_state['week'] > WEEKS:
|
|
|
|
| 832 |
logs_df = pd.json_normalize(state['logs'])
|
| 833 |
fig = plot_results(
|
| 834 |
logs_df,
|
| 835 |
+
# [修改点 2.4]: 移除报告标题中的实验条件提示
|
| 836 |
+
f"Beer Game (Human: {state['human_role']})",
|
| 837 |
state['human_role']
|
| 838 |
)
|
| 839 |
st.pyplot(fig)
|