Song commited on
Commit
6f1d28d
·
1 Parent(s): 16b5d27
Files changed (2) hide show
  1. app.py +13 -49
  2. game_ui_helper.py +0 -36
app.py CHANGED
@@ -12,7 +12,7 @@ import random
12
  from datetime import datetime
13
  from typing import Dict, List, Optional, Tuple
14
  from game_system import game_state, ACHIEVEMENTS, SCENARIO_DIFFICULTY, PERSONA_COMPLEXITY
15
- from game_ui_helper import update_game_display, format_achievement_popup
16
 
17
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
18
  if not OPENROUTER_API_KEY:
@@ -180,11 +180,14 @@ def analyze_risk(teacher_input: str, persona: str, scenario: str) -> tuple[str,
180
  # 嘗試從回應中提取 JSON
181
  json_match = re.search(r'\{.*\}', response, re.DOTALL)
182
  if json_match:
 
 
 
183
  try:
184
- data = json.loads(json_match.group(0))
185
- print("DEBUG: Extracted JSON successfully")
186
  except Exception as e2:
187
- print(f"DEBUG: Extracted JSON parsing failed: {e2}")
188
 
189
  if data:
190
  risk_score = data.get("risk_score", 50)
@@ -275,16 +278,11 @@ def start_conversation(persona: str, scenario: str, challenge_mode: str):
275
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
276
  level_info, ach_summary, stats_summary, challenge_info)
277
 
278
- popup_html = format_achievement_popup(new_achievements)
279
- popup_visible = len(new_achievements) > 0
280
- print(f"DEBUG: Popup shown in start_conversation, visible={popup_visible}, achievements={len(new_achievements)}")
281
-
282
  return (
283
  new_chat,
284
  risk_html,
285
  gr.update(value=""), # 清空輸入框
286
- level_html, score_html, ach_html, stats_html, progress_html, challenge_html,
287
- gr.update(value=popup_html, visible=popup_visible)
288
  )
289
 
290
  def continue_conversation(persona: str, scenario: str, chat_history: list, teacher_input: str):
@@ -324,10 +322,6 @@ def continue_conversation(persona: str, scenario: str, chat_history: list, teach
324
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
325
  level_info, ach_summary, stats_summary, challenge_info)
326
 
327
- popup_html = format_achievement_popup(new_achievements)
328
- popup_visible = len(new_achievements) > 0
329
- print(f"DEBUG: Popup shown in continue_conversation, visible={popup_visible}, achievements={len(new_achievements)}")
330
-
331
  # 挑戰完成檢查
332
  challenge_complete = game_state.check_challenge_complete() if game_state.current_challenge_mode else False
333
 
@@ -339,8 +333,7 @@ def continue_conversation(persona: str, scenario: str, chat_history: list, teach
339
  new_chat,
340
  risk_html,
341
  teacher_update,
342
- level_html, score_html, ach_html, stats_html, progress_html, challenge_html,
343
- gr.update(value=popup_html, visible=popup_visible)
344
  )
345
 
346
  def reset_conversation():
@@ -353,13 +346,11 @@ def reset_conversation():
353
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
354
  level_info, ach_summary, stats_summary, challenge_info)
355
 
356
- print("DEBUG: Popup hidden in reset_conversation")
357
  return (
358
  [],
359
  "💬 請先選擇人格與情境,然後點「開始對話」",
360
  gr.update(value=""),
361
- level_html, score_html, ach_html, stats_html, progress_html, challenge_html,
362
- gr.update(value="", visible=False)
363
  )
364
 
365
  # =============== CSS ===============
@@ -394,31 +385,6 @@ CUSTOM_CSS = """
394
  transition: width 0.3s ease;
395
  }
396
 
397
- .achievement-popup {
398
- position: fixed;
399
- top: 50%;
400
- left: 50%;
401
- transform: translate(-50%, -50%);
402
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
403
- color: white;
404
- padding: 24px;
405
- border-radius: 12px;
406
- box-shadow: 0 8px 32px rgba(0,0,0,0.3);
407
- z-index: 1000;
408
- text-align: center;
409
- animation: popup 0.3s ease;
410
- }
411
-
412
- @keyframes popup {
413
- from {
414
- opacity: 0;
415
- transform: translate(-50%, -50%) scale(0.8);
416
- }
417
- to {
418
- opacity: 1;
419
- transform: translate(-50%, -50%) scale(1);
420
- }
421
- }
422
  """
423
 
424
  # =============== Gradio UI ===============
@@ -437,7 +403,7 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
437
  stats_display = gr.HTML()
438
  with gr.Accordion("📅 每日挑戰", open=False):
439
  challenge_display = gr.HTML()
440
- achievement_popup = gr.HTML("", visible=False)
441
 
442
  # 選擇區
443
  with gr.Row():
@@ -477,8 +443,7 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
477
 
478
  # 事件綁定
479
  common_outputs = [chatbot, risk_output, teacher_input, level_display, score_display,
480
- achievements_display, stats_display, progress_bar, challenge_display,
481
- achievement_popup]
482
 
483
  start_btn.click(
484
  start_conversation,
@@ -504,8 +469,7 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
504
  demo.load(
505
  reset_conversation,
506
  outputs=[chatbot, risk_output, teacher_input, level_display, score_display,
507
- achievements_display, stats_display, progress_bar, challenge_display,
508
- achievement_popup]
509
  )
510
 
511
  if __name__ == "__main__":
 
12
  from datetime import datetime
13
  from typing import Dict, List, Optional, Tuple
14
  from game_system import game_state, ACHIEVEMENTS, SCENARIO_DIFFICULTY, PERSONA_COMPLEXITY
15
+ from game_ui_helper import update_game_display
16
 
17
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
18
  if not OPENROUTER_API_KEY:
 
180
  # 嘗試從回應中提取 JSON
181
  json_match = re.search(r'\{.*\}', response, re.DOTALL)
182
  if json_match:
183
+ extracted = json_match.group(0)
184
+ # 清理未轉義的新行
185
+ extracted = re.sub(r'(?<!\\)\n', r'\\n', extracted)
186
  try:
187
+ data = json.loads(extracted)
188
+ print("DEBUG: Extracted and cleaned JSON successfully")
189
  except Exception as e2:
190
+ print(f"DEBUG: Extracted and cleaned JSON parsing failed: {e2}")
191
 
192
  if data:
193
  risk_score = data.get("risk_score", 50)
 
278
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
279
  level_info, ach_summary, stats_summary, challenge_info)
280
 
 
 
 
 
281
  return (
282
  new_chat,
283
  risk_html,
284
  gr.update(value=""), # 清空輸入框
285
+ level_html, score_html, ach_html, stats_html, progress_html, challenge_html
 
286
  )
287
 
288
  def continue_conversation(persona: str, scenario: str, chat_history: list, teacher_input: str):
 
322
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
323
  level_info, ach_summary, stats_summary, challenge_info)
324
 
 
 
 
 
325
  # 挑戰完成檢查
326
  challenge_complete = game_state.check_challenge_complete() if game_state.current_challenge_mode else False
327
 
 
333
  new_chat,
334
  risk_html,
335
  teacher_update,
336
+ level_html, score_html, ach_html, stats_html, progress_html, challenge_html
 
337
  )
338
 
339
  def reset_conversation():
 
346
  level_html, score_html, ach_html, stats_html, progress_html, challenge_html = update_game_display(
347
  level_info, ach_summary, stats_summary, challenge_info)
348
 
 
349
  return (
350
  [],
351
  "💬 請先選擇人格與情境,然後點「開始對話」",
352
  gr.update(value=""),
353
+ level_html, score_html, ach_html, stats_html, progress_html, challenge_html
 
354
  )
355
 
356
  # =============== CSS ===============
 
385
  transition: width 0.3s ease;
386
  }
387
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  """
389
 
390
  # =============== Gradio UI ===============
 
403
  stats_display = gr.HTML()
404
  with gr.Accordion("📅 每日挑戰", open=False):
405
  challenge_display = gr.HTML()
406
+
407
 
408
  # 選擇區
409
  with gr.Row():
 
443
 
444
  # 事件綁定
445
  common_outputs = [chatbot, risk_output, teacher_input, level_display, score_display,
446
+ achievements_display, stats_display, progress_bar, challenge_display]
 
447
 
448
  start_btn.click(
449
  start_conversation,
 
469
  demo.load(
470
  reset_conversation,
471
  outputs=[chatbot, risk_output, teacher_input, level_display, score_display,
472
+ achievements_display, stats_display, progress_bar, challenge_display]
 
473
  )
474
 
475
  if __name__ == "__main__":
game_ui_helper.py CHANGED
@@ -240,42 +240,6 @@ def format_daily_challenge_display(challenge_info: Dict) -> str:
240
  """
241
 
242
 
243
- def format_achievement_popup(achievements: List[Dict]) -> str:
244
- """格式化成就解鎖彈窗"""
245
- if not achievements:
246
- return ""
247
-
248
- html = '<div class="achievement-popup" style="opacity: 1; transition: opacity 0.5s ease-out;">'
249
- html += '<div style="font-size: 2em; margin-bottom: 8px;">🎉</div>'
250
- html += '<div style="font-size: 1.2em; font-weight: bold; margin-bottom: 8px;">新成就解鎖!</div>'
251
-
252
- for achievement in achievements:
253
- icon = achievement.get("icon", "🏆")
254
- name = achievement.get("name", "未知成就")
255
- points = achievement.get("points", 0)
256
- html += f'<div style="margin: 4px 0;">{icon} {name} (+{points} 分)</div>'
257
-
258
- html += '</div>'
259
- html += '''<script>
260
- (function() {
261
- var popup = document.querySelector('.achievement-popup');
262
- if (popup) {
263
- function dismiss() {
264
- if (popup) {
265
- popup.style.opacity = '0';
266
- popup.addEventListener('transitionend', function() {
267
- if (popup) {
268
- popup.style.display = 'none';
269
- }
270
- }, { once: true });
271
- }
272
- }
273
- setTimeout(dismiss, 2000);
274
- }
275
- })();
276
- </script>'''
277
- return html
278
-
279
 
280
  def update_game_display(level_info: Dict, achievements_summary: Dict, stats_summary: Dict, challenge_info: Dict = None) -> tuple:
281
  """更新遊戲顯示(所有元素)"""
 
240
  """
241
 
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
  def update_game_display(level_info: Dict, achievements_summary: Dict, stats_summary: Dict, challenge_info: Dict = None) -> tuple:
245
  """更新遊戲顯示(所有元素)"""