Lilli98 commited on
Commit
6dfc3e7
ยท
verified ยท
1 Parent(s): 519ca56

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -29
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # app.py
2
- # @title Beer Game Final Version (v4.3 - Added Weekly Decision Log)
3
 
4
  # -----------------------------------------------------------------------------
5
  # 1. Import Libraries
@@ -41,6 +41,7 @@ BACKLOG_COST = 1.0
41
  OPENAI_MODEL = "gpt-4o-mini"
42
  LOCAL_LOG_DIR = Path("logs")
43
  LOCAL_LOG_DIR.mkdir(exist_ok=True)
 
44
 
45
  # --- API & Secrets Configuration ---
46
  try:
@@ -144,7 +145,6 @@ def step_game(human_final_order: int, human_initial_order: int, ai_suggestion: i
144
  echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"]
145
  llm_raw_responses = {}
146
 
147
- # Store pre-step state for logging
148
  pre_step_inventory = echelons[human_role]['inventory']
149
  pre_step_backlog = echelons[human_role]['backlog']
150
 
@@ -215,7 +215,7 @@ def plot_results(df: pd.DataFrame, title: str, human_role: str):
215
  inventory_pivot.plot(ax=axes[0], kind='line', marker='o', markersize=4); axes[0].set_title('Inventory Levels'); axes[0].grid(True, linestyle='--'); axes[0].set_ylabel('Stock (Units)')
216
 
217
  order_pivot = plot_df.pivot(index='week', columns='echelon', values='order_placed').reindex(columns=echelons)
218
- 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 (The Bullwhip Effect)'); axes[1].grid(True, linestyle='--'); axes[1].legend(); axes[1].set_ylabel('Ordered (Units)')
219
 
220
  total_costs = plot_df.groupby('echelon')['total_cost'].max().reindex(echelons)
221
  total_costs.plot(kind='bar', ax=axes[2], rot=0); axes[2].set_title('Total Cumulative Cost'); axes[2].set_ylabel('Cost ($)')
@@ -255,6 +255,7 @@ if st.session_state.get('initialization_error'):
255
  else:
256
  # --- Game Setup & Instructions ---
257
  if 'game_state' not in st.session_state or not st.session_state.game_state.get('game_running', False):
 
258
  st.markdown("---")
259
  st.header("๐Ÿ“– Welcome to the Beer Game!")
260
  st.markdown("""
@@ -266,9 +267,18 @@ else:
266
  - **Holding Inventory:** **$0.50 per unit per week**
267
  - **Backlog (Unfilled Orders):** **$1.00 per unit per week**
268
  """)
 
269
  col1, col2 = st.columns(2)
270
  with col1:
271
  st.subheader("๐Ÿ”— The Supply Chain")
 
 
 
 
 
 
 
 
272
  st.markdown("""
273
  You will be randomly assigned one of four roles. The other three will be controlled by AI agents.
274
  - **Retailer:** Fulfills end-customer demand. Orders from the Wholesaler.
@@ -279,8 +289,13 @@ else:
279
  with col2:
280
  st.subheader("โณ The Challenge: Delays!")
281
  st.markdown("""
282
- The key challenge is managing delays. There is a **communication delay** for orders to reach your supplier and a **shipping delay** for goods to arrive. You must order enough to meet future demand without creating a huge pile of expensive inventory.
 
 
 
 
283
  """)
 
284
  st.subheader("๐ŸŽฎ How to Play This Version")
285
  st.markdown("""
286
  1. **Configure the Game:** Choose the AI's behavior and the level of information sharing.
@@ -291,12 +306,14 @@ else:
291
  4. **Advance:** Once you submit your final order, the week advances, and all AI agents make their moves.
292
  """)
293
  st.markdown("---")
 
294
  st.header("โš™๏ธ Game Configuration")
295
  c1, c2 = st.columns(2)
296
  with c1:
297
  llm_personality = st.selectbox("AI Agent 'Personality'", ('human_like', 'perfect_rational'), format_func=lambda x: x.replace('_', ' ').title(), help="**Human-like:** Tends to react emotionally, potentially over-ordering. **Perfect Rational:** Uses a mathematical heuristic to make stable, logical decisions.")
298
  with c2:
299
  info_sharing = st.selectbox("Information Sharing Level", ('local', 'full'), format_func=lambda x: x.title(), help="**Local:** You and the AI agents can only see your own inventory and incoming orders. **Full:** Everyone can see the entire supply chain's status and the true end-customer demand.")
 
300
  if st.button("๐Ÿš€ Start Game", type="primary", disabled=(client is None)):
301
  init_game_state(llm_personality, info_sharing)
302
  st.rerun()
@@ -359,40 +376,31 @@ else:
359
  del st.session_state.final_order_input
360
  st.rerun()
361
 
362
- # =============== NEW SECTION STARTS HERE ===============
363
  st.markdown("---")
364
  with st.expander("๐Ÿ“– Your Weekly Decision Log", expanded=False):
365
  if not state['logs']:
366
  st.write("Your weekly history will be displayed here after you complete the first week.")
367
  else:
368
  history_df = pd.json_normalize(state['logs'])
369
-
370
- # Define columns to display for the human player
371
  human_cols = {
372
- 'week': 'Week',
373
- f'{human_role}.opening_inventory': 'Opening Inv.',
374
- f'{human_role}.opening_backlog': 'Opening Backlog',
375
- f'{human_role}.incoming_order': 'Incoming Order',
376
- f'{human_role}.initial_order': 'Your Initial Order',
377
- f'{human_role}.ai_suggestion': 'AI Suggestion',
378
- f'{human_role}.order_placed': 'Your Final Order',
379
- f'{human_role}.weekly_cost': 'Weekly Cost',
380
  }
381
-
382
- # Filter and rename columns
383
- display_df = history_df[list(human_cols.keys())].rename(columns=human_cols)
384
-
385
- # Format cost column
386
- display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}")
387
-
388
- # Display dataframe, sorted with the latest week on top
389
- st.dataframe(
390
- display_df.sort_values(by="Week", ascending=False),
391
- hide_index=True,
392
- use_container_width=True
393
- )
394
- # =============== NEW SECTION ENDS HERE =================
395
 
 
 
 
 
 
 
 
396
  st.sidebar.header("Game Info")
397
  st.sidebar.markdown(f"**Game ID**: `{state['participant_id']}`\n\n**Current Week**: {week}")
398
  if st.sidebar.button("๐Ÿ”„ Reset Game"):
 
1
  # app.py
2
+ # @title Beer Game Final Version (v4.4 - Added Diagram Image)
3
 
4
  # -----------------------------------------------------------------------------
5
  # 1. Import Libraries
 
41
  OPENAI_MODEL = "gpt-4o-mini"
42
  LOCAL_LOG_DIR = Path("logs")
43
  LOCAL_LOG_DIR.mkdir(exist_ok=True)
44
+ IMAGE_PATH = "beer_game_diagram.png" # Path to your uploaded image
45
 
46
  # --- API & Secrets Configuration ---
47
  try:
 
145
  echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"]
146
  llm_raw_responses = {}
147
 
 
148
  pre_step_inventory = echelons[human_role]['inventory']
149
  pre_step_backlog = echelons[human_role]['backlog']
150
 
 
215
  inventory_pivot.plot(ax=axes[0], kind='line', marker='o', markersize=4); axes[0].set_title('Inventory Levels'); axes[0].grid(True, linestyle='--'); axes[0].set_ylabel('Stock (Units)')
216
 
217
  order_pivot = plot_df.pivot(index='week', columns='echelon', values='order_placed').reindex(columns=echelons)
218
+ order_pivot.plot(ax=axes[1], style='--'); axes[1].plot(range(1, WS + 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 (The Bullwhip Effect)'); axes[1].grid(True, linestyle='--'); axes[1].legend(); axes[1].set_ylabel('Ordered (Units)')
219
 
220
  total_costs = plot_df.groupby('echelon')['total_cost'].max().reindex(echelons)
221
  total_costs.plot(kind='bar', ax=axes[2], rot=0); axes[2].set_title('Total Cumulative Cost'); axes[2].set_ylabel('Cost ($)')
 
255
  else:
256
  # --- Game Setup & Instructions ---
257
  if 'game_state' not in st.session_state or not st.session_state.game_state.get('game_running', False):
258
+
259
  st.markdown("---")
260
  st.header("๐Ÿ“– Welcome to the Beer Game!")
261
  st.markdown("""
 
267
  - **Holding Inventory:** **$0.50 per unit per week**
268
  - **Backlog (Unfilled Orders):** **$1.00 per unit per week**
269
  """)
270
+
271
  col1, col2 = st.columns(2)
272
  with col1:
273
  st.subheader("๐Ÿ”— The Supply Chain")
274
+
275
+ # =============== IMAGE ADDED HERE ===============
276
+ try:
277
+ st.image(IMAGE_PATH, caption="Orders flow upstream (right), beer flows downstream (left).")
278
+ except FileNotFoundError:
279
+ st.warning("Image file not found. Please ensure 'beer_game_diagram.png' is uploaded to the repository.")
280
+ # ================================================
281
+
282
  st.markdown("""
283
  You will be randomly assigned one of four roles. The other three will be controlled by AI agents.
284
  - **Retailer:** Fulfills end-customer demand. Orders from the Wholesaler.
 
289
  with col2:
290
  st.subheader("โณ The Challenge: Delays!")
291
  st.markdown("""
292
+ The key challenge is managing delays. As shown in the diagram, there are **shipping delays** for beer to arrive and **production delays** at the factory.
293
+
294
+ There is also a one-week **order-passing delay** (the small boxes) for your order to reach your supplier.
295
+
296
+ You must order enough to meet future demand without creating a huge pile of expensive inventory.
297
  """)
298
+
299
  st.subheader("๐ŸŽฎ How to Play This Version")
300
  st.markdown("""
301
  1. **Configure the Game:** Choose the AI's behavior and the level of information sharing.
 
306
  4. **Advance:** Once you submit your final order, the week advances, and all AI agents make their moves.
307
  """)
308
  st.markdown("---")
309
+
310
  st.header("โš™๏ธ Game Configuration")
311
  c1, c2 = st.columns(2)
312
  with c1:
313
  llm_personality = st.selectbox("AI Agent 'Personality'", ('human_like', 'perfect_rational'), format_func=lambda x: x.replace('_', ' ').title(), help="**Human-like:** Tends to react emotionally, potentially over-ordering. **Perfect Rational:** Uses a mathematical heuristic to make stable, logical decisions.")
314
  with c2:
315
  info_sharing = st.selectbox("Information Sharing Level", ('local', 'full'), format_func=lambda x: x.title(), help="**Local:** You and the AI agents can only see your own inventory and incoming orders. **Full:** Everyone can see the entire supply chain's status and the true end-customer demand.")
316
+
317
  if st.button("๐Ÿš€ Start Game", type="primary", disabled=(client is None)):
318
  init_game_state(llm_personality, info_sharing)
319
  st.rerun()
 
376
  del st.session_state.final_order_input
377
  st.rerun()
378
 
 
379
  st.markdown("---")
380
  with st.expander("๐Ÿ“– Your Weekly Decision Log", expanded=False):
381
  if not state['logs']:
382
  st.write("Your weekly history will be displayed here after you complete the first week.")
383
  else:
384
  history_df = pd.json_normalize(state['logs'])
 
 
385
  human_cols = {
386
+ 'week': 'Week', f'{human_role}.opening_inventory': 'Opening Inv.',
387
+ f'{human_role}.opening_backlog': 'Opening Backlog', f'{human_role}.incoming_order': 'Incoming Order',
388
+ f'{human_role}.initial_order': 'Your Initial Order', f'{human_role}.ai_suggestion': 'AI Suggestion',
389
+ f'{human_role}.order_placed': 'Your Final Order', f'{human_role}.weekly_cost': 'Weekly Cost',
 
 
 
 
390
  }
391
+ display_cols = [col for col in human_cols.keys() if col in history_df.columns]
392
+ display_df = history_df[display_cols].rename(columns=human_cols)
393
+ if 'Weekly Cost' in display_df:
394
+ display_df['Weekly Cost'] = display_df['Weekly Cost'].apply(lambda x: f"${x:,.2f}")
395
+ st.dataframe(display_df.sort_values(by="Week", ascending=False), hide_index=True, use_container_width=True)
 
 
 
 
 
 
 
 
 
396
 
397
+ # =============== IMAGE ADDED TO SIDEBAR ===============
398
+ try:
399
+ st.sidebar.image(IMAGE_PATH, caption="Supply Chain Reference")
400
+ except FileNotFoundError:
401
+ st.sidebar.warning("Image file not found.")
402
+ # ======================================================
403
+
404
  st.sidebar.header("Game Info")
405
  st.sidebar.markdown(f"**Game ID**: `{state['participant_id']}`\n\n**Current Week**: {week}")
406
  if st.sidebar.button("๐Ÿ”„ Reset Game"):