question.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. from typing import List, Optional, Dict, Any
  2. import random
  3. class Question:
  4. """
  5. 问题类,用于生成不同类型的24点题目
  6. """
  7. # 缓存数据
  8. _cache = {
  9. 'A': [], # 推理未知数题目
  10. 'B': [], # 恰好有两个解法的题目
  11. 'C': [], # 有三个或以上解法的题目
  12. 'D': [], # 包含flag=1的题目
  13. 'E': [] # 包含flag=2的题目
  14. }
  15. _is_cache_loaded = False
  16. def __init__(self, q_type: str):
  17. self.type = q_type
  18. self.question = ""
  19. self.numbers: List[Optional[int]] = []
  20. self.answers: List[Any] = []
  21. # 确保缓存已加载
  22. if not Question._is_cache_loaded:
  23. Question._load_cache()
  24. @classmethod
  25. def _load_cache(cls):
  26. """
  27. 从core.load模块加载缓存数据并按题型和特性分类
  28. """
  29. try:
  30. # 初始化缓存结构
  31. cls._cache = {
  32. 'A': [], # 推理未知数题目
  33. 'B': [], # 恰好有两个解法的题目
  34. 'C': [], # 有三个或以上解法的题目
  35. 'D': [], # 包含flag=1的题目
  36. 'E': [] # 包含flag=2的题目
  37. }
  38. # 从core.load模块获取数据
  39. from core.load import all_data
  40. data = all_data
  41. # 分类处理数据
  42. for item in data:
  43. # 从GameDataDTO构建numbers数组
  44. numbers = [item.num1, item.num2, item.num3, item.num4]
  45. solutions = item.solutions
  46. # 转换解法格式
  47. formatted_solutions = []
  48. for sol in solutions:
  49. formatted_solutions.append({
  50. 'expression': sol.expression,
  51. 'flag': sol.flag
  52. })
  53. # 构建标准格式的题目数据
  54. formatted_item = {
  55. 'id': item.id,
  56. 'no': item.no,
  57. 'numbers': numbers,
  58. 'solutions': formatted_solutions
  59. }
  60. if not solutions:
  61. continue
  62. # 计算解法数量和最高flag值
  63. solution_count = len(solutions)
  64. max_flag = max([sol.flag for sol in solutions], default=0)
  65. # 分类存储
  66. # 类型A:推理未知数题目 - 只有4个数字中有两个相同的题目才可以用于类型A
  67. # 检查numbers中是否有两个相同的数字
  68. num_count = {}
  69. for num in numbers:
  70. num_count[num] = num_count.get(num, 0) + 1
  71. has_duplicate = any(count == 2 for count in num_count.values())
  72. if solution_count > 0 and has_duplicate:
  73. cls._cache['A'].append(formatted_item)
  74. # 类型B:恰好有两个解法的题目
  75. if solution_count == 2:
  76. cls._cache['B'].append(formatted_item)
  77. # 类型C:有三个或以上解法的题目
  78. if solution_count >= 3:
  79. cls._cache['C'].append(formatted_item)
  80. # 类型D:包含flag=1的题目
  81. if max_flag == 1:
  82. cls._cache['D'].append(formatted_item)
  83. # 类型E:包含flag=2的题目
  84. if max_flag == 2:
  85. cls._cache['E'].append(formatted_item)
  86. cls._is_cache_loaded = True
  87. print(f"缓存加载完成,各类型题目数量:A:{len(cls._cache['A'])}, B:{len(cls._cache['B'])}, C:{len(cls._cache['C'])}, D:{len(cls._cache['D'])}, E:{len(cls._cache['E'])}")
  88. except Exception as e:
  89. print(f"加载缓存数据失败: {e}")
  90. cls._is_cache_loaded = False
  91. def generate(self) -> Dict[str, Any]:
  92. """
  93. 根据题型生成题目
  94. """
  95. if self.type == "A":
  96. data = self._generate_type_a()
  97. data['type'] = "A"
  98. elif self.type == "B":
  99. data = self._generate_type_b()
  100. data['type'] = "B"
  101. elif self.type == "C":
  102. data = self._generate_type_c()
  103. data['type'] = "C"
  104. elif self.type == "D":
  105. data = self._generate_type_d()
  106. data['type'] = "D"
  107. elif self.type == "E":
  108. data = self._generate_type_e()
  109. data['type'] = "E"
  110. else:
  111. self.type= random.choice(["A", "B", "C", "D", "E"])
  112. data = self.generate()
  113. return data
  114. @classmethod
  115. def _generate_type_a(cls) -> Dict[str, Any]:
  116. """
  117. 推理未知数:给出四个数字(1-13)算24,其中一个是x,并且x等于另外三个数(这三个数不能相同)中的一个。
  118. 要求找出x的可能值,并写出相应的算式
  119. """
  120. if not cls._is_cache_loaded:
  121. cls._load_cache()
  122. if not cls._cache['A']:
  123. raise ValueError("缓存中没有可用的A类型题目数据")
  124. # 从缓存中随机选择一个题目
  125. item = random.choice(cls._cache['A'])
  126. original_numbers = item.get('numbers', [])
  127. solutions = item.get('solutions', [])
  128. if len(original_numbers) != 4 or not solutions:
  129. raise ValueError("缓存数据格式不正确")
  130. # 找出重复的数字和不重复的数字
  131. num_count = {}
  132. for num in original_numbers:
  133. num_count[num] = num_count.get(num, 0) + 1
  134. # 找出重复的数字(出现次数为2的数字)
  135. duplicate_num = None
  136. for num, count in num_count.items():
  137. if count == 2:
  138. duplicate_num = num
  139. break
  140. if duplicate_num is None:
  141. raise ValueError("A类型题目数据中没有重复的数字")
  142. # 收集三个不同的数字(包括重复的那个数字)
  143. distinct_nums = []
  144. for num in num_count.keys():
  145. if num not in distinct_nums:
  146. distinct_nums.append(num)
  147. if len(distinct_nums) >= 3:
  148. break
  149. # 随机选择一个位置放置未知数
  150. unknown_pos = random.randint(0, 3)
  151. numbers = distinct_nums.copy()
  152. numbers.insert(unknown_pos, None) # None 表示未知数 x
  153. # 构造答案
  154. answers = []
  155. for possible_x in distinct_nums:
  156. # 构建排序后的测试数字组合
  157. test_numbers = sorted([n if n is not None else possible_x for n in numbers])
  158. test_no = f"{test_numbers[0]}_{test_numbers[1]}_{test_numbers[2]}_{test_numbers[3]}"
  159. # 收集当前x值的所有解法
  160. for item in cls._cache['A']:
  161. if item.get('no', "") == test_no:
  162. solution = item.get('solutions', [])
  163. # 使用正确的 join 方法
  164. solution_str = ", ".join([sol['expression'] for sol in solution])
  165. answers.append(f"当X={possible_x}时,解法为:{solution_str}")
  166. # 构造问题文本
  167. question_text = "推理未知数:给出四个数字算24点,其中一个是未知数x,且x等于另外三个数中的一个。请找出x的可能值,并写出对应算式。"
  168. if len(answers)>1:
  169. question_text += f"(共有{len(answers)}个答案,请写全)"
  170. return {
  171. "question": question_text,
  172. "numbers": numbers,
  173. "answers": answers
  174. }
  175. @classmethod
  176. def _generate_type_b(cls) -> Dict[str, Any]:
  177. """
  178. 一题多解1:生成恰好有两个解法的题目
  179. """
  180. if not cls._is_cache_loaded:
  181. cls._load_cache()
  182. if not cls._cache['B']:
  183. raise ValueError("缓存中没有可用的B类型题目数据")
  184. # 从缓存中随机选择一个题目
  185. item = random.choice(cls._cache['B'])
  186. numbers = item.get('numbers', [])
  187. solutions = item.get('solutions', [])
  188. question_text = "一题多解:请找出这组数字的所有24点解法。"
  189. # 转换解法格式
  190. answers = []
  191. for solution in solutions:
  192. answers.append({
  193. "expression": solution.get('expression', ''),
  194. "flag": solution.get('flag', 0)
  195. })
  196. return {
  197. "question": question_text,
  198. "numbers": numbers,
  199. "answers": answers
  200. }
  201. @classmethod
  202. def _generate_type_c(cls) -> Dict[str, Any]:
  203. """
  204. 一题多解2:生成有三个或以上解法的题目
  205. """
  206. if not cls._is_cache_loaded:
  207. cls._load_cache()
  208. if not cls._cache['C']:
  209. raise ValueError("缓存中没有可用的C类型题目数据")
  210. # 从缓存中随机选择一个题目
  211. item = random.choice(cls._cache['C'])
  212. numbers = item.get('numbers', [])
  213. solutions = item.get('solutions', [])
  214. question_text = "一题多解:请找出这组数字的所有24点解法!"
  215. # 转换解法格式
  216. answers = []
  217. for solution in solutions:
  218. answers.append({
  219. "expression": solution.get('expression', ''),
  220. "flag": solution.get('flag', 0)
  221. })
  222. return {
  223. "question": question_text,
  224. "numbers": numbers,
  225. "answers": answers
  226. }
  227. @classmethod
  228. def _generate_type_d(cls) -> Dict[str, Any]:
  229. """
  230. 一星题目:生成包含flag=1的题目
  231. """
  232. if not cls._is_cache_loaded:
  233. cls._load_cache()
  234. if not cls._cache['D']:
  235. raise ValueError("缓存中没有可用的D类型题目数据")
  236. # 从缓存中随机选择一个题目
  237. item = random.choice(cls._cache['D'])
  238. numbers = item.get('numbers', [])
  239. solutions = item.get('solutions', [])
  240. question_text = "一星题目:请找出这组数字的24点解法。"
  241. # 转换解法格式,只保留flag=1的解法
  242. answers = []
  243. for solution in solutions:
  244. if solution.get('flag', 0) == 1:
  245. answers.append({
  246. "expression": solution.get('expression', ''),
  247. "flag": 1
  248. })
  249. return {
  250. "question": question_text,
  251. "numbers": numbers,
  252. "answers": answers
  253. }
  254. @classmethod
  255. def _generate_type_e(cls) -> Dict[str, Any]:
  256. """
  257. 二星题目:生成包含flag=2的题目
  258. """
  259. if not cls._is_cache_loaded:
  260. cls._load_cache()
  261. if not cls._cache['E']:
  262. raise ValueError("缓存中没有可用的E类型题目数据")
  263. # 从缓存中随机选择一个题目
  264. item = random.choice(cls._cache['E'])
  265. numbers = item.get('numbers', [])
  266. solutions = item.get('solutions', [])
  267. question_text = "二星题目:请找出这组数字的24点解法。"
  268. # 转换解法格式,只保留flag=2的解法
  269. answers = []
  270. for solution in solutions:
  271. if solution.get('flag', 0) == 2:
  272. answers.append({
  273. "expression": solution.get('expression', ''),
  274. "flag": 2
  275. })
  276. return {
  277. "question": question_text,
  278. "numbers": numbers,
  279. "answers": answers
  280. }