class Node: # класс решений (вопрос или конечный ответ) def __init__(self, text: str): self.text = text self.is_question = text.endswith('?') self.left = None # Ссылка на левого потомка (выбор a да) self.right = None # Ссылка на правого потомка (выбор n нет) class ZooExpertSystem: # Класс для управления системой зоопарка # Отвечает за 1чтение данных, 2построение бинарного дерева решений # 3валидацию структуры и 4проведение взаимодействия с пользователем def __init__(self): # Корень построенного бинарного дерева self.root = None # Счетчики элементов всей базы знаний self.q_count = 0 # Количество вопросов self.a_count = 0 # Количество ответов (животных) # Списки для разделенных строк входа self.db_lines = [] # Строки относящиеся к базе знаний (дереву) self.user_choices = [] # Строки с ответами пользователя (a или n) def run(self): # контроллер запускает программу в строгом соответствии с жизненным циклом. print("Expert zo zoo to vie.") # Считываем весь поток ввода и разделяем его if not self._parse_input(): # _parse_input говорит нам что метод приватый print("Chybna databaza") return # Строим дерево решений из прочитанных строк if not self._build_tree(): # _build_tree создает цыпочку из вопросов которые приводят к ответу да или нет print("Chybna databaza") return # Выводим информацию о базе знаний self._print_stats() # Запускаем интерактивный опрос пользователя self._run_quiz() def _read_tree_lines(self) -> list[str]: # Считывает строки базы знаний из input() до первой пустой строки tree_lines = [] try: text = input().rstrip("\r") while text != "": tree_lines.append(text) text = input().rstrip("\r") except EOFError: pass return tree_lines def _parse_input(self) -> bool: # Читаем базу знаний до пустой строки self.db_lines = self._read_tree_lines() if not self.db_lines: return False # Читаем оставшиеся строки с ответами пользователя (a / n) try: while True: choice = input().rstrip("\r") self.user_choices.append(choice) except EOFError: pass return True def _build_tree(self) -> bool: # Рекурсивная сборка бинарного дерева из db_lines в формате Preorder idx = 0 def parse_node(): nonlocal idx if idx >= len(self.db_lines): return None text = self.db_lines[idx] idx += 1 node = Node(text) if node.is_question: self.q_count += 1 # Сначала собираем левую ветку (да a), затем правую (нет n) node.left = parse_node() node.right = parse_node() # У каждого вопроса обязательно должны быть оба потомка if node.left is None or node.right is None: return None else: self.a_count += 1 return node self.root = parse_node() # Если структура ломается или в базе остались лишние неиспользованные строки if self.root is None or idx != len(self.db_lines): return False return True def _print_stats(self): # Вывод количества вопросов, ответов и подсказки пользователю print(f"Pocet otazok: {self.q_count}") print(f"Pocet odpovedi: {self.a_count}") print("Odpovedajte 'a' pre prvu moznost alebo 'n' pre druhu moznost.") def _run_quiz(self): # Пошаговый спуск по дереву от корня к животному curr = self.root choice_idx = 0 while True: # Выводим текущий вопрос или название животного print(curr.text) # Дошли до конечной точки (листа с животным) if not curr.is_question: print("Koniec") return # Если для дальнейшего вопроса у пользователя не осталось ответа if choice_idx >= len(self.user_choices): print("Koniec vstupu") return choice = self.user_choices[choice_idx] choice_idx += 1 if choice == "a": curr = curr.left elif choice == "n": curr = curr.right else: # Введен неверный символ (не a и не n) print("Nerozumiem") return if __name__ == "__main__": system = ZooExpertSystem() system.run()