| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- from typing import List, Optional, Dict, Any
- import random
- class Question:
- """
- 问题类,用于生成不同类型的24点题目
- """
- # 缓存数据
- _cache = {
- 'A': [], # 推理未知数题目
- 'B': [], # 恰好有两个解法的题目
- 'C': [], # 有三个或以上解法的题目
- 'D': [], # 包含flag=1的题目
- 'E': [] # 包含flag=2的题目
- }
- _is_cache_loaded = False
-
- def __init__(self, q_type: str):
- self.type = q_type
- self.question = ""
- self.numbers: List[Optional[int]] = []
- self.answers: List[Any] = []
-
- # 确保缓存已加载
- if not Question._is_cache_loaded:
- Question._load_cache()
- @classmethod
- def _load_cache(cls):
- """
- 从core.load模块加载缓存数据并按题型和特性分类
- """
- try:
- # 初始化缓存结构
- cls._cache = {
- 'A': [], # 推理未知数题目
- 'B': [], # 恰好有两个解法的题目
- 'C': [], # 有三个或以上解法的题目
- 'D': [], # 包含flag=1的题目
- 'E': [] # 包含flag=2的题目
- }
-
- # 从core.load模块获取数据
- from core.load import all_data
- data = all_data
-
- # 分类处理数据
- for item in data:
- # 从GameDataDTO构建numbers数组
- numbers = [item.num1, item.num2, item.num3, item.num4]
- solutions = item.solutions
-
- # 转换解法格式
- formatted_solutions = []
- for sol in solutions:
- formatted_solutions.append({
- 'expression': sol.expression,
- 'flag': sol.flag
- })
-
- # 构建标准格式的题目数据
- formatted_item = {
- 'id': item.id,
- 'no': item.no,
- 'numbers': numbers,
- 'solutions': formatted_solutions
- }
-
- if not solutions:
- continue
-
- # 计算解法数量和最高flag值
- solution_count = len(solutions)
- max_flag = max([sol.flag for sol in solutions], default=0)
-
- # 分类存储
- # 类型A:推理未知数题目 - 只有4个数字中有两个相同的题目才可以用于类型A
- # 检查numbers中是否有两个相同的数字
- num_count = {}
- for num in numbers:
- num_count[num] = num_count.get(num, 0) + 1
-
- has_duplicate = any(count == 2 for count in num_count.values())
- if solution_count > 0 and has_duplicate:
- cls._cache['A'].append(formatted_item)
-
- # 类型B:恰好有两个解法的题目
- if solution_count == 2:
- cls._cache['B'].append(formatted_item)
-
- # 类型C:有三个或以上解法的题目
- if solution_count >= 3:
- cls._cache['C'].append(formatted_item)
-
- # 类型D:包含flag=1的题目
- if max_flag == 1:
- cls._cache['D'].append(formatted_item)
-
- # 类型E:包含flag=2的题目
- if max_flag == 2:
- cls._cache['E'].append(formatted_item)
-
- cls._is_cache_loaded = True
- 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'])}")
-
- except Exception as e:
- print(f"加载缓存数据失败: {e}")
- cls._is_cache_loaded = False
-
- def generate(self) -> Dict[str, Any]:
- """
- 根据题型生成题目
- """
- valid_types = ["A", "B", "C", "D", "E"]
-
- # 如果类型无效,随机选择一个有效类型
- if self.type not in valid_types:
- self.type = random.choice(valid_types)
- return self.generate()
-
- # 调用对应类型的生成方法
- generator_method = getattr(self, f"_generate_type_{self.type.lower()}")
- data = generator_method()
- data['type'] = self.type
-
- return data
- @classmethod
- def _ensure_cache_loaded(cls, cache_type: str) -> None:
- """
- 确保缓存已加载,并验证指定类型的缓存是否有可用数据
- """
- if not cls._is_cache_loaded:
- cls._load_cache()
-
- if not cls._cache[cache_type]:
- raise ValueError(f"缓存中没有可用的{cache_type}类型题目数据")
-
- @classmethod
- def _get_random_item(cls, cache_type: str) -> Dict[str, Any]:
- """
- 从指定类型的缓存中随机获取一个题目
- """
- cls._ensure_cache_loaded(cache_type)
- return random.choice(cls._cache[cache_type])
-
- @classmethod
- def _format_solutions(cls, solutions: List[Dict], flag_value: Optional[int] = None) -> List[Dict]:
- """
- 统一格式化解法数据
- 如果指定了flag_value,则只保留该flag值的解法
- """
- answers = []
- for solution in solutions:
- # 如果指定了flag值且当前解法的flag不匹配,则跳过
- if flag_value is not None and solution.get('flag', 0) != flag_value:
- continue
-
- answers.append({
- "expression": solution.get('expression', ''),
- "flag": flag_value if flag_value is not None else solution.get('flag', 0)
- })
- return answers
-
- @classmethod
- def _create_question_result(cls, question_text: str, numbers: List, answers: List) -> Dict[str, Any]:
- """
- 创建标准格式的问题结果对象
- """
- return {
- "question": question_text,
- "numbers": numbers,
- "answers": answers
- }
-
- @classmethod
- def _generate_type_a(cls) -> Dict[str, Any]:
- """
- 推理未知数:给出四个数字(1-13)算24,其中一个是x,并且x等于另外三个数(这三个数不能相同)中的一个。
- 要求找出x的可能值,并写出相应的算式
- """
- # 获取随机题目
- item = cls._get_random_item('A')
-
- original_numbers = item.get('numbers', [])
- solutions = item.get('solutions', [])
-
- if len(original_numbers) != 4 or not solutions:
- raise ValueError("缓存数据格式不正确")
-
- # 找出重复的数字和不重复的数字
- num_count = {}
- for num in original_numbers:
- num_count[num] = num_count.get(num, 0) + 1
-
- # 找出重复的数字(出现次数为2的数字)
- duplicate_num = None
- for num, count in num_count.items():
- if count == 2:
- duplicate_num = num
- break
-
- if duplicate_num is None:
- raise ValueError("A类型题目数据中没有重复的数字")
-
- # 收集三个不同的数字(包括重复的那个数字)
- distinct_nums = []
- for num in num_count.keys():
- if num not in distinct_nums:
- distinct_nums.append(num)
- if len(distinct_nums) >= 3:
- break
-
- # 随机选择一个位置放置未知数
- unknown_pos = random.randint(0, 3)
- numbers = distinct_nums.copy()
- numbers.insert(unknown_pos, None) # None 表示未知数 x
-
- # 构造答案
- answers = []
- for possible_x in distinct_nums:
- # 构建排序后的测试数字组合
- test_numbers = sorted([n if n is not None else possible_x for n in numbers])
- test_no = f"{test_numbers[0]}_{test_numbers[1]}_{test_numbers[2]}_{test_numbers[3]}"
- # 收集当前x值的所有解法
- for item in cls._cache['A']:
- if item.get('no', "") == test_no:
- solution = item.get('solutions', [])
- # 使用正确的 join 方法
- solution_str = ", ".join([sol['expression'] for sol in solution])
- answers.append(f"当X={possible_x}时,解法为:{solution_str}")
- # 构造问题文本
- question_text = "推理未知数:给出四个数字算24点,其中一个是未知数x,且x等于另外三个数中的一个。请找出x的可能值,并写出对应算式。"
- if len(answers)>1:
- question_text += f"(共有{len(answers)}个答案,请写全)"
- return cls._create_question_result(question_text, numbers, answers)
- @classmethod
- def _generate_type_b(cls) -> Dict[str, Any]:
- """
- 一题多解1:生成恰好有两个解法的题目
- """
- # 获取随机题目
- item = cls._get_random_item('B')
- numbers = item.get('numbers', [])
- solutions = item.get('solutions', [])
-
- question_text = "一题多解:请找出这组数字的所有24点解法。"
-
- # 格式化解法
- answers = cls._format_solutions(solutions)
-
- return cls._create_question_result(question_text, numbers, answers)
- @classmethod
- def _generate_type_c(cls) -> Dict[str, Any]:
- """
- 一题多解2:生成有三个或以上解法的题目
- """
- # 获取随机题目
- item = cls._get_random_item('C')
- numbers = item.get('numbers', [])
- solutions = item.get('solutions', [])
-
- question_text = "一题多解:请找出这组数字的所有24点解法!"
-
- # 格式化解法
- answers = cls._format_solutions(solutions)
-
- return cls._create_question_result(question_text, numbers, answers)
- @classmethod
- def _generate_type_d(cls) -> Dict[str, Any]:
- """
- 一星题目:生成包含flag=1的题目
- """
- # 获取随机题目
- item = cls._get_random_item('D')
- numbers = item.get('numbers', [])
- solutions = item.get('solutions', [])
-
- question_text = "【一星】题目:请找出这组数字的24点解法。"
-
- # 格式化解法,只保留flag=1的解法
- answers = cls._format_solutions(solutions, flag_value=1)
- return cls._create_question_result(question_text, numbers, answers)
- @classmethod
- def _generate_type_e(cls) -> Dict[str, Any]:
- """
- 二星题目:生成包含flag=2的题目
- """
- # 获取随机题目
- item = cls._get_random_item('E')
- numbers = item.get('numbers', [])
- solutions = item.get('solutions', [])
-
- question_text = "【二星】题目:请找出这组数字的24点解法。"
-
- # 格式化解法,只保留flag=2的解法
- answers = cls._format_solutions(solutions, flag_value=2)
-
- return cls._create_question_result(question_text, numbers, answers)
|