beatccjiang commited on
Commit
9b5deef
·
verified ·
1 Parent(s): 79dbe7e

Upload 3 files

Browse files
Files changed (3) hide show
  1. GUI-Light.py +1040 -0
  2. app.py +60 -0
  3. requirements.txt +7 -0
GUI-Light.py ADDED
@@ -0,0 +1,1040 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==================== 导入标准库 ====================
2
+ import json
3
+ import os
4
+ import re
5
+ import sys
6
+ from typing import List, Tuple, Optional
7
+
8
+ # ==================== 设置路径(必须在导入 TextEnv_v2 之前)====================
9
+ current_dir = os.path.dirname(os.path.abspath(__file__))
10
+ lightenv_path = os.path.join(current_dir, "LightEnv")
11
+ if os.path.exists(lightenv_path):
12
+ sys.path.insert(0, lightenv_path)
13
+
14
+ # ==================== 修复 huggingface_hub 兼容性(必须在导入 gradio 之前)====================
15
+ # Gradio 4.x 需要 HfFolder,但旧版本的 huggingface_hub 没有
16
+ def _fix_huggingface_hub():
17
+ """修复 huggingface_hub 兼容性问题"""
18
+ try:
19
+ import huggingface_hub
20
+ if not hasattr(huggingface_hub, 'HfFolder'):
21
+ class HfFolder:
22
+ @staticmethod
23
+ def save_token(token):
24
+ pass
25
+ @staticmethod
26
+ def get_token():
27
+ return None
28
+ @staticmethod
29
+ def get_token_path():
30
+ return None
31
+ huggingface_hub.HfFolder = HfFolder
32
+ if hasattr(huggingface_hub, '__all__'):
33
+ if 'HfFolder' not in huggingface_hub.__all__:
34
+ huggingface_hub.__all__.append('HfFolder')
35
+ except Exception:
36
+ pass
37
+
38
+ _fix_huggingface_hub()
39
+
40
+ # ==================== 导入第三方库 ====================
41
+ import gradio as gr
42
+
43
+ # ==================== 导入本地模块(在路径设置之后)====================
44
+ # fmt: on
45
+ from TextEnv_v2 import LightBulbEnv
46
+
47
+ # ------------------- 全局变量 -------------------
48
+ current_env: Optional[LightBulbEnv] = None
49
+ test_data: List[dict] = []
50
+ current_env_idx: int = 0
51
+ history_records: List[str] = []
52
+ current_user_id: str = ""
53
+ show_logic: bool = False # 默认隐藏逻辑规则
54
+ save_dir = os.path.join(current_dir, "user_progress")
55
+ MAX_STEPS = 200 # lights 任务的步骤上限
56
+
57
+ # ------------------- 工具函数 -------------------
58
+
59
+
60
+ def load_test_data():
61
+ """加载测试数据"""
62
+ global test_data
63
+ # 支持相对路径和绝对路径
64
+ test_file = os.path.join(
65
+ current_dir, "test_data/turnonlights/test_turnonlights_lite_251030.json")
66
+ # 如果文件不存在,尝试相对路径
67
+ if not os.path.exists(test_file):
68
+ test_file = "test_data/turnonlights/test_turnonlights_lite_251030.json"
69
+
70
+ try:
71
+ with open(test_file, 'r', encoding='utf-8') as f:
72
+ test_data = json.load(f)
73
+ return f"✅ 成功加载 {len(test_data)} 个测试环境"
74
+ except FileNotFoundError:
75
+ return f"❌ 文件未找到: {test_file}\n提示: 请确保测试数据文件存在"
76
+ except Exception as e:
77
+ return f"❌ 加载失败: {str(e)}"
78
+
79
+
80
+ def format_bulb_state(obs: List[bool]) -> str:
81
+ """格式化灯泡状态显示"""
82
+ state_str = " ".join(["💡" if b else "○" for b in obs])
83
+ indices = " ".join([f"{i:2d}" for i in range(len(obs))])
84
+ return f"{state_str}\n{indices}"
85
+
86
+
87
+ def format_logic(logic_expr: dict) -> str:
88
+ """格式化逻辑表达式显示"""
89
+ lines = []
90
+ for bulb in sorted(logic_expr.keys()):
91
+ lines.append(f"{bulb}: {logic_expr[bulb]}")
92
+ return "\n".join(lines)
93
+
94
+
95
+ def load_environment(env_idx_display: int) -> Tuple[str, str, str, str, str]:
96
+ """加载环境(如果有保存的进度就加载,否则初始化新环境)
97
+ Args:
98
+ env_idx_display: 用户输入的环境索引(1-30,显示用)
99
+ Returns: (环境信息, 状态显示, 逻辑规则, 历史记录, 进度摘要)
100
+ """
101
+ global current_env, current_env_idx, history_records, show_logic, test_data, current_user_id
102
+
103
+ if not current_user_id:
104
+ progress_summary_text = "点击'查看未完成问题'按钮查看进度"
105
+ return "❌ 请先输入用户 ID", "", "", "", progress_summary_text
106
+
107
+ if not test_data:
108
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
109
+ return "❌ 请先加载测试数据", "", "", "", progress_summary_text
110
+
111
+ # 将用户输入的 1-30 转换为内部索引 0-29
112
+ env_idx = env_idx_display - 1
113
+
114
+ if env_idx < 0 or env_idx >= len(test_data):
115
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
116
+ return f"❌ 环境索引超出范围 (1-{len(test_data)})", "", "", "", progress_summary_text
117
+
118
+ # 检查是否有保存的进度(检查两个版本的文件)
119
+ has_saved_progress = False
120
+ saved_progress_data = None
121
+ found_with_logic = None
122
+
123
+ # 检查带逻辑规则和不带逻辑规则的版本
124
+ for with_logic_flag in [True, False]:
125
+ save_path = get_save_path(current_user_id, with_logic=with_logic_flag)
126
+ if os.path.exists(save_path):
127
+ try:
128
+ with open(save_path, 'r', encoding='utf-8') as f:
129
+ all_progress_data = json.load(f)
130
+
131
+ # 处理新旧格式兼容
132
+ if "environments" in all_progress_data:
133
+ # 新格式:所有环境的进度保存在一个文件中
134
+ environments = all_progress_data.get("environments", {})
135
+ if str(env_idx) in environments:
136
+ saved_progress_data = environments[str(env_idx)]
137
+ has_saved_progress = True
138
+ found_with_logic = with_logic_flag
139
+ break # 找到就退出循环
140
+ else:
141
+ # 旧格式:单个环境的进度
142
+ old_env_idx = all_progress_data.get("env_idx", -1)
143
+ if old_env_idx == env_idx:
144
+ saved_progress_data = all_progress_data
145
+ has_saved_progress = True
146
+ found_with_logic = with_logic_flag
147
+ break # 找到就退出循环
148
+ except Exception:
149
+ continue
150
+
151
+ # 如果找到了保存的进度,更新 show_logic 以匹配找到的文件
152
+ if has_saved_progress and found_with_logic is not None:
153
+ show_logic = found_with_logic
154
+
155
+ # 如果有保存的进度,检查是否已完成
156
+ if has_saved_progress and saved_progress_data:
157
+ bulb_states = saved_progress_data.get("bulb_states", [])
158
+ num_steps = saved_progress_data.get("num_steps", 0)
159
+ is_completed = False
160
+ if bulb_states and all(bulb_states):
161
+ is_completed = True # 成功完成
162
+ elif num_steps >= MAX_STEPS:
163
+ is_completed = True # 达到上限,算作完成
164
+
165
+ if is_completed:
166
+ # 已完成,不覆盖,直接加载
167
+ return load_specific_environment(env_idx, saved_progress_data)
168
+ else:
169
+ # 未完成,加载保存的进度
170
+ return load_specific_environment(env_idx, saved_progress_data)
171
+
172
+ # 没有保存的进度,初始化新环境
173
+ current_env_idx = env_idx
174
+ d = test_data[env_idx]
175
+ current_env = LightBulbEnv(
176
+ custom_logic=d["custom_logic"], num_bulbs=d["level"])
177
+ history_records = []
178
+
179
+ # 保存初始化后的状态
180
+ save_progress()
181
+
182
+ obs = current_env._get_obs()
183
+ state_display = format_bulb_state(obs)
184
+ # 初始化环境时总是显示逻辑规则(因为这是新环境)
185
+ logic_display = format_logic(d["custom_logic"])
186
+ show_logic = True # 初始化时设置为显示
187
+ history_display = "环境已初始化(新环境)\n"
188
+
189
+ info = f"✅ 环境 {env_idx_display}/{len(test_data)} 已初始化(新环境)\n"
190
+ info += f"灯泡数量: {d['level']}\n"
191
+ info += f"初始状态: {state_display.split(chr(10))[0]}"
192
+
193
+ # 自动更新进度摘要
194
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
195
+
196
+ return info, state_display, logic_display, history_display, progress_summary_text
197
+
198
+
199
+ def load_specific_environment(env_idx: int, progress_data: dict) -> Tuple[str, str, str, str, str]:
200
+ """加载特定环境的保存进度
201
+ Args:
202
+ env_idx: 环境索引
203
+ progress_data: 保存的进度数据
204
+ Returns: (环境信息, 状态显示, 逻辑规则, 历史记录, 进度摘要)
205
+ """
206
+ global current_env, current_env_idx, history_records, show_logic, test_data, current_user_id
207
+
208
+ current_env_idx = env_idx
209
+ env_idx_display = progress_data.get("env_idx_display", env_idx + 1)
210
+ bulb_states = progress_data.get("bulb_states", [])
211
+ history_records = progress_data.get("history", [])
212
+ level = progress_data.get("level", 0)
213
+ num_steps = progress_data.get("num_steps", len(history_records))
214
+
215
+ # 获取逻辑规则
216
+ custom_logic = progress_data.get("custom_logic", {})
217
+
218
+ # 根据数据是否包含逻辑规则设置显示状态
219
+ has_logic_in_data = bool(custom_logic and len(custom_logic) > 0)
220
+ if has_logic_in_data:
221
+ show_logic = True
222
+ else:
223
+ show_logic = False
224
+
225
+ # 如果没有保存逻辑规则,从test_data中获取
226
+ if not custom_logic and env_idx < len(test_data):
227
+ custom_logic = test_data[env_idx].get("custom_logic", {})
228
+
229
+ # 恢复环境
230
+ if env_idx < len(test_data) and level > 0:
231
+ current_env = LightBulbEnv(custom_logic=custom_logic, num_bulbs=level)
232
+ # 恢复环境的步数
233
+ current_env.steps = num_steps
234
+ # 恢复灯泡状态
235
+ for i, state in enumerate(bulb_states):
236
+ if i < current_env.num_bulbs:
237
+ bulb_name = f"B{i}"
238
+ if bulb_name in current_env.bulbs:
239
+ current_env.bulbs[bulb_name] = state
240
+
241
+ obs = current_env._get_obs()
242
+ state_display = format_bulb_state(obs)
243
+
244
+ # 根据数据是否包含逻辑规则决定显示
245
+ if show_logic and has_logic_in_data:
246
+ logic_display = format_logic(progress_data.get("custom_logic", {}))
247
+ else:
248
+ logic_display = "(此进度文件不包含逻辑规则)"
249
+
250
+ history_display = "\n".join(history_records) if history_records else "无历史记录"
251
+
252
+ # 检查是否已完成
253
+ is_completed = False
254
+ if bulb_states and all(bulb_states):
255
+ is_completed = True
256
+ elif num_steps >= MAX_STEPS:
257
+ is_completed = True
258
+
259
+ if is_completed:
260
+ if all(bulb_states):
261
+ status_text = "已加载(已完成)"
262
+ else:
263
+ status_text = "已加载(已达到步骤上限)"
264
+ else:
265
+ status_text = "已加载(继续之前的进度)"
266
+
267
+ info = f"✅ 环境 {env_idx_display}/{len(test_data)} {status_text}\n"
268
+ info += f"灯泡数量: {level}\n"
269
+ info += f"步骤数: {len(history_records)}"
270
+
271
+ # 自动更新进度摘要
272
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
273
+
274
+ return info, state_display, logic_display, history_display, progress_summary_text
275
+
276
+
277
+ def step_environment(action_str: str) -> Tuple[str, str, str, bool]:
278
+ """执行一步动作"""
279
+ global current_env, history_records
280
+
281
+ # 获取当前状态,用于错误时保持显示
282
+ current_state_display = ""
283
+ if current_env is not None:
284
+ obs = current_env._get_obs()
285
+ current_state_display = format_bulb_state(obs)
286
+
287
+ if current_env is None:
288
+ return "❌ 请先初始化环境", current_state_display if current_state_display else "请先初始化环境", "", False
289
+
290
+ if not current_user_id:
291
+ return "❌ 请先输入用户 ID", current_state_display, "", False
292
+
293
+ # 解析动作
294
+ action = None
295
+ action_error = None
296
+ try:
297
+ action = int(action_str.strip())
298
+ if action < 0 or action >= current_env.num_bulbs:
299
+ action_error = f"动作超出范围 (0-{current_env.num_bulbs-1})"
300
+ except ValueError:
301
+ action_error = f"无效的动作格式: {action_str}"
302
+
303
+ # 检查是否已经达到步骤上限(在执行动作之前)
304
+ if current_env.steps >= MAX_STEPS:
305
+ # 已达到上限,任务算作完成(但失败了)
306
+ history_display = "\n".join(history_records) if history_records else ""
307
+ save_status = save_progress()
308
+ feedback_info = f"⚠️ 已达到步骤上限 ({MAX_STEPS} 步)\n"
309
+ feedback_info += "任务已结束(未能在规定步数内完成)\n"
310
+ feedback_info += "无法继续执行动作\n"
311
+ feedback_info += save_status
312
+
313
+ return feedback_info, current_state_display, history_display, True
314
+
315
+ # 如果动作无效,也要算作一步并记录到历史
316
+ if action_error:
317
+ # 记录无效动作到历史
318
+ history_records.append(
319
+ f"步骤 {len(history_records) + 1}: 动作={action_str} (无效), 反馈={action_error}")
320
+ history_display = "\n".join(history_records)
321
+
322
+ # 增加环境的步数(即使动作无效也算一步)
323
+ current_env.steps += 1
324
+
325
+ # 检查是否达到上限(执行无效动作后)
326
+ if current_env.steps >= MAX_STEPS:
327
+ # 达到上限,任务算作完成(但失败了)
328
+ history_records.append(
329
+ f"步骤 {len(history_records) + 1}: 已达到步骤上限 ({MAX_STEPS} 步),任务结束")
330
+ history_display = "\n".join(history_records)
331
+
332
+ # 自动保存进度
333
+ save_status = save_progress()
334
+
335
+ # 生成反馈信息
336
+ feedback_info = f"动作: {action_str}\n反馈: ❌ {action_error}\n"
337
+ feedback_info += f"⚠️ 已达到步骤上限 ({MAX_STEPS} 步)\n"
338
+ feedback_info += "任务已结束(未能在规定步数内完成)\n"
339
+ feedback_info += save_status
340
+
341
+ return feedback_info, current_state_display, history_display, True
342
+
343
+ # 自动保存进度
344
+ save_status = save_progress()
345
+
346
+ # 生成反馈信息
347
+ feedback_info = f"动作: {action_str}\n反馈: ❌ {action_error}\n"
348
+ feedback_info += save_status
349
+
350
+ return feedback_info, current_state_display, history_display, False
351
+
352
+ # 执行有效动作
353
+ obs, feedback, done, _ = current_env.step(action)
354
+ state_display = format_bulb_state(obs)
355
+
356
+ # 更新历史
357
+ history_records.append(
358
+ f"步骤 {len(history_records) + 1}: 动作={action}, 反馈={feedback}")
359
+ history_display = "\n".join(history_records)
360
+
361
+ # 检查是否达到上限(在执行动作后)
362
+ if current_env.steps >= MAX_STEPS:
363
+ done = True # 达到上限,任务算作完成(但失败了)
364
+ if not all(obs): # 如果还没完成所有灯泡
365
+ feedback = f"{feedback}\n⚠️ 已达到步骤上限 ({MAX_STEPS} 步),任务结束(未能在规定步数内完成)"
366
+
367
+ # 自动保存进度
368
+ save_status = save_progress()
369
+
370
+ # 生成反馈信息
371
+ feedback_info = f"动作: {action}\n反馈: {feedback}\n"
372
+ if done:
373
+ if all(obs): # 所有灯泡都点亮了
374
+ feedback_info += "🎉 任务完成!所有灯泡已点亮!\n"
375
+ else: # 达到上限但未完成
376
+ feedback_info += f"⚠️ 任务已结束(已达到步骤上限 {MAX_STEPS} 步)\n"
377
+ feedback_info += save_status
378
+
379
+ return feedback_info, state_display, history_display, done
380
+
381
+
382
+ def reset_environment() -> Tuple[str, str, str, str]:
383
+ """重置当前环境
384
+ Returns: (环境信息, 状态显示, 历史记录, 进度摘要)
385
+ """
386
+ global current_env, history_records, current_user_id
387
+
388
+ if current_env is None:
389
+ return "❌ 请先初始化环境", "", "", "点击'查看未完成问题'按钮查看进度"
390
+
391
+ current_env.reset()
392
+ history_records = []
393
+
394
+ # 保存重置后的状态
395
+ save_progress()
396
+
397
+ obs = current_env._get_obs()
398
+ state_display = format_bulb_state(obs)
399
+ history_display = "环境已重置\n"
400
+
401
+ # 自动更新进度摘要
402
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
403
+
404
+ return "✅ 环境已重置", state_display, history_display, progress_summary_text
405
+
406
+
407
+ def get_save_path(user_id: str, with_logic: bool = None) -> str:
408
+ """获取用户进度保存路径
409
+ Args:
410
+ user_id: 用户ID
411
+ with_logic: 是否包含逻辑规则。如果为None,则根据全局show_logic决定
412
+ """
413
+ os.makedirs(save_dir, exist_ok=True)
414
+ if with_logic is None:
415
+ with_logic = show_logic
416
+
417
+ if with_logic:
418
+ return os.path.join(save_dir, f"user_{user_id}_with_logic.json")
419
+ else:
420
+ return os.path.join(save_dir, f"user_{user_id}_no_logic.json")
421
+
422
+
423
+ def save_progress() -> str:
424
+ """保存当前进度(保存所有环境的进度到一个文件中)"""
425
+ global current_env, current_env_idx, history_records, current_user_id, test_data, show_logic
426
+
427
+ if not current_user_id:
428
+ return "⚠️ 请先输入用户 ID"
429
+
430
+ if current_env is None:
431
+ return "⚠️ 没有可保存的进度"
432
+
433
+ try:
434
+ # 获取当前环境状态
435
+ obs = current_env._get_obs()
436
+
437
+ # 根据是否显示逻辑规则保存到不同的文件
438
+ save_path = get_save_path(current_user_id, with_logic=show_logic)
439
+
440
+ # 读取已有的进度数据(如果存在)
441
+ all_progress = {}
442
+ if os.path.exists(save_path):
443
+ try:
444
+ with open(save_path, 'r', encoding='utf-8') as f:
445
+ existing_data = json.load(f)
446
+ # 如果旧格式(单个环境),转换为新格式
447
+ if "env_idx" in existing_data and "environments" not in existing_data:
448
+ # 旧格式,转换为新格式
449
+ old_env_idx = existing_data.get("env_idx", -1)
450
+ all_progress["environments"] = {}
451
+ all_progress["environments"][str(old_env_idx)] = existing_data
452
+ else:
453
+ # 新格式
454
+ all_progress = existing_data
455
+ except Exception:
456
+ all_progress = {}
457
+
458
+ # 确保 environments 字典存在
459
+ if "environments" not in all_progress:
460
+ all_progress["environments"] = {}
461
+
462
+ # 构建当前环境的进度数据
463
+ env_progress = {
464
+ "user_id": current_user_id,
465
+ "env_idx": current_env_idx,
466
+ "env_idx_display": current_env_idx + 1,
467
+ "bulb_states": obs,
468
+ "history": history_records,
469
+ "num_steps": current_env.steps,
470
+ "level": current_env.num_bulbs,
471
+ }
472
+
473
+ # 如果显示逻辑规则,才保存逻辑规则数据
474
+ if show_logic and current_env_idx < len(test_data):
475
+ env_progress["custom_logic"] = test_data[current_env_idx]["custom_logic"]
476
+
477
+ # 保存当前环境的进度
478
+ all_progress["environments"][str(current_env_idx)] = env_progress
479
+ all_progress["user_id"] = current_user_id
480
+ all_progress["current_env_idx"] = current_env_idx
481
+
482
+ # 保存到文件
483
+ with open(save_path, 'w', encoding='utf-8') as f:
484
+ json.dump(all_progress, f, ensure_ascii=False, indent=2)
485
+
486
+ return f"✅ 进度已保存 (环境 {current_env_idx + 1}, 步骤 {len(history_records)})"
487
+ except Exception as e:
488
+ return f"❌ 保存失败: {str(e)}"
489
+
490
+
491
+ def load_progress(user_id: str, with_logic: bool) -> Tuple[str, float, str, str, str, str]:
492
+ """加载用户进度
493
+ Args:
494
+ user_id: 用户ID
495
+ with_logic: 是否加载带逻辑规则的版本
496
+ Returns: (状态信息, 环境���引显示(数字), 灯泡状态, 逻辑规则, 历史记录, 进度摘要)
497
+ """
498
+ global current_env, current_env_idx, history_records, current_user_id, test_data, show_logic
499
+
500
+ if not user_id or not user_id.strip():
501
+ progress_summary_text = "点击'查看未完成问题'按钮查看进度"
502
+ return "⚠️ 请输入用户 ID", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
503
+
504
+ user_id = user_id.strip()
505
+ save_path = get_save_path(user_id, with_logic=with_logic)
506
+
507
+ if not os.path.exists(save_path):
508
+ # 尝试加载另一个版本的文件
509
+ alt_save_path = get_save_path(user_id, with_logic=not with_logic)
510
+ if os.path.exists(alt_save_path):
511
+ # 如果另一个版本存在,提示用户
512
+ version_type = "带逻辑规则" if not with_logic else "不带逻辑规则"
513
+ current_user_id = user_id
514
+ show_logic = False
515
+ progress_summary_text = get_user_progress_summary(user_id)
516
+ return f"ℹ️ 用户 {user_id} 的{version_type}版本进度存在,请选择对应的版本", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
517
+ else:
518
+ current_user_id = user_id
519
+ show_logic = False # 默认隐藏
520
+ progress_summary_text = get_user_progress_summary(user_id)
521
+ return f"ℹ️ 用户 {user_id} 没有保存的进度,请初始化新环境", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
522
+
523
+ try:
524
+ with open(save_path, 'r', encoding='utf-8') as f:
525
+ all_progress_data = json.load(f)
526
+
527
+ # 处理新旧格式兼容
528
+ if "environments" in all_progress_data:
529
+ # 新格式:所有环境的进度保存在一个文件中
530
+ current_env_idx_from_file = all_progress_data.get("current_env_idx", 0)
531
+ environments = all_progress_data.get("environments", {})
532
+ # 加载当前环境的进度
533
+ if str(current_env_idx_from_file) in environments:
534
+ progress_data = environments[str(current_env_idx_from_file)]
535
+ else:
536
+ # 如果没有当前环境的进度,尝试加载第一个环境
537
+ if environments:
538
+ first_env_key = sorted(environments.keys())[0]
539
+ progress_data = environments[first_env_key]
540
+ else:
541
+ current_user_id = user_id
542
+ show_logic = False
543
+ progress_summary_text = get_user_progress_summary(user_id)
544
+ return f"⚠️ 进度文件格式错误", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
545
+ else:
546
+ # 旧格式:单个环境的进度
547
+ progress_data = all_progress_data
548
+
549
+ # 恢复数据
550
+ current_user_id = user_id
551
+ env_idx = progress_data.get("env_idx", 0)
552
+ env_idx_display = progress_data.get("env_idx_display", env_idx + 1)
553
+ bulb_states = progress_data.get("bulb_states", [])
554
+ history_records = progress_data.get("history", [])
555
+ level = progress_data.get("level", 0)
556
+
557
+ # 恢复环境的步数
558
+ num_steps = progress_data.get("num_steps", len(history_records))
559
+
560
+ # 获取逻辑规则
561
+ custom_logic = progress_data.get("custom_logic", {})
562
+
563
+ # 根据加载的数据是否包含逻辑规则来自动设置显示状态
564
+ # 如果数据中包含 custom_logic 且不为空,自动显示逻辑规则
565
+ # 如果数据中不包含 custom_logic 或为空,自动隐藏逻辑规则
566
+ has_logic_in_data = bool(custom_logic and len(custom_logic) > 0)
567
+ if has_logic_in_data:
568
+ show_logic = True
569
+ else:
570
+ show_logic = False
571
+
572
+ # 如果没有保存逻辑规则,从test_data中获取(用于环境恢复,但不影响显示状态)
573
+ if not custom_logic and env_idx < len(test_data):
574
+ custom_logic = test_data[env_idx].get("custom_logic", {})
575
+
576
+ # 检查 test_data 是否已加载
577
+ if not test_data:
578
+ current_user_id = user_id
579
+ show_logic = False
580
+ progress_summary_text = get_user_progress_summary(user_id)
581
+ return f"⚠️ 请先加载测试数据,然后再加载用户进度", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
582
+
583
+ # 恢复环境
584
+ if env_idx < len(test_data) and level > 0:
585
+ current_env_idx = env_idx
586
+ current_env = LightBulbEnv(custom_logic=custom_logic, num_bulbs=level)
587
+ # 恢复环境的步数
588
+ current_env.steps = num_steps
589
+ # 恢复灯泡状态
590
+ for i, state in enumerate(bulb_states):
591
+ if i < current_env.num_bulbs:
592
+ bulb_name = f"B{i}"
593
+ if bulb_name in current_env.bulbs:
594
+ current_env.bulbs[bulb_name] = state
595
+ else:
596
+ current_user_id = user_id
597
+ show_logic = False
598
+ progress_summary_text = get_user_progress_summary(user_id)
599
+ return f"⚠️ 环境数据无效,请重新初始化", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
600
+
601
+ # 格式化显示
602
+ if current_env is None:
603
+ current_user_id = user_id
604
+ show_logic = False
605
+ progress_summary_text = get_user_progress_summary(user_id)
606
+ return f"⚠️ 环境恢复失败,请重新初始化", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
607
+
608
+ obs = current_env._get_obs()
609
+ state_display = format_bulb_state(obs)
610
+
611
+ # 根据数据中是否包含逻辑规则来决定是否显示
612
+ # 如果数据中包含逻辑规则,自动显示;否则隐藏
613
+ # 注意:只显示原始数据中的逻辑规则,不从test_data中获取用于显示
614
+ if show_logic and has_logic_in_data:
615
+ logic_display = format_logic(progress_data.get("custom_logic", {}))
616
+ else:
617
+ logic_display = "(此进度文件不包含逻辑规则)"
618
+
619
+ history_display = "\n".join(history_records) if history_records else "无历史记录"
620
+
621
+ # 根据数据中是否包含逻辑规则来确定版本类型
622
+ version_type = "带逻辑规则" if has_logic_in_data else "不带逻辑规则"
623
+ info = f"✅ 已加载用户 {user_id} 的{version_type}版本进度\n"
624
+ info += f"环境: {env_idx_display}/{len(test_data)}\n"
625
+ info += f"步骤数: {len(history_records)}"
626
+ if show_logic:
627
+ info += "\n逻辑规则已自动显示"
628
+ else:
629
+ info += "\n逻辑规则已隐藏"
630
+
631
+ # 自动更新进度摘要
632
+ progress_summary_text = get_user_progress_summary(user_id)
633
+
634
+ # 返回数字类型,而不是字符串
635
+ return info, float(env_idx_display), state_display, logic_display, history_display, progress_summary_text
636
+
637
+ except Exception as e:
638
+ # 简化错误信息
639
+ error_msg = f"❌ 加载失败: {str(e)}"
640
+ progress_summary_text = get_user_progress_summary(user_id) if user_id else "点击'查看未完成问题'按钮查看进度"
641
+ return error_msg, 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
642
+
643
+
644
+ def set_user_id(user_id: str, with_logic: bool) -> Tuple[str, float, str, str, str, str]:
645
+ """设置用户 ID 并尝试加载进度
646
+ Args:
647
+ user_id: 用户ID
648
+ with_logic: 是否加载带逻辑规则的版本
649
+ Returns: (状态信息, 环境索引显示(数字), 灯泡状态, 逻辑规则, 历史记录, 进度摘要)
650
+ """
651
+ global current_user_id
652
+
653
+ try:
654
+ if not user_id or not str(user_id).strip():
655
+ progress_summary_text = "点击'查看未完成问题'按钮查看进度"
656
+ return "⚠️ 请输入有效的用户 ID", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
657
+
658
+ user_id = str(user_id).strip()
659
+ # 加载进度(根据with_logic选择对应的文件)
660
+ return load_progress(user_id, with_logic)
661
+ except Exception as e:
662
+ # 如果出错,至少设置用户ID
663
+ current_user_id = str(user_id).strip() if user_id else ""
664
+ progress_summary_text = get_user_progress_summary(current_user_id) if current_user_id else "点击'查看未完成问题'按钮查看进度"
665
+ return f"❌ 设置用户ID时出错: {str(e)}", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
666
+
667
+
668
+ def get_user_progress_summary(user_id: str) -> str:
669
+ """获取用户进度摘要,显示未完成的问题
670
+ Args:
671
+ user_id: 用户ID
672
+ Returns: 格式化的进度摘要字符串
673
+ """
674
+ global test_data
675
+
676
+ if not user_id or not user_id.strip():
677
+ return "⚠️ 请输入用户 ID"
678
+
679
+ user_id = user_id.strip()
680
+
681
+ # 检查两个版本的文件
682
+ completed_envs = set()
683
+ all_progress_files = []
684
+
685
+ # 检查带逻辑规则和不带逻辑规则的版本
686
+ for with_logic in [True, False]:
687
+ save_path = get_save_path(user_id, with_logic=with_logic)
688
+ if os.path.exists(save_path):
689
+ try:
690
+ with open(save_path, 'r', encoding='utf-8') as f:
691
+ all_progress_data = json.load(f)
692
+
693
+ # 处理新旧格式兼容
694
+ if "environments" in all_progress_data:
695
+ # 新格式:所有环境的进度保存在一个文件中
696
+ environments = all_progress_data.get("environments", {})
697
+ # 遍历所有环境的进度
698
+ for env_key, progress_data in environments.items():
699
+ env_idx = progress_data.get("env_idx", -1)
700
+ bulb_states = progress_data.get("bulb_states", [])
701
+ num_steps = progress_data.get("num_steps", 0)
702
+
703
+ # 检查是否完成:
704
+ # 1. 所有灯泡都点亮(成功完成)
705
+ # 2. 达到步骤上限(200步,算作完成但失败)
706
+ is_completed = False
707
+ if bulb_states and all(bulb_states):
708
+ is_completed = True # 成功完成
709
+ elif num_steps >= MAX_STEPS:
710
+ is_completed = True # 达到上限,算作完成(但失败)
711
+
712
+ if is_completed:
713
+ completed_envs.add(env_idx)
714
+
715
+ all_progress_files.append({
716
+ "env_idx": env_idx,
717
+ "env_idx_display": progress_data.get("env_idx_display", env_idx + 1),
718
+ "completed": is_completed,
719
+ "num_steps": num_steps,
720
+ "with_logic": with_logic
721
+ })
722
+ else:
723
+ # 旧格式:单个环境的进度
724
+ progress_data = all_progress_data
725
+ env_idx = progress_data.get("env_idx", -1)
726
+ bulb_states = progress_data.get("bulb_states", [])
727
+ num_steps = progress_data.get("num_steps", 0)
728
+
729
+ # 检查是否完成
730
+ is_completed = False
731
+ if bulb_states and all(bulb_states):
732
+ is_completed = True
733
+ elif num_steps >= MAX_STEPS:
734
+ is_completed = True
735
+
736
+ if is_completed:
737
+ completed_envs.add(env_idx)
738
+
739
+ all_progress_files.append({
740
+ "env_idx": env_idx,
741
+ "env_idx_display": progress_data.get("env_idx_display", env_idx + 1),
742
+ "completed": is_completed,
743
+ "num_steps": num_steps,
744
+ "with_logic": with_logic
745
+ })
746
+ except Exception:
747
+ pass
748
+
749
+ if not all_progress_files:
750
+ return f"ℹ️ 用户 {user_id} 还没有任何进度记录"
751
+
752
+ # 获取总环境数
753
+ total_envs = len(test_data) if test_data else 0
754
+ if total_envs == 0:
755
+ return "⚠️ 请先加载测试数据"
756
+
757
+ # 找出未完成的环境
758
+ all_env_indices = set(range(total_envs))
759
+ incomplete_envs = sorted(all_env_indices - completed_envs)
760
+
761
+ # 构建摘要信息
762
+ summary_lines = []
763
+ summary_lines.append(f"📊 用户 {user_id} 的进度摘要")
764
+ summary_lines.append(f"总环境数: {total_envs}")
765
+ summary_lines.append(f"已完成: {len(completed_envs)}/{total_envs}")
766
+ summary_lines.append(f"未完成: {len(incomplete_envs)}/{total_envs}")
767
+
768
+ if incomplete_envs:
769
+ summary_lines.append("\n❌ 未完成的环境:")
770
+ # 每行显示5个环境索引
771
+ for i in range(0, len(incomplete_envs), 5):
772
+ env_display_list = [str(env_idx + 1) for env_idx in incomplete_envs[i:i+5]]
773
+ summary_lines.append(" " + ", ".join(env_display_list))
774
+ else:
775
+ summary_lines.append("\n🎉 恭喜!所有环境都已完成!")
776
+
777
+ return "\n".join(summary_lines)
778
+
779
+
780
+ def extract_action_from_llm_output(text: str) -> str:
781
+ """从 LLM 输出中提取动作"""
782
+ m = re.search(r"<action>(.*?)</action>", text, re.IGNORECASE | re.DOTALL)
783
+ if m:
784
+ return m.group(1).strip()
785
+ return ""
786
+
787
+
788
+ def generate_prompt_for_llm(env: LightBulbEnv, history: List[str], feedback: str) -> str:
789
+ """生成 LLM 的输入 prompt"""
790
+ grid_text = env.return_obs()
791
+ history_text = "\n".join(history) if history else "无历史记录"
792
+
793
+ prompt = f"""
794
+ You are an intelligent agent.
795
+
796
+ ### Goal:
797
+ Your mission is to light on all the bulbs.
798
+ However, the accessibility of the bulbs is based on the current condition of other bulbs.
799
+ You need to learn the hidden rule behind the environment and complete the task.
800
+
801
+ ### Action Space:
802
+ The action space is based on the index of bulbs. For example, you would like to light on / off the first bulb, you should \
803
+ output <action>0</action> to toggle the state of the bulb.
804
+
805
+ ### History Action and Feedback:
806
+ {history_text}
807
+
808
+ ### Current State:
809
+ {grid_text}
810
+
811
+ Now think step by step and choose the next action to act in the environment.
812
+ You are encouraged to act actively to derive the environment dynamics.
813
+ Output ONLY one action in the format: <action>n</action>
814
+ """
815
+ return prompt.strip()
816
+
817
+ # ------------------- Gradio 界面 -------------------
818
+
819
+
820
+ def create_interface():
821
+ """创建 Gradio 界面"""
822
+
823
+ with gr.Blocks(title="灯泡环境交互界面", theme=gr.themes.Soft()) as demo:
824
+ gr.Markdown("""
825
+ # 💡 灯泡环境交互界面
826
+
827
+ 这是一个灯泡控制环境,你需要通过点击灯泡来点亮所有灯泡。
828
+ 每个灯泡的可用性取决于其他灯泡的状态。
829
+ """)
830
+
831
+ with gr.Row():
832
+ with gr.Column(scale=1):
833
+ gr.Markdown("### 📝 逻辑规则")
834
+ logic_display = gr.Textbox(
835
+ label="依赖逻辑",
836
+ interactive=False,
837
+ lines=10,
838
+ value="逻辑规则将根据加载的数据自动显示或隐藏"
839
+ )
840
+
841
+ gr.Markdown("### 👤 用户信息")
842
+ user_id_input = gr.Textbox(
843
+ label="用户 ID",
844
+ placeholder="请输入您的用户 ID",
845
+ info="输入您的 ID 以保存和加载进度"
846
+ )
847
+ load_with_logic = gr.Checkbox(
848
+ label="加载带逻辑规则的版本",
849
+ value=False,
850
+ info="勾选则加载带逻辑规则的进度文件,否则加载不带逻辑规则的进度文件"
851
+ )
852
+ load_user_btn = gr.Button("加载用户进度", variant="primary")
853
+ check_progress_btn = gr.Button("查看未完成问题", variant="secondary")
854
+ progress_summary = gr.Textbox(
855
+ label="进度摘要",
856
+ interactive=False,
857
+ lines=10,
858
+ value="点击'查看未完成问题'按钮查看进度"
859
+ )
860
+
861
+ gr.Markdown("### 🎮 环境控制")
862
+ # 动态更新最大值(最多30个环境)
863
+ max_envs = min(30, len(test_data)) if test_data else 30
864
+ env_idx_input = gr.Number(
865
+ label="环境索引",
866
+ value=1,
867
+ minimum=1,
868
+ maximum=max_envs,
869
+ precision=0,
870
+ info=f"选择要加载的环境 (1-{max_envs})"
871
+ )
872
+ init_btn = gr.Button("加载环境", variant="primary")
873
+ reset_btn = gr.Button("重置环境")
874
+
875
+ env_info = gr.Textbox(label="环境信息", interactive=False, lines=5)
876
+
877
+ with gr.Column(scale=2):
878
+ gr.Markdown("### 💡 当前状态")
879
+ state_display = gr.Textbox(
880
+ label="灯泡状态",
881
+ interactive=False,
882
+ lines=3,
883
+ value="请先加载环境"
884
+ )
885
+
886
+ gr.Markdown("### 🎯 动作输入")
887
+ action_input = gr.Textbox(
888
+ label="输入动作 (灯泡索引)",
889
+ placeholder="例如: 0",
890
+ info="输入要切换的灯泡索引 (0 开始)"
891
+ )
892
+ step_btn = gr.Button("执行动作", variant="primary")
893
+
894
+ gr.Markdown("### 💬 环境反馈")
895
+ feedback_display = gr.Textbox(
896
+ label="反馈信息",
897
+ interactive=False,
898
+ lines=5
899
+ )
900
+
901
+ gr.Markdown("### 📜 操作历史")
902
+ history_display = gr.Textbox(
903
+ label="操作历史",
904
+ interactive=False,
905
+ lines=10
906
+ )
907
+
908
+ # 事件绑定
909
+ # 加载用户进度 - 添加错误处理包装
910
+ def load_user_wrapper(user_id, with_logic_flag):
911
+ """包装函数,处理用户ID加载,添加错误处理"""
912
+ try:
913
+ if not user_id or not str(user_id).strip():
914
+ progress_summary_text = "点击'查看未完成问题'按钮查看进度"
915
+ return "⚠️ 请输入有效的用户 ID", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
916
+ result = set_user_id(str(user_id).strip(), bool(with_logic_flag))
917
+ # 确保返回6个值,且第二个值是数字
918
+ if not isinstance(result, tuple) or len(result) != 6:
919
+ progress_summary_text = get_user_progress_summary(str(user_id).strip()) if user_id else "点击'查看未完成问题'按钮查看进度"
920
+ return "❌ 加载函数返回格式错误", 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
921
+ # 确保第二个值是数字类型
922
+ if not isinstance(result[1], (int, float)):
923
+ result = (result[0], float(result[1]) if result[1] else 1.0, result[2], result[3], result[4], result[5])
924
+ return result
925
+ except Exception as e:
926
+ # 简化错误信息
927
+ error_msg = f"❌ 加载用户进度时出错: {str(e)}"
928
+ progress_summary_text = get_user_progress_summary(str(user_id).strip()) if user_id else "点击'查看未完成问题'按钮查看进度"
929
+ return error_msg, 1.0, "请先初始化环境", "(暂无逻辑规则显示)", "", progress_summary_text
930
+
931
+ load_user_btn.click(
932
+ fn=load_user_wrapper,
933
+ inputs=[user_id_input, load_with_logic],
934
+ outputs=[env_info, env_idx_input, state_display, logic_display, history_display, progress_summary]
935
+ )
936
+
937
+ # 用户ID输入框回车键
938
+ user_id_input.submit(
939
+ fn=load_user_wrapper,
940
+ inputs=[user_id_input, load_with_logic],
941
+ outputs=[env_info, env_idx_input, state_display, logic_display, history_display, progress_summary]
942
+ )
943
+
944
+ # 查看未完成问题
945
+ def check_progress_wrapper(user_id):
946
+ """包装函数,检查用户进度"""
947
+ try:
948
+ if not user_id or not str(user_id).strip():
949
+ return "⚠️ 请输入用户 ID"
950
+ return get_user_progress_summary(str(user_id).strip())
951
+ except Exception as e:
952
+ return f"❌ 检查进度时出错: {str(e)}"
953
+
954
+ check_progress_btn.click(
955
+ fn=check_progress_wrapper,
956
+ inputs=user_id_input,
957
+ outputs=progress_summary
958
+ )
959
+
960
+ # 用户ID输入框回车键也可以触发进度检查(但优先加载进度)
961
+ # 这里不添加,避免冲突
962
+
963
+ init_btn.click(
964
+ fn=load_environment,
965
+ inputs=env_idx_input,
966
+ outputs=[env_info, state_display, logic_display, history_display, progress_summary]
967
+ )
968
+
969
+ reset_btn.click(
970
+ fn=reset_environment,
971
+ outputs=[env_info, state_display, history_display, progress_summary]
972
+ )
973
+
974
+ def step_wrapper(action_str):
975
+ feedback, state, history, done = step_environment(action_str)
976
+ # 如果完成,更新环境信息(显示索引从1开始)
977
+ if done:
978
+ env_info_text = f"🎉 任务完成!所有灯泡已点亮!\n环境索引: {current_env_idx + 1}/{len(test_data) if test_data else 0}"
979
+ else:
980
+ env_info_text = f"环境索引: {current_env_idx + 1}/{len(test_data) if test_data else 0}\n步骤数: {len(history_records)}"
981
+ return feedback, state, history, env_info_text
982
+
983
+ step_btn.click(
984
+ fn=step_wrapper,
985
+ inputs=action_input,
986
+ outputs=[feedback_display, state_display,
987
+ history_display, env_info]
988
+ )
989
+
990
+ # 支持回车键执行
991
+ action_input.submit(
992
+ fn=step_wrapper,
993
+ inputs=action_input,
994
+ outputs=[feedback_display, state_display,
995
+ history_display, env_info]
996
+ )
997
+
998
+ gr.Markdown("""
999
+ ### 📖 使用说明
1000
+
1001
+ 1. **输入用户 ID**: 在"用户 ID"框中输入您的 ID,点击"加载用户进度"(如果有保存的进度会自动加载)
1002
+ 2. **加载环境**: 输入环境索引(1-30),点击"加载环境"。如果有保存的进度会自动加载,否则初始化新环境(测试数据已自动加载)
1003
+ 3. **执行动作**: 在"输入动作"框中输入灯泡索引(0 开始),点击"执行动作"或按回车
1004
+ 4. **查看状态**: 观察灯泡状态变化和反馈信息
1005
+ 5. **重置环境**: 点击"重置环境"可以重新开始当前环境
1006
+ 6. **查看进度**: 点击"查看未完成问题"可以查看您的完成情况
1007
+
1008
+ **进度保存**:
1009
+ - 每次执行动作后会自动保存进度
1010
+ - 下次输入相同的用户 ID 可以继续之前的进度
1011
+ - 进度保存在 `user_progress/` 目录下
1012
+
1013
+ **提示**:
1014
+ - 💡 表示灯泡已点亮
1015
+ - ○ 表示灯泡未点亮
1016
+ - 每个灯泡的可用性取决于其他灯泡的状态(见逻辑规则)
1017
+ """)
1018
+
1019
+ return demo
1020
+
1021
+
1022
+ # ------------------- 主函数 -------------------
1023
+ if __name__ == "__main__":
1024
+ # 自动加载测试数据
1025
+ load_test_data()
1026
+
1027
+ demo = create_interface()
1028
+ # 适配 Hugging Face Spaces 和本地运行
1029
+ # 如果在 Hugging Face Spaces 上运行,不设置 server_name 和 server_port
1030
+ # 如果在本地运行,使用 127.0.0.1 或 localhost
1031
+ if os.getenv("SPACE_ID") is None:
1032
+ # 本地运行
1033
+ demo.launch(
1034
+ server_name="127.0.0.1",
1035
+ server_port=7860,
1036
+ share=False
1037
+ )
1038
+ else:
1039
+ # Hugging Face Spaces 运行
1040
+ demo.launch()
app.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Hugging Face Spaces 入口文件
3
+ 这个文件是 Hugging Face Spaces 的标准入口点
4
+ """
5
+ import os
6
+ import sys
7
+
8
+ # 设置工作目录
9
+ script_dir = os.path.dirname(os.path.abspath(__file__))
10
+ os.chdir(script_dir)
11
+
12
+ # 添加 LightEnv 目录到 Python 路径
13
+ lightenv_path = os.path.join(script_dir, "LightEnv")
14
+ if os.path.exists(lightenv_path):
15
+ sys.path.insert(0, lightenv_path)
16
+
17
+ # 修复 huggingface_hub 兼容性(必须在导入 gradio 之前)
18
+ def _fix_huggingface_hub():
19
+ """修复 huggingface_hub 兼容性问题"""
20
+ try:
21
+ import huggingface_hub
22
+ if not hasattr(huggingface_hub, 'HfFolder'):
23
+ class HfFolder:
24
+ @staticmethod
25
+ def save_token(token):
26
+ pass
27
+ @staticmethod
28
+ def get_token():
29
+ return None
30
+ @staticmethod
31
+ def get_token_path():
32
+ return None
33
+ huggingface_hub.HfFolder = HfFolder
34
+ if hasattr(huggingface_hub, '__all__'):
35
+ if 'HfFolder' not in huggingface_hub.__all__:
36
+ huggingface_hub.__all__.append('HfFolder')
37
+ except Exception:
38
+ pass
39
+
40
+ _fix_huggingface_hub()
41
+
42
+ # 导入 GUI-Light 模块
43
+ # 由于文件名包含连字符,需要使用 importlib
44
+ import importlib.util
45
+ gui_light_path = os.path.join(script_dir, "GUI-Light.py")
46
+ spec = importlib.util.spec_from_file_location("gui_light", gui_light_path)
47
+ gui_light = importlib.util.module_from_spec(spec)
48
+ sys.modules["gui_light"] = gui_light
49
+ spec.loader.exec_module(gui_light)
50
+
51
+ # 加载测试数据
52
+ gui_light.load_test_data()
53
+
54
+ # 创建 Gradio 应用
55
+ demo = gui_light.create_interface()
56
+
57
+ # Hugging Face Spaces 会自动调用 demo.launch()
58
+ # 但为了确保兼容性,我们也可以显式调用
59
+ if __name__ == "__main__":
60
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ numpy
3
+ huggingface_hub>=0.20.0
4
+ requests
5
+
6
+
7
+