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
|
|
@@ -64,14 +64,8 @@ def get_customer_demand(week: int) -> int:
|
|
| 64 |
return 4 if week <= 4 else 8
|
| 65 |
|
| 66 |
def init_game_state(llm_personality: str, info_sharing: str):
|
| 67 |
-
"""Initializes or resets the game state in st.session_state."""
|
| 68 |
roles = ["Retailer", "Wholesaler", "Distributor", "Factory"]
|
| 69 |
-
|
| 70 |
-
# =============== CHANGE 1: Fixed Role ===============
|
| 71 |
-
# Human role is no longer random. Everyone is Distributor.
|
| 72 |
-
human_role = "Distributor"
|
| 73 |
-
# ======================================================
|
| 74 |
-
|
| 75 |
participant_id = str(uuid.uuid4())[:8]
|
| 76 |
|
| 77 |
st.session_state.game_state = {
|
|
@@ -87,7 +81,6 @@ def init_game_state(llm_personality: str, info_sharing: str):
|
|
| 87 |
upstream = roles[i + 1] if i + 1 < len(roles) else None
|
| 88 |
downstream = roles[i - 1] if i - 1 >= 0 else None
|
| 89 |
|
| 90 |
-
# This logic remains correct: Factory has 0 shipping weeks for incoming.
|
| 91 |
if name == "Distributor": shipping_weeks = FACTORY_SHIPPING_DELAY
|
| 92 |
elif name == "Factory": shipping_weeks = 0
|
| 93 |
else: shipping_weeks = SHIPPING_DELAY
|
|
@@ -109,7 +102,7 @@ def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
|
| 109 |
response = client.chat.completions.create(
|
| 110 |
model=OPENAI_MODEL,
|
| 111 |
messages=[
|
| 112 |
-
{"role": "system", "content": "You are a supply chain manager playing the Beer Game. Your response must be only an integer number representing your order quantity and nothing else. For example: 8"},
|
| 113 |
{"role": "user", "content": prompt}
|
| 114 |
],
|
| 115 |
temperature=temp, max_tokens=10
|
|
@@ -122,36 +115,90 @@ def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
|
| 122 |
st.error(f"API call failed for {echelon_name}: {e}")
|
| 123 |
return 8, f"API_ERROR: {e}"
|
| 124 |
|
|
|
|
| 125 |
def get_llm_prompt(echelon_state: dict, week: int, llm_personality: str, info_sharing: str, all_echelons_state: dict) -> str:
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
if llm_personality == 'perfect_rational' and info_sharing == 'full':
|
| 129 |
-
stable_demand = 8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
target_inventory_level = (stable_demand * total_lead_time) + safety_stock
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
optimal_order = max(0, int(target_inventory_level - inventory_position))
|
| 133 |
-
|
|
|
|
|
|
|
| 134 |
elif llm_personality == 'perfect_rational' and info_sharing == 'local':
|
| 135 |
safety_stock = 4; anchor_demand = echelon_state['incoming_order']
|
| 136 |
inventory_correction = safety_stock - (echelon_state['inventory'] - echelon_state['backlog'])
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
calculated_order = anchor_demand + inventory_correction - supply_line
|
| 139 |
rational_local_order = max(0, int(calculated_order))
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
| 141 |
elif llm_personality == 'human_like' and info_sharing == 'full':
|
| 142 |
full_info_str = f"\n**Full Supply Chain Information:**\n- End-Customer Demand this week: {get_customer_demand(week)} units.\n"
|
| 143 |
for name, e_state in all_echelons_state.items():
|
| 144 |
if name != echelon_state['name']: full_info_str += f"- {name}: Inventory={e_state['inventory']}, Backlog={e_state['backlog']}\n"
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
elif llm_personality == 'human_like' and info_sharing == 'local':
|
| 147 |
-
return f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
def step_game(human_final_order: int, human_initial_order: int, ai_suggestion: int):
|
| 150 |
-
# This
|
| 151 |
-
# Factory inventory is correctly handled by 'factory_production_pipeline'.
|
| 152 |
-
# Other echelons are correctly handled by 'incoming_shipments'.
|
| 153 |
-
# No changes needed here.
|
| 154 |
-
|
| 155 |
state = st.session_state.game_state
|
| 156 |
week, echelons, human_role = state['week'], state['echelons'], state['human_role']
|
| 157 |
llm_personality, info_sharing = state['llm_personality'], state['info_sharing']
|
|
@@ -288,33 +335,34 @@ else:
|
|
| 288 |
st.image(IMAGE_PATH, caption="Orders flow upstream (right), beer flows downstream (left).")
|
| 289 |
except FileNotFoundError:
|
| 290 |
st.warning("Image file not found. Please ensure 'beer_game_diagram.png' is uploaded to the repository.")
|
| 291 |
-
|
| 292 |
st.markdown("""
|
| 293 |
You will play the role of the **Distributor**. The other three roles (Retailer, Wholesaler, Factory) will be controlled by AI agents.
|
| 294 |
-
- **Retailer:** Fulfills end-customer demand. Orders from the Wholesaler.
|
| 295 |
-
- **Wholesaler:** Fulfills Retailer orders. Orders from the Distributor.
|
| 296 |
- **Distributor (You):** Fulfills Wholesaler orders. Orders from the Factory.
|
| 297 |
-
- **Factory:** Fulfills your orders. Produces new beer.
|
| 298 |
""")
|
|
|
|
| 299 |
with col2:
|
| 300 |
st.subheader("⏳ The Challenge: Delays!")
|
| 301 |
-
st.markdown("""
|
| 302 |
-
The key challenge is managing the **delays**
|
| 303 |
|
| 304 |
-
* **Order
|
| 305 |
-
* **
|
| 306 |
-
* **
|
| 307 |
|
| 308 |
-
This means there is a **
|
| 309 |
""")
|
| 310 |
|
| 311 |
st.subheader("🎮 How Each Week Works")
|
| 312 |
-
st.markdown("""
|
| 313 |
Your main job is to place an order each week. The system handles the rest.
|
| 314 |
|
| 315 |
**1. Automated Steps (Start of Week)**
|
| 316 |
When a new week begins, the system first automates three steps based on past decisions:
|
| 317 |
-
* **(Step 1: Shipments Arrive):** Shipments from the Factory (which you ordered weeks ago) arrive and are added to your inventory.
|
| 318 |
* **(Step 2: New Orders Arrive):** You receive a new incoming order from the Wholesaler.
|
| 319 |
* **(Step 3: Ship Beer):** The system automatically ships as much beer as you have in stock to fulfill the Wholesaler's order, plus any leftover backlog. Any unfulfilled amount becomes your new backlog for next week.
|
| 320 |
|
|
@@ -356,14 +404,12 @@ else:
|
|
| 356 |
st.metric("Inventory", e['inventory']); st.metric("Backlog", e['backlog'])
|
| 357 |
st.write(f"Incoming Order: **{e['incoming_order']}**")
|
| 358 |
|
| 359 |
-
# =============== CHANGE 2: Fixed Factory UI Logic ===============
|
| 360 |
if name == "Factory":
|
| 361 |
prod_completing = list(state['factory_production_pipeline'])[0] if state['factory_production_pipeline'] else 0
|
| 362 |
st.write(f"Production Completing: **{prod_completing}**")
|
| 363 |
else:
|
| 364 |
arriving = list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0
|
| 365 |
st.write(f"Arriving Next Week: **{arriving}**")
|
| 366 |
-
# ==============================================================
|
| 367 |
else:
|
| 368 |
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 369 |
e = echelons[human_role] # This is always the Distributor
|
|
@@ -372,7 +418,6 @@ else:
|
|
| 372 |
col1.metric("Current Inventory", e['inventory'])
|
| 373 |
col2.metric("Current Backlog", e['backlog'])
|
| 374 |
col3.write(f"**Incoming Order (This Week):**\n# {e['incoming_order']}")
|
| 375 |
-
# This is correct for the Distributor
|
| 376 |
col4.write(f"**Shipment Arriving (Next Week):**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 377 |
|
| 378 |
st.markdown("---")
|
|
@@ -443,13 +488,13 @@ else:
|
|
| 443 |
state = st.session_state.game_state
|
| 444 |
logs_df = pd.json_normalize(state['logs'])
|
| 445 |
|
| 446 |
-
# ===============
|
| 447 |
fig = plot_results(
|
| 448 |
logs_df,
|
| 449 |
f"Beer Game (Human: {state['human_role']})\n(AI: {state['llm_personality']} | Info: {state['info_sharing']})",
|
| 450 |
state['human_role']
|
| 451 |
)
|
| 452 |
-
#
|
| 453 |
|
| 454 |
st.pyplot(fig)
|
| 455 |
save_logs_and_upload(state)
|
|
|
|
| 1 |
# app.py
|
| 2 |
+
# @title Beer Game Final Version (v4.8 - Unified Human-Like Prompts)
|
| 3 |
|
| 4 |
# -----------------------------------------------------------------------------
|
| 5 |
# 1. Import Libraries
|
|
|
|
| 64 |
return 4 if week <= 4 else 8
|
| 65 |
|
| 66 |
def init_game_state(llm_personality: str, info_sharing: str):
|
|
|
|
| 67 |
roles = ["Retailer", "Wholesaler", "Distributor", "Factory"]
|
| 68 |
+
human_role = "Distributor" # Role is fixed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
participant_id = str(uuid.uuid4())[:8]
|
| 70 |
|
| 71 |
st.session_state.game_state = {
|
|
|
|
| 81 |
upstream = roles[i + 1] if i + 1 < len(roles) else None
|
| 82 |
downstream = roles[i - 1] if i - 1 >= 0 else None
|
| 83 |
|
|
|
|
| 84 |
if name == "Distributor": shipping_weeks = FACTORY_SHIPPING_DELAY
|
| 85 |
elif name == "Factory": shipping_weeks = 0
|
| 86 |
else: shipping_weeks = SHIPPING_DELAY
|
|
|
|
| 102 |
response = client.chat.completions.create(
|
| 103 |
model=OPENAI_MODEL,
|
| 104 |
messages=[
|
| 105 |
+
{"role": "system", "content": "You are a supply chain manager playing the Beer Game. Your response must be only an integer number representing your order or production quantity and nothing else. For example: 8"},
|
| 106 |
{"role": "user", "content": prompt}
|
| 107 |
],
|
| 108 |
temperature=temp, max_tokens=10
|
|
|
|
| 115 |
st.error(f"API call failed for {echelon_name}: {e}")
|
| 116 |
return 8, f"API_ERROR: {e}"
|
| 117 |
|
| 118 |
+
# =============== MODIFIED FUNCTION (Unified Prompts) ===============
|
| 119 |
def get_llm_prompt(echelon_state: dict, week: int, llm_personality: str, info_sharing: str, all_echelons_state: dict) -> str:
|
| 120 |
+
"""Generates the prompt for the LLM based on the game scenario."""
|
| 121 |
+
|
| 122 |
+
base_info = f"Your Current Status at the **{echelon_state['name']}** for **Week {week}**:\n- On-hand inventory: {echelon_state['inventory']} units.\n- Backlog (unfilled orders): {echelon_state['backlog']} units.\n- Incoming order this week (from your customer): {echelon_state['incoming_order']} units.\n"
|
| 123 |
+
|
| 124 |
+
# Define task word and role-specific info
|
| 125 |
+
if echelon_state['name'] == 'Factory':
|
| 126 |
+
task_word = "production quantity"
|
| 127 |
+
base_info += f"- Production pipeline (completing in future weeks): {list(st.session_state.game_state['factory_production_pipeline'])}"
|
| 128 |
+
else:
|
| 129 |
+
task_word = "order quantity"
|
| 130 |
+
base_info += f"- Shipments on the way to you: {list(echelon_state['incoming_shipments'])}\n- Orders you have placed being processed by your supplier: {list(echelon_state['order_pipeline'])}"
|
| 131 |
+
|
| 132 |
+
# --- Perfect Rational (Unchanged from last version, logic is correct) ---
|
| 133 |
if llm_personality == 'perfect_rational' and info_sharing == 'full':
|
| 134 |
+
stable_demand = 8
|
| 135 |
+
if echelon_state['name'] == 'Factory':
|
| 136 |
+
total_lead_time = FACTORY_LEAD_TIME
|
| 137 |
+
elif echelon_state['name'] == 'Distributor':
|
| 138 |
+
total_lead_time = ORDER_PASSING_DELAY + FACTORY_LEAD_TIME + FACTORY_SHIPPING_DELAY
|
| 139 |
+
else:
|
| 140 |
+
total_lead_time = ORDER_PASSING_DELAY + SHIPPING_DELAY
|
| 141 |
+
|
| 142 |
+
safety_stock = 4
|
| 143 |
target_inventory_level = (stable_demand * total_lead_time) + safety_stock
|
| 144 |
+
|
| 145 |
+
if echelon_state['name'] == 'Factory':
|
| 146 |
+
inv_pos_components = f"(Inv: {echelon_state['inventory']} - Backlog: {echelon_state['backlog']} + In_Production: {sum(st.session_state.game_state['factory_production_pipeline'])})"
|
| 147 |
+
inventory_position = (echelon_state['inventory'] - echelon_state['backlog'] + sum(st.session_state.game_state['factory_production_pipeline']))
|
| 148 |
+
else:
|
| 149 |
+
inv_pos_components = f"(Inv: {echelon_state['inventory']} - Backlog: {echelon_state['backlog']} + In_Transit: {sum(echelon_state['incoming_shipments'])} + In_Pipeline: {sum(echelon_state['order_pipeline'])})"
|
| 150 |
+
inventory_position = (echelon_state['inventory'] - echelon_state['backlog'] + sum(echelon_state['incoming_shipments']) + sum(echelon_state['order_pipeline']))
|
| 151 |
+
|
| 152 |
optimal_order = max(0, int(target_inventory_level - inventory_position))
|
| 153 |
+
|
| 154 |
+
return f"**You are a perfectly rational supply chain AI with full system visibility.**\nYour only goal is to maintain stability and minimize costs based on mathematical optimization.\n**System Analysis:**\n* **Known Stable End-Customer Demand:** {stable_demand} units/week.\n* **Your Current Total Inventory Position:** {inventory_position} units. {inv_pos_components}\n* **Optimal Target Inventory Level:** {target_inventory_level} units (Target for {total_lead_time} weeks lead time).\n* **Mathematically Optimal {task_word.title()}:** The optimal decision is **{optimal_order} units**.\n**Your Task:** Confirm this optimal {task_word}. Respond with a single integer."
|
| 155 |
+
|
| 156 |
elif llm_personality == 'perfect_rational' and info_sharing == 'local':
|
| 157 |
safety_stock = 4; anchor_demand = echelon_state['incoming_order']
|
| 158 |
inventory_correction = safety_stock - (echelon_state['inventory'] - echelon_state['backlog'])
|
| 159 |
+
|
| 160 |
+
if echelon_state['name'] == 'Factory':
|
| 161 |
+
supply_line = sum(st.session_state.game_state['factory_production_pipeline'])
|
| 162 |
+
supply_line_desc = "In Production"
|
| 163 |
+
else:
|
| 164 |
+
supply_line = sum(echelon_state['incoming_shipments']) + sum(echelon_state['order_pipeline'])
|
| 165 |
+
supply_line_desc = "Supply Line (In Transit + In Pipeline)"
|
| 166 |
+
|
| 167 |
calculated_order = anchor_demand + inventory_correction - supply_line
|
| 168 |
rational_local_order = max(0, int(calculated_order))
|
| 169 |
+
|
| 170 |
+
return f"**You are a perfectly rational supply chain AI with ONLY LOCAL information.**\nYou must use a logical heuristic to make a stable decision. A proven method is \"Anchoring and Adjustment\".\n\n{base_info}\n\n**Rational Calculation (Anchoring & Adjustment):**\n1. **Anchor on Demand:** Your best guess for future demand is your last incoming order: **{anchor_demand} units**.\n2. **Adjust for Inventory:** You want to hold a safety stock of {safety_stock} units. Your current stock is {echelon_state['inventory'] - echelon_state['backlog']}. You need to order an extra **{inventory_correction} units** to correct this.\n3. **Account for {supply_line_desc}:** You already have **{supply_line} units** being processed. These should be subtracted from your new order.\n\n**Final Calculation:**\n* Decision = (Anchor Demand) + (Inventory Adjustment) - ({supply_line_desc})\n* Decision = {anchor_demand} + {inventory_correction} - {supply_line} = **{rational_local_order} units**.\n**Your Task:** Confirm this locally rational {task_word}. Respond with a single integer."
|
| 171 |
+
|
| 172 |
+
# --- Human-like (Unified Prompts as requested) ---
|
| 173 |
elif llm_personality == 'human_like' and info_sharing == 'full':
|
| 174 |
full_info_str = f"\n**Full Supply Chain Information:**\n- End-Customer Demand this week: {get_customer_demand(week)} units.\n"
|
| 175 |
for name, e_state in all_echelons_state.items():
|
| 176 |
if name != echelon_state['name']: full_info_str += f"- {name}: Inventory={e_state['inventory']}, Backlog={e_state['backlog']}\n"
|
| 177 |
+
|
| 178 |
+
return f"""
|
| 179 |
+
**You are a supply chain manager ({echelon_state['name']}) with full system visibility.**
|
| 180 |
+
You can see everyone's inventory and the real customer demand.
|
| 181 |
+
{base_info}
|
| 182 |
+
{full_info_str}
|
| 183 |
+
**Your Task:** Your primary responsibility is to meet the demand from your direct customer (your `Incoming order this week`: **{echelon_state['incoming_order']}** units).
|
| 184 |
+
While you can see the stable end-customer demand ({get_customer_demand(week)} units), your priority is to fulfill the order you just received to avoid a backlog.
|
| 185 |
+
You are still human and might get anxious about your own stock levels.
|
| 186 |
+
What {task_word} should you decide on this week? Respond with a single integer.
|
| 187 |
+
"""
|
| 188 |
+
|
| 189 |
elif llm_personality == 'human_like' and info_sharing == 'local':
|
| 190 |
+
return f"""
|
| 191 |
+
**You are a reactive supply chain manager for the {echelon_state['name']}.** You have a limited view and tend to over-correct based on fear.
|
| 192 |
+
Your top priority is to NOT have a backlog.
|
| 193 |
+
{base_info}
|
| 194 |
+
**Your Task:** You just received an incoming order for **{echelon_state['incoming_order']}** units.
|
| 195 |
+
Your gut instinct is to panic and {task_word.split(' ')[0]} enough to ensure you are never caught with a backlog again.
|
| 196 |
+
**React emotionally.** What is your knee-jerk {task_word}? Respond with a single integer.
|
| 197 |
+
"""
|
| 198 |
+
# ===============================================
|
| 199 |
|
| 200 |
def step_game(human_final_order: int, human_initial_order: int, ai_suggestion: int):
|
| 201 |
+
# This core logic function remains correct and unchanged.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
state = st.session_state.game_state
|
| 203 |
week, echelons, human_role = state['week'], state['echelons'], state['human_role']
|
| 204 |
llm_personality, info_sharing = state['llm_personality'], state['info_sharing']
|
|
|
|
| 335 |
st.image(IMAGE_PATH, caption="Orders flow upstream (right), beer flows downstream (left).")
|
| 336 |
except FileNotFoundError:
|
| 337 |
st.warning("Image file not found. Please ensure 'beer_game_diagram.png' is uploaded to the repository.")
|
| 338 |
+
|
| 339 |
st.markdown("""
|
| 340 |
You will play the role of the **Distributor**. The other three roles (Retailer, Wholesaler, Factory) will be controlled by AI agents.
|
| 341 |
+
- **Retailer (AI):** Fulfills end-customer demand. Orders from the Wholesaler.
|
| 342 |
+
- **Wholesaler (AI):** Fulfills Retailer orders. Orders from the Distributor.
|
| 343 |
- **Distributor (You):** Fulfills Wholesaler orders. Orders from the Factory.
|
| 344 |
+
- **Factory (AI):** Fulfills your orders. Produces new beer.
|
| 345 |
""")
|
| 346 |
+
|
| 347 |
with col2:
|
| 348 |
st.subheader("⏳ The Challenge: Delays!")
|
| 349 |
+
st.markdown(f"""
|
| 350 |
+
The key challenge is managing the **delays** in our specific game:
|
| 351 |
|
| 352 |
+
* **Order Delay:** It takes **{ORDER_PASSING_DELAY} week** for your order to reach the Factory.
|
| 353 |
+
* **Production Delay:** The Factory needs **{FACTORY_LEAD_TIME} week** to produce your order.
|
| 354 |
+
* **Shipping Delay:** It takes **{FACTORY_SHIPPING_DELAY} week** for the Factory to ship the finished beer to you.
|
| 355 |
|
| 356 |
+
This means there is a **{ORDER_PASSING_DELAY + FACTORY_LEAD_TIME + FACTORY_SHIPPING_DELAY}-week total lead time** from when you place an order to when you receive the beer. You must order for what you'll need 3 weeks from now!
|
| 357 |
""")
|
| 358 |
|
| 359 |
st.subheader("🎮 How Each Week Works")
|
| 360 |
+
st.markdown(f"""
|
| 361 |
Your main job is to place an order each week. The system handles the rest.
|
| 362 |
|
| 363 |
**1. Automated Steps (Start of Week)**
|
| 364 |
When a new week begins, the system first automates three steps based on past decisions:
|
| 365 |
+
* **(Step 1: Shipments Arrive):** Shipments from the Factory (which you ordered {ORDER_PASSING_DELAY + FACTORY_LEAD_TIME + FACTORY_SHIPPING_DELAY} weeks ago) arrive and are added to your inventory.
|
| 366 |
* **(Step 2: New Orders Arrive):** You receive a new incoming order from the Wholesaler.
|
| 367 |
* **(Step 3: Ship Beer):** The system automatically ships as much beer as you have in stock to fulfill the Wholesaler's order, plus any leftover backlog. Any unfulfilled amount becomes your new backlog for next week.
|
| 368 |
|
|
|
|
| 404 |
st.metric("Inventory", e['inventory']); st.metric("Backlog", e['backlog'])
|
| 405 |
st.write(f"Incoming Order: **{e['incoming_order']}**")
|
| 406 |
|
|
|
|
| 407 |
if name == "Factory":
|
| 408 |
prod_completing = list(state['factory_production_pipeline'])[0] if state['factory_production_pipeline'] else 0
|
| 409 |
st.write(f"Production Completing: **{prod_completing}**")
|
| 410 |
else:
|
| 411 |
arriving = list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0
|
| 412 |
st.write(f"Arriving Next Week: **{arriving}**")
|
|
|
|
| 413 |
else:
|
| 414 |
st.info("In Local Information mode, you can only see your own status dashboard.")
|
| 415 |
e = echelons[human_role] # This is always the Distributor
|
|
|
|
| 418 |
col1.metric("Current Inventory", e['inventory'])
|
| 419 |
col2.metric("Current Backlog", e['backlog'])
|
| 420 |
col3.write(f"**Incoming Order (This Week):**\n# {e['incoming_order']}")
|
|
|
|
| 421 |
col4.write(f"**Shipment Arriving (Next Week):**\n# {list(e['incoming_shipments'])[0] if e['incoming_shipments'] else 0}")
|
| 422 |
|
| 423 |
st.markdown("---")
|
|
|
|
| 488 |
state = st.session_state.game_state
|
| 489 |
logs_df = pd.json_normalize(state['logs'])
|
| 490 |
|
| 491 |
+
# =============== FIXED Typo Bug ===============
|
| 492 |
fig = plot_results(
|
| 493 |
logs_df,
|
| 494 |
f"Beer Game (Human: {state['human_role']})\n(AI: {state['llm_personality']} | Info: {state['info_sharing']})",
|
| 495 |
state['human_role']
|
| 496 |
)
|
| 497 |
+
# ==============================================
|
| 498 |
|
| 499 |
st.pyplot(fig)
|
| 500 |
save_logs_and_upload(state)
|