question.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. valid_types = ["A", "B", "C", "D", "E"]
  96. # 如果类型无效,随机选择一个有效类型
  97. if self.type not in valid_types:
  98. self.type = random.choice(valid_types)
  99. return self.generate()
  100. # 调用对应类型的生成方法
  101. generator_method = getattr(self, f"_generate_type_{self.type.lower()}")
  102. data = generator_method()
  103. data['type'] = self.type
  104. return data
  105. @classmethod
  106. def _ensure_cache_loaded(cls, cache_type: str) -> None:
  107. """
  108. 确保缓存已加载,并验证指定类型的缓存是否有可用数据
  109. """
  110. if not cls._is_cache_loaded:
  111. cls._load_cache()
  112. if not cls._cache[cache_type]:
  113. raise ValueError(f"缓存中没有可用的{cache_type}类型题目数据")
  114. @classmethod
  115. def _get_random_item(cls, cache_type: str) -> Dict[str, Any]:
  116. """
  117. 从指定类型的缓存中随机获取一个题目
  118. """
  119. cls._ensure_cache_loaded(cache_type)
  120. return random.choice(cls._cache[cache_type])
  121. @classmethod
  122. def _format_solutions(cls, solutions: List[Dict], flag_value: Optional[int] = None) -> List[Dict]:
  123. """
  124. 统一格式化解法数据
  125. 如果指定了flag_value,则只保留该flag值的解法
  126. """
  127. answers = []
  128. for solution in solutions:
  129. # 如果指定了flag值且当前解法的flag不匹配,则跳过
  130. if flag_value is not None and solution.get('flag', 0) != flag_value:
  131. continue
  132. answers.append({
  133. "expression": solution.get('expression', ''),
  134. "flag": flag_value if flag_value is not None else solution.get('flag', 0)
  135. })
  136. return answers
  137. @classmethod
  138. def _create_question_result(cls, question_text: str, numbers: List, answers: List) -> Dict[str, Any]:
  139. """
  140. 创建标准格式的问题结果对象
  141. """
  142. return {
  143. "question": question_text,
  144. "numbers": numbers,
  145. "answers": answers
  146. }
  147. @classmethod
  148. def _generate_type_a(cls) -> Dict[str, Any]:
  149. """
  150. 推理未知数:给出四个数字(1-13)算24,其中一个是x,并且x等于另外三个数(这三个数不能相同)中的一个。
  151. 要求找出x的可能值,并写出相应的算式
  152. """
  153. # 获取随机题目
  154. item = cls._get_random_item('A')
  155. original_numbers = item.get('numbers', [])
  156. solutions = item.get('solutions', [])
  157. if len(original_numbers) != 4 or not solutions:
  158. raise ValueError("缓存数据格式不正确")
  159. # 找出重复的数字和不重复的数字
  160. num_count = {}
  161. for num in original_numbers:
  162. num_count[num] = num_count.get(num, 0) + 1
  163. # 找出重复的数字(出现次数为2的数字)
  164. duplicate_num = None
  165. for num, count in num_count.items():
  166. if count == 2:
  167. duplicate_num = num
  168. break
  169. if duplicate_num is None:
  170. raise ValueError("A类型题目数据中没有重复的数字")
  171. # 收集三个不同的数字(包括重复的那个数字)
  172. distinct_nums = []
  173. for num in num_count.keys():
  174. if num not in distinct_nums:
  175. distinct_nums.append(num)
  176. if len(distinct_nums) >= 3:
  177. break
  178. # 随机选择一个位置放置未知数
  179. unknown_pos = random.randint(0, 3)
  180. numbers = distinct_nums.copy()
  181. numbers.insert(unknown_pos, None) # None 表示未知数 x
  182. # 构造答案
  183. answers = []
  184. for possible_x in distinct_nums:
  185. # 构建排序后的测试数字组合
  186. test_numbers = sorted([n if n is not None else possible_x for n in numbers])
  187. test_no = f"{test_numbers[0]}_{test_numbers[1]}_{test_numbers[2]}_{test_numbers[3]}"
  188. # 收集当前x值的所有解法
  189. for item in cls._cache['A']:
  190. if item.get('no', "") == test_no:
  191. solution = item.get('solutions', [])
  192. # 使用正确的 join 方法
  193. solution_str = ", ".join([sol['expression'] for sol in solution])
  194. answers.append(f"当X={possible_x}时,解法为:{solution_str}")
  195. # 构造问题文本
  196. question_text = "推理未知数:给出四个数字算24点,其中一个是未知数x,且x等于另外三个数中的一个。请找出x的可能值,并写出对应算式。"
  197. if len(answers)>1:
  198. question_text += f"(共有{len(answers)}个答案,请写全)"
  199. return cls._create_question_result(question_text, numbers, answers)
  200. @classmethod
  201. def _generate_type_b(cls) -> Dict[str, Any]:
  202. """
  203. 一题多解1:生成恰好有两个解法的题目
  204. """
  205. # 获取随机题目
  206. item = cls._get_random_item('B')
  207. numbers = item.get('numbers', [])
  208. solutions = item.get('solutions', [])
  209. question_text = "一题多解:请找出这组数字的所有24点解法。"
  210. # 格式化解法
  211. answers = cls._format_solutions(solutions)
  212. return cls._create_question_result(question_text, numbers, answers)
  213. @classmethod
  214. def _generate_type_c(cls) -> Dict[str, Any]:
  215. """
  216. 一题多解2:生成有三个或以上解法的题目
  217. """
  218. # 获取随机题目
  219. item = cls._get_random_item('C')
  220. numbers = item.get('numbers', [])
  221. solutions = item.get('solutions', [])
  222. question_text = "一题多解:请找出这组数字的所有24点解法!"
  223. # 格式化解法
  224. answers = cls._format_solutions(solutions)
  225. return cls._create_question_result(question_text, numbers, answers)
  226. @classmethod
  227. def _generate_type_d(cls) -> Dict[str, Any]:
  228. """
  229. 一星题目:生成包含flag=1的题目
  230. """
  231. # 获取随机题目
  232. item = cls._get_random_item('D')
  233. numbers = item.get('numbers', [])
  234. solutions = item.get('solutions', [])
  235. question_text = "【一星】题目:请找出这组数字的24点解法。"
  236. # 格式化解法,只保留flag=1的解法
  237. answers = cls._format_solutions(solutions, flag_value=1)
  238. return cls._create_question_result(question_text, numbers, answers)
  239. @classmethod
  240. def _generate_type_e(cls) -> Dict[str, Any]:
  241. """
  242. 二星题目:生成包含flag=2的题目
  243. """
  244. # 获取随机题目
  245. item = cls._get_random_item('E')
  246. numbers = item.get('numbers', [])
  247. solutions = item.get('solutions', [])
  248. question_text = "【二星】题目:请找出这组数字的24点解法。"
  249. # 格式化解法,只保留flag=2的解法
  250. answers = cls._format_solutions(solutions, flag_value=2)
  251. return cls._create_question_result(question_text, numbers, answers)