Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# app.py
|
| 2 |
-
# @title Beer Game Final Version (v4.
|
| 3 |
|
| 4 |
# -----------------------------------------------------------------------------
|
| 5 |
# 1. Import Libraries
|
|
@@ -57,7 +57,7 @@ else:
|
|
| 57 |
|
| 58 |
|
| 59 |
# -----------------------------------------------------------------------------
|
| 60 |
-
# 3. Core Game Logic Functions
|
| 61 |
# -----------------------------------------------------------------------------
|
| 62 |
|
| 63 |
def get_customer_demand(week: int) -> int:
|
|
@@ -87,8 +87,7 @@ def init_game_state(llm_personality: str, info_sharing: str):
|
|
| 87 |
else: shipping_weeks = SHIPPING_DELAY
|
| 88 |
|
| 89 |
st.session_state.game_state['echelons'][name] = {
|
| 90 |
-
'name': name,
|
| 91 |
-
'inventory': INITIAL_INVENTORY, 'backlog': INITIAL_BACKLOG,
|
| 92 |
'incoming_shipments': deque([0] * shipping_weeks, maxlen=shipping_weeks),
|
| 93 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 94 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
|
@@ -302,7 +301,7 @@ def save_logs_and_upload(state: dict):
|
|
| 302 |
except Exception as e_save: st.error(f"Error processing or saving log data: {e_save}")
|
| 303 |
|
| 304 |
# -----------------------------------------------------------------------------
|
| 305 |
-
# 4. Streamlit UI (
|
| 306 |
# -----------------------------------------------------------------------------
|
| 307 |
st.title("🍺 The Beer Game: A Human-AI Collaboration Challenge")
|
| 308 |
|
|
@@ -332,12 +331,37 @@ else:
|
|
| 332 |
st.markdown(f"* **Week 10 (You):** You place an order for **50**.\n* **Week 11 (System):** Your order arrives at the Factory (**{ORDER_PASSING_DELAY}w Order Delay**). Factory AI decides to produce 50.\n* **Week 12 (System):** Factory finishes producing 50 (**{FACTORY_LEAD_TIME}w Production Delay**) & ships it.\n* **Week 13 (System):** The 50 units arrive at your warehouse (**{FACTORY_SHIPPING_DELAY}w Shipping Delay**).\n**Conclusion:** Think 3 weeks ahead! Your order in Week 10 arrives at the start of Week 13.")
|
| 333 |
st.subheader("4. Understanding Inventory & Backlog")
|
| 334 |
st.markdown("Managing your inventory and backlog is key to minimizing costs. Here's how they work:\n* **Effective \"Orders to Fill\":** Each week, the total demand you need to satisfy is your `Incoming Order` for the week PLUS any `Backlog` carried over from the previous week.\n* **If you DON'T have enough inventory:**\n * You ship **all** the inventory you have (after receiving any arrivals for the week).\n * The remaining unfilled \"Orders to Fill\" becomes your **new Backlog** for next week.\n * **Backlog is cumulative!** If you start Week 10 with a backlog of 5, get an order for 8 (total needed = 13), receive 10 units, and ship those 10 units, your new backlog for Week 11 is `13 - 10 = 3`.\n* **If you DO have enough inventory:**\n * You ship all the \"Orders to Fill\".\n * Your Backlog becomes 0.\n * The remaining inventory is carried over to next week (and incurs holding costs).")
|
| 335 |
-
st.subheader("5. The
|
| 336 |
st.markdown("The \"Bullwhip Effect\" happens when small changes in customer demand cause **amplified**, chaotic swings in orders further up the supply chain (like you and the Factory). This often leads to cycles of **panic ordering** (ordering too much when out of stock) followed by **massive inventory pile-ups** (when late orders arrive). This cycle is very expensive. Try to order smoothly.")
|
|
|
|
|
|
|
| 337 |
st.subheader("6. How Each Week Works & Understanding Your Dashboard")
|
| 338 |
-
st.markdown(f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
-
# --- Game Configuration ---
|
| 341 |
st.markdown("---")
|
| 342 |
st.header("⚙️ Game Configuration")
|
| 343 |
c1, c2 = st.columns(2)
|
|
@@ -372,7 +396,7 @@ else:
|
|
| 372 |
# =============== UI CHANGE: Highlight Player ===============
|
| 373 |
if name == human_role:
|
| 374 |
# Use markdown with HTML/CSS for highlighting
|
| 375 |
-
st.markdown(f"##### **<span style='
|
| 376 |
else:
|
| 377 |
st.markdown(f"##### {icon} {name}")
|
| 378 |
# ========================================================
|
|
@@ -382,41 +406,48 @@ else:
|
|
| 382 |
st.metric("Backlog (Opening)", e['backlog'])
|
| 383 |
|
| 384 |
# =============== UI CHANGE: Removed Costs ===============
|
| 385 |
-
#
|
| 386 |
-
# st.metric("Total Cost (Cumulative)", f"${e['total_cost']:,.2f}")
|
| 387 |
-
# last_week_cost = state['logs'][-1][f"{human_role}.weekly_cost"] if week > 1 and state['logs'] else 0
|
| 388 |
-
# st.metric("Cost Last Week", f"${last_week_cost:,.2f}")
|
| 389 |
# =======================================================
|
| 390 |
|
| 391 |
# Display info about THIS week's events / NEXT week's arrivals
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
if name == "Factory":
|
| 394 |
-
# Production completing NEXT week
|
| 395 |
prod_completing_next = list(state['factory_production_pipeline'])[0] if state['factory_production_pipeline'] else 0
|
| 396 |
st.write(f"Completing Next Week: **{prod_completing_next}**")
|
| 397 |
else:
|
| 398 |
-
# Shipment arriving NEXT week
|
| 399 |
arriving_next = list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0
|
| 400 |
st.write(f"Arriving Next Week: **{arriving_next}**")
|
| 401 |
else: # Local Info Mode
|
| 402 |
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 403 |
e = echelons[human_role]
|
| 404 |
-
# =============== UI CHANGE: Highlight Player ===============
|
| 405 |
st.markdown(f"### 👤 **<span style='color:#FF4B4B;'>{human_role} (Your Dashboard - Start of Week State)</span>**", unsafe_allow_html=True) # Highlight self
|
| 406 |
-
# ========================================================
|
| 407 |
|
| 408 |
col1, col2, col3, col4 = st.columns(4)
|
| 409 |
# Display OPENING state
|
| 410 |
col1.metric("Inventory (Opening)", e['inventory'])
|
| 411 |
col2.metric("Backlog (Opening)", e['backlog'])
|
|
|
|
| 412 |
# Display info about THIS week's events / NEXT week's arrivals
|
| 413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
col4.write(f"**Shipment Arriving (Next Week):**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 415 |
|
| 416 |
# =============== UI CHANGE: Removed Costs ===============
|
| 417 |
-
#
|
| 418 |
-
# last_week_cost = state['logs'][-1][f"{human_role}.weekly_cost"] if week > 1 and state['logs'] else 0
|
| 419 |
-
# st.metric("Cost Last Week", f"${last_week_cost:,.2f}")
|
| 420 |
# =======================================================
|
| 421 |
|
| 422 |
|
|
@@ -436,13 +467,18 @@ else:
|
|
| 436 |
|
| 437 |
# Calculate the state AFTER arrivals and incoming orders for the prompt
|
| 438 |
inv_after_arrival = e_curr['inventory'] + arrived
|
| 439 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
|
| 441 |
all_decision_point_states[name] = {
|
| 442 |
-
'name': name,
|
| 443 |
-
'
|
| 444 |
-
'backlog': backlog_after_new_order, # State for decision making
|
| 445 |
-
'incoming_order': e_curr['incoming_order'], # Info for decision making
|
| 446 |
'incoming_shipments': e_curr['incoming_shipments'].copy() if name != "Factory" else deque()
|
| 447 |
}
|
| 448 |
human_echelon_state_for_prompt = all_decision_point_states[human_role]
|
|
@@ -451,11 +487,12 @@ else:
|
|
| 451 |
if state['decision_step'] == 'initial_order':
|
| 452 |
with st.form(key="initial_order_form"):
|
| 453 |
st.markdown("#### **Step 4a:** Based on the dashboard, submit your **initial** order to the Factory.")
|
| 454 |
-
#
|
| 455 |
-
|
| 456 |
-
|
| 457 |
if st.form_submit_button("Submit Initial Order & See AI Suggestion", type="primary"):
|
| 458 |
-
|
|
|
|
| 459 |
state['decision_step'] = 'final_order'
|
| 460 |
st.rerun()
|
| 461 |
|
|
@@ -465,16 +502,23 @@ else:
|
|
| 465 |
prompt_sugg = get_llm_prompt(human_echelon_state_for_prompt, week, state['llm_personality'], state['info_sharing'], all_decision_point_states)
|
| 466 |
ai_suggestion, _ = get_llm_order_decision(prompt_sugg, f"{human_role} (Suggestion)")
|
| 467 |
|
| 468 |
-
|
| 469 |
-
|
|
|
|
| 470 |
|
| 471 |
with st.form(key="final_order_form"):
|
| 472 |
st.markdown(f"#### **Step 4b:** The AI suggests ordering **{ai_suggestion}** units.")
|
| 473 |
st.markdown("Considering the AI's advice, submit your **final** order to end the week. (This order will arrive in 3 weeks).")
|
| 474 |
-
|
|
|
|
|
|
|
| 475 |
if st.form_submit_button("Submit Final Order & Advance to Next Week"):
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
| 477 |
step_game(final_order_value, state['human_initial_order'], ai_suggestion)
|
|
|
|
| 478 |
if 'final_order_input' in st.session_state: del st.session_state.final_order_input
|
| 479 |
st.rerun()
|
| 480 |
|
|
@@ -505,7 +549,6 @@ else:
|
|
| 505 |
else:
|
| 506 |
display_df = history_df[final_cols_to_display].rename(columns=human_cols)
|
| 507 |
if 'Weekly Cost' in display_df.columns:
|
| 508 |
-
# Safely apply formatting, handling potential non-numeric data
|
| 509 |
display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}" if isinstance(x, (int, float)) else "")
|
| 510 |
st.dataframe(display_df.sort_values(by="Week", ascending=False), hide_index=True, use_container_width=True)
|
| 511 |
except Exception as e:
|
|
|
|
| 1 |
# app.py
|
| 2 |
+
# @title Beer Game Final Version (v4.20 - Fixed UI Incoming Order Timing & Input Defaults)
|
| 3 |
|
| 4 |
# -----------------------------------------------------------------------------
|
| 5 |
# 1. Import Libraries
|
|
|
|
| 57 |
|
| 58 |
|
| 59 |
# -----------------------------------------------------------------------------
|
| 60 |
+
# 3. Core Game Logic Functions
|
| 61 |
# -----------------------------------------------------------------------------
|
| 62 |
|
| 63 |
def get_customer_demand(week: int) -> int:
|
|
|
|
| 87 |
else: shipping_weeks = SHIPPING_DELAY
|
| 88 |
|
| 89 |
st.session_state.game_state['echelons'][name] = {
|
| 90 |
+
'name': name, 'inventory': INITIAL_INVENTORY, 'backlog': INITIAL_BACKLOG,
|
|
|
|
| 91 |
'incoming_shipments': deque([0] * shipping_weeks, maxlen=shipping_weeks),
|
| 92 |
'incoming_order': 0, 'order_placed': 0, 'shipment_sent': 0,
|
| 93 |
'weekly_cost': 0, 'total_cost': 0, 'upstream_name': upstream, 'downstream_name': downstream,
|
|
|
|
| 301 |
except Exception as e_save: st.error(f"Error processing or saving log data: {e_save}")
|
| 302 |
|
| 303 |
# -----------------------------------------------------------------------------
|
| 304 |
+
# 4. Streamlit UI (Adjusted Dashboard Labels & Logic)
|
| 305 |
# -----------------------------------------------------------------------------
|
| 306 |
st.title("🍺 The Beer Game: A Human-AI Collaboration Challenge")
|
| 307 |
|
|
|
|
| 331 |
st.markdown(f"* **Week 10 (You):** You place an order for **50**.\n* **Week 11 (System):** Your order arrives at the Factory (**{ORDER_PASSING_DELAY}w Order Delay**). Factory AI decides to produce 50.\n* **Week 12 (System):** Factory finishes producing 50 (**{FACTORY_LEAD_TIME}w Production Delay**) & ships it.\n* **Week 13 (System):** The 50 units arrive at your warehouse (**{FACTORY_SHIPPING_DELAY}w Shipping Delay**).\n**Conclusion:** Think 3 weeks ahead! Your order in Week 10 arrives at the start of Week 13.")
|
| 332 |
st.subheader("4. Understanding Inventory & Backlog")
|
| 333 |
st.markdown("Managing your inventory and backlog is key to minimizing costs. Here's how they work:\n* **Effective \"Orders to Fill\":** Each week, the total demand you need to satisfy is your `Incoming Order` for the week PLUS any `Backlog` carried over from the previous week.\n* **If you DON'T have enough inventory:**\n * You ship **all** the inventory you have (after receiving any arrivals for the week).\n * The remaining unfilled \"Orders to Fill\" becomes your **new Backlog** for next week.\n * **Backlog is cumulative!** If you start Week 10 with a backlog of 5, get an order for 8 (total needed = 13), receive 10 units, and ship those 10 units, your new backlog for Week 11 is `13 - 10 = 3`.\n* **If you DO have enough inventory:**\n * You ship all the \"Orders to Fill\".\n * Your Backlog becomes 0.\n * The remaining inventory is carried over to next week (and incurs holding costs).")
|
| 334 |
+
st.subheader("5. The Bullwhisp Effect (What to Avoid)")
|
| 335 |
st.markdown("The \"Bullwhip Effect\" happens when small changes in customer demand cause **amplified**, chaotic swings in orders further up the supply chain (like you and the Factory). This often leads to cycles of **panic ordering** (ordering too much when out of stock) followed by **massive inventory pile-ups** (when late orders arrive). This cycle is very expensive. Try to order smoothly.")
|
| 336 |
+
|
| 337 |
+
# =============== UPDATED: How Each Week Works & Dashboard Explanation ===============
|
| 338 |
st.subheader("6. How Each Week Works & Understanding Your Dashboard")
|
| 339 |
+
st.markdown(f"""
|
| 340 |
+
Your main job is simple: place one order each week based on the dashboard presented to you.
|
| 341 |
+
|
| 342 |
+
**A) At the start of every week, BEFORE your turn:**
|
| 343 |
+
* **(Step 1) Shipments Arrive:** Beer you ordered {ORDER_PASSING_DELAY + FACTORY_LEAD_TIME + FACTORY_SHIPPING_DELAY} weeks ago arrives.
|
| 344 |
+
* **(Step 2) New Orders Arrive:** You receive a new order from the Wholesaler (this is their order placed *last* week, due to the {ORDER_PASSING_DELAY} week delay).
|
| 345 |
+
* **(Step 3) You Ship Beer (Automatically):** The system ships beer *immediately* based on your inventory *after* Step 1 and the total demand *after* Step 2.
|
| 346 |
+
|
| 347 |
+
**B) Your Dashboard (What You See for Your Turn):**
|
| 348 |
+
The dashboard shows your status **at the start of the week, BEFORE Steps 1, 2, and 3 happen**:
|
| 349 |
+
* `Inventory (Opening)`: Your stock **at the beginning of the week**.
|
| 350 |
+
* `Backlog (Opening)`: Unfilled orders **carried over from the end of last week**.
|
| 351 |
+
* `Incoming Order (This Week)`: The specific order quantity arriving from the Wholesaler *during* this week (Step 2). **This is the primary demand signal you need to react to.**
|
| 352 |
+
* `Arriving Next Week`: The quantity scheduled to arrive from the Factory at the start of the **next week**.
|
| 353 |
+
* `Your Total Cumulative Cost`: Sum of all weekly costs up to the **end of last week** (Removed from dashboard).
|
| 354 |
+
* `Cost Last Week`: The specific cost incurred just **last week** (Removed from dashboard).
|
| 355 |
+
|
| 356 |
+
**C) Your Decision (Step 4 - Two Parts):**
|
| 357 |
+
Now, looking at the dashboard (showing the start-of-week state) and considering the incoming order and future arrivals, you decide how much to order:
|
| 358 |
+
* **(Step 4a - Initial Order):** Submit your first estimate. Input box starts blank.
|
| 359 |
+
* **(Step 4b - Final Order):** See the AI's suggestion, then submit your final decision. Input box starts blank (or with AI suggestion if preferred - currently blank). This order will arrive in 3 weeks.
|
| 360 |
+
|
| 361 |
+
Submitting your final order ends the week. The system then calculates your `Weekly Cost` based on your inventory/backlog *after* Step 3 shipping, logs everything, and advances to the next week.
|
| 362 |
+
""")
|
| 363 |
+
# ==============================================================================
|
| 364 |
|
|
|
|
| 365 |
st.markdown("---")
|
| 366 |
st.header("⚙️ Game Configuration")
|
| 367 |
c1, c2 = st.columns(2)
|
|
|
|
| 396 |
# =============== UI CHANGE: Highlight Player ===============
|
| 397 |
if name == human_role:
|
| 398 |
# Use markdown with HTML/CSS for highlighting
|
| 399 |
+
st.markdown(f"##### **<span style='border: 1px solid #FF4B4B; padding: 2px 5px; border-radius: 3px;'>{icon} {name} (You)</span>**", unsafe_allow_html=True)
|
| 400 |
else:
|
| 401 |
st.markdown(f"##### {icon} {name}")
|
| 402 |
# ========================================================
|
|
|
|
| 406 |
st.metric("Backlog (Opening)", e['backlog'])
|
| 407 |
|
| 408 |
# =============== UI CHANGE: Removed Costs ===============
|
| 409 |
+
# Costs are no longer displayed on the main dashboard
|
|
|
|
|
|
|
|
|
|
| 410 |
# =======================================================
|
| 411 |
|
| 412 |
# Display info about THIS week's events / NEXT week's arrivals
|
| 413 |
+
# Calculate the INCOMING order for THIS week
|
| 414 |
+
current_incoming_order = 0
|
| 415 |
+
if name == "Retailer":
|
| 416 |
+
current_incoming_order = get_customer_demand(week)
|
| 417 |
+
else:
|
| 418 |
+
downstream_name = e['downstream_name']
|
| 419 |
+
if downstream_name:
|
| 420 |
+
current_incoming_order = state['last_week_orders'].get(downstream_name, 0)
|
| 421 |
+
st.write(f"Incoming Order (This Week): **{current_incoming_order}**") # Display calculated order
|
| 422 |
+
|
| 423 |
+
# Display prediction for NEXT week's arrivals
|
| 424 |
if name == "Factory":
|
|
|
|
| 425 |
prod_completing_next = list(state['factory_production_pipeline'])[0] if state['factory_production_pipeline'] else 0
|
| 426 |
st.write(f"Completing Next Week: **{prod_completing_next}**")
|
| 427 |
else:
|
|
|
|
| 428 |
arriving_next = list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0
|
| 429 |
st.write(f"Arriving Next Week: **{arriving_next}**")
|
| 430 |
else: # Local Info Mode
|
| 431 |
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 432 |
e = echelons[human_role]
|
|
|
|
| 433 |
st.markdown(f"### 👤 **<span style='color:#FF4B4B;'>{human_role} (Your Dashboard - Start of Week State)</span>**", unsafe_allow_html=True) # Highlight self
|
|
|
|
| 434 |
|
| 435 |
col1, col2, col3, col4 = st.columns(4)
|
| 436 |
# Display OPENING state
|
| 437 |
col1.metric("Inventory (Opening)", e['inventory'])
|
| 438 |
col2.metric("Backlog (Opening)", e['backlog'])
|
| 439 |
+
|
| 440 |
# Display info about THIS week's events / NEXT week's arrivals
|
| 441 |
+
# Calculate the INCOMING order for THIS week
|
| 442 |
+
current_incoming_order = 0
|
| 443 |
+
downstream_name = e['downstream_name'] # Wholesaler
|
| 444 |
+
if downstream_name:
|
| 445 |
+
current_incoming_order = state['last_week_orders'].get(downstream_name, 0)
|
| 446 |
+
col3.write(f"**Incoming Order (This Week):**\n# {current_incoming_order}") # Display calculated order
|
| 447 |
col4.write(f"**Shipment Arriving (Next Week):**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 448 |
|
| 449 |
# =============== UI CHANGE: Removed Costs ===============
|
| 450 |
+
# Costs are no longer displayed on the main dashboard
|
|
|
|
|
|
|
| 451 |
# =======================================================
|
| 452 |
|
| 453 |
|
|
|
|
| 467 |
|
| 468 |
# Calculate the state AFTER arrivals and incoming orders for the prompt
|
| 469 |
inv_after_arrival = e_curr['inventory'] + arrived
|
| 470 |
+
# Determine incoming order for *this* week again for prompt state
|
| 471 |
+
inc_order_this_week = 0
|
| 472 |
+
if name == "Retailer": inc_order_this_week = get_customer_demand(week)
|
| 473 |
+
else:
|
| 474 |
+
ds_name = e_curr['downstream_name']
|
| 475 |
+
if ds_name: inc_order_this_week = state['last_week_orders'].get(ds_name, 0)
|
| 476 |
+
|
| 477 |
+
backlog_after_new_order = e_curr['backlog'] + inc_order_this_week
|
| 478 |
|
| 479 |
all_decision_point_states[name] = {
|
| 480 |
+
'name': name, 'inventory': inv_after_arrival, 'backlog': backlog_after_new_order,
|
| 481 |
+
'incoming_order': inc_order_this_week, # Use the correctly calculated incoming order
|
|
|
|
|
|
|
| 482 |
'incoming_shipments': e_curr['incoming_shipments'].copy() if name != "Factory" else deque()
|
| 483 |
}
|
| 484 |
human_echelon_state_for_prompt = all_decision_point_states[human_role]
|
|
|
|
| 487 |
if state['decision_step'] == 'initial_order':
|
| 488 |
with st.form(key="initial_order_form"):
|
| 489 |
st.markdown("#### **Step 4a:** Based on the dashboard, submit your **initial** order to the Factory.")
|
| 490 |
+
# =============== UI CHANGE: Removed Default Value ===============
|
| 491 |
+
initial_order = st.number_input("Your Initial Order Quantity:", min_value=0, step=1) # No 'value' argument
|
| 492 |
+
# ===============================================================
|
| 493 |
if st.form_submit_button("Submit Initial Order & See AI Suggestion", type="primary"):
|
| 494 |
+
# Handle case where user leaves it blank (input returns None)
|
| 495 |
+
state['human_initial_order'] = int(initial_order) if initial_order is not None else 0
|
| 496 |
state['decision_step'] = 'final_order'
|
| 497 |
st.rerun()
|
| 498 |
|
|
|
|
| 502 |
prompt_sugg = get_llm_prompt(human_echelon_state_for_prompt, week, state['llm_personality'], state['info_sharing'], all_decision_point_states)
|
| 503 |
ai_suggestion, _ = get_llm_order_decision(prompt_sugg, f"{human_role} (Suggestion)")
|
| 504 |
|
| 505 |
+
# We don't pre-fill the final_order_input anymore
|
| 506 |
+
# if 'final_order_input' not in st.session_state:
|
| 507 |
+
# st.session_state.final_order_input = ai_suggestion
|
| 508 |
|
| 509 |
with st.form(key="final_order_form"):
|
| 510 |
st.markdown(f"#### **Step 4b:** The AI suggests ordering **{ai_suggestion}** units.")
|
| 511 |
st.markdown("Considering the AI's advice, submit your **final** order to end the week. (This order will arrive in 3 weeks).")
|
| 512 |
+
# =============== UI CHANGE: Removed Default Value ===============
|
| 513 |
+
st.number_input("Your Final Order Quantity:", min_value=0, step=1, key='final_order_input') # No 'value' argument
|
| 514 |
+
# ===============================================================
|
| 515 |
if st.form_submit_button("Submit Final Order & Advance to Next Week"):
|
| 516 |
+
# Handle case where user leaves it blank
|
| 517 |
+
final_order_value = st.session_state.get('final_order_input', 0) # Use .get with default
|
| 518 |
+
final_order_value = int(final_order_value) if final_order_value is not None else 0
|
| 519 |
+
|
| 520 |
step_game(final_order_value, state['human_initial_order'], ai_suggestion)
|
| 521 |
+
# Clean up session state for the input key
|
| 522 |
if 'final_order_input' in st.session_state: del st.session_state.final_order_input
|
| 523 |
st.rerun()
|
| 524 |
|
|
|
|
| 549 |
else:
|
| 550 |
display_df = history_df[final_cols_to_display].rename(columns=human_cols)
|
| 551 |
if 'Weekly Cost' in display_df.columns:
|
|
|
|
| 552 |
display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}" if isinstance(x, (int, float)) else "")
|
| 553 |
st.dataframe(display_df.sort_values(by="Week", ascending=False), hide_index=True, use_container_width=True)
|
| 554 |
except Exception as e:
|