import pygame
import sys
import random
import operator

# Инициализация Pygame
pygame.init()

# Размеры окна
WIDTH, HEIGHT = 1200, 700
GAME_WIDTH = 1000
MAP_WIDTH = WIDTH - GAME_WIDTH
GAME_HEIGHT = HEIGHT - 100  # Оставляем место для консоли

WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Математическая Игра с NPC")

# Цвета
BLUE = (0, 0, 255)
GREEN = (0, 200, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
LIGHT_BLUE = (173, 216, 230)
GRAY = (200, 200, 200)
DARK_GRAY = (50, 50, 50)
LIGHT_GRAY = (230, 230, 230)

# FPS
FPS = 60
clock = pygame.time.Clock()

# Шрифты
font = pygame.font.SysFont(None, 24)
large_font = pygame.font.SysFont(None, 32)
npc_font = pygame.font.SysFont(None, 20)
console_font = pygame.font.SysFont(None, 22)
map_font = pygame.font.SysFont(None, 16)

# Фоновые цвета для территорий
terrain_backgrounds = {
    "Засушливые земли": LIGHT_GRAY,
    "Великие равнины": (144, 238, 144),  # Light green
    "Горные пастбища": (169, 169, 169),  # Dark Gray
    "Туманная долина": (176, 224, 230),  # Powder blue
}

# Названия территорий распределенные по регионам
terrain_map = {}  # Ключ: (x, y), Значение: название местности

# Игровые параметры
player_size = 50
player_pos = [GAME_WIDTH // 2, GAME_HEIGHT // 2]
player_color = BLUE
player_speed = 5

# Карта мира
WORLD_SIZE = 30  # 30x30 регионов
MAP_TILE_SIZE = 15  # Уменьшен размер клетки

# Уровни сложности задач и соответствующие территории
npc_levels = {}
for level in range(1, 11):
    if level <= 3:
        math_range = (1, 10)
        territory = "Засушливые земли"
    elif level <= 6:
        math_range = (10, 50)
        territory = "Великие равнины"
    elif level <= 8:
        math_range = (50, 100)
        territory = "Горные пастбища"
    else:
        math_range = (100, 1000)
        territory = "Туманная долина"
    npc_levels[level] = {"math_range": math_range, "territory": territory}

# Уникальные названия предметов
sword_names = [
    "Рубиголов", "Остроклюв", "Буревестник", "Темнокаменный клинок",
    "Звёздочёрный меч", "Лунный колун", "Солнечный разрубатель",
    "Грозовой крюк", "Теневой блеск", "Тиранический кромкогрив",
    "Изумрудный резак", "Пламеглот", "Кристальный рубанок",
    "Багровый пырсок", "Сапфировый прорезинитель"
]

shield_names = [
    "Чешуя Дракона", "Черепаший панцирь", "Обелисковый бастион",
    "Гранитная кулачность", "Ледяной заслон", "Мифриловый щит",
    "Эльфийская структура", "Баншейская маржа", "Древний барьер",
    "Стальной фундамент", "Титановый блеск", "Железный усадок",
    "Кожаный покров", "Магический заслон", "Адамантиновый щит"
]

armor_names = [
    "Непроницаемая кожа", "Стальной заслон", "Броня носорога",
    "Серебряный панцирь", "Драконья кожа", "Мифриловая броня",
    "Эльфийское обмундирование", "Гномская кольчуга",
    "Титановая латная рубаха", "Магическая мантия",
    "Закалённый факел", "Железный каркас", "Кожаный покров",
    "Духовная броня", "Кристальная защита"
]

# Типы предметов
items_types = [
    {
        'type': 'Меч',
        'category_names': sword_names,
        'stat': 'Атака',
        'stat_value_range': (5, 15)
    },
    {
        'type': 'Щит',
        'category_names': shield_names,
        'stat': 'Защита',
        'stat_value_range': (3, 10)
    },
    {
        'type': 'Доспех',
        'category_names': armor_names,
        'stat': 'Защита',
        'stat_value_range': (4, 12)
    }
]

# NPC профессии и имена
npc_professions = [
    {'profession': 'Дровосек', 'names': ['Иван', 'Пётр', 'Алексей', 'Николай', 'Ли']},
    {'profession': 'Кузнец', 'names': ['Софья', 'Мария', 'Елена', 'Анна', 'Хуа']},
    {'profession': 'Травник', 'names': ['Дмитрий', 'Максим', 'Виктор', 'Андрей', 'Тихон']},
    {'profession': 'Рыбак', 'names': ['Константин', 'Георгий', 'Василий', 'Михаил', 'Лун']},
    {'profession': 'Алхимик', 'names': ['Лора', 'Майкл', 'Сара', 'Джек', 'Мию']},
    {'profession': 'Мастер', 'names': ['Боб', 'Том', 'Лиза', 'Кэт', 'Нана']}
]

# Структура региона
class Region:
    def __init__(self, x, y):
        self.coord = (x, y)
        self.npcs = []  # Список NPC в регионе
        self.generate_npcs()

    def generate_npcs(self):
        num_npcs = random.randint(1, 5)  # Ограничение от 1 до 5 NPC
        for _ in range(num_npcs):
            profession_entry = random.choice(npc_professions)
            profession = profession_entry['profession']
            name = random.choice(profession_entry['names'])
            # Генерация уровня NPC от 1 до 10 для различия сложности
            level = random.randint(1, 10)
            # Генерация позиции NPC с учётом предотвращения наложения
            pos = generate_npc_position()
            # Название местности
            terrain = npc_levels[level]['territory']
            self.npcs.append({
                'pos': pos,
                'has_interacted': False,
                'profession': profession,
                'name': name,
                'level': level,
                'task_solved': False
            })

def generate_npc_position(existing_npcs=None):
    if existing_npcs is None:
        existing_npcs = []
    attempts = 0
    while attempts < 100:
        x = random.randint(50, GAME_WIDTH - 200)
        y = random.randint(50, GAME_HEIGHT - 150)
        new_rect = pygame.Rect(x, y, 50, 50)
        overlap = False
        for npc in existing_npcs:
            existing_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
            if new_rect.colliderect(existing_rect.inflate(50, 50)):
                overlap = True
                break
        if not overlap:
            return [x, y]
        attempts += 1
    return [random.randint(50, GAME_WIDTH - 200), random.randint(50, GAME_HEIGHT - 150)]

# Генерация и хранение регионов
world_regions = {}

def get_region(x, y):
    if (x, y) not in world_regions and 0 <= x < WORLD_SIZE and 0 <= y < WORLD_SIZE:
        world_regions[(x, y)] = Region(x, y)
    return world_regions.get((x, y), None)

# Текущий регион игрока
current_region_coord = (WORLD_SIZE // 2, WORLD_SIZE // 2)  # Начинаем в центре
current_region = get_region(*current_region_coord)

# Инициализация имен местностей
def assign_territories(x, y):
    if (x, y) not in terrain_map:
        territory = npc_levels[random.randint(1,10)]['territory']
        terrain_map[(x, y)] = territory

# Вызов функции для начальной территории
assign_territories(*current_region_coord)

# Инвентарь
inventory = []

# Активное снаряжение
equipped = {
    'Меч': [],
    'Щит': None,
    'Доспех': None
}

# Игрокские параметры
attack = 0
defense = 0
knowledge_level = 1
tasks_solved = 0
tasks_total = 0  # Общее количество решенных задач
tasks_needed = 10  # Необходимое количество для повышения уровня

# Процент верных решений
def calculate_percentage():
    if tasks_total == 0:
        return 0
    return int((tasks_solved / tasks_total) * 100)

# Состояния игры
state = 'move'  # Возможные состояния: 'move', 'dialog', 'inventory'

# Диалоговые окна
current_npc = None
current_question = None
correct_answer = None
player_answer = ''

# Информационная консоль
console_message = ""

# Функция для пересчёта параметров игрока
def recalculate_stats():
    global attack, defense
    attack = sum([item['value'] for item in equipped['Меч']])
    shield_def = equipped['Щит']['value'] if equipped['Щит'] else 0
    armor_def = equipped['Доспех']['value'] if equipped['Доспех'] else 0
    defense = shield_def + armor_def

# Функция для обновления уровня знаний
def update_level():
    global knowledge_level, tasks_needed, console_message
    levels_requirements = {1:10, 2:20, 3:30, 4:40, 5:50, 6:60, 7:70, 8:80, 9:90, 10:100}
    if knowledge_level in levels_requirements:
        if tasks_solved >= levels_requirements[knowledge_level]:
            knowledge_level +=1
            tasks_needed = levels_requirements.get(knowledge_level, tasks_needed)
            console_message += f" Поздравляем! Вы достигли уровня {knowledge_level} знаний."

# Функция для открытия окна инвентаря
def open_inventory():
    inventory_running = True
    while inventory_running:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    inventory_running = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                # Проверяем нажатие на кнопки "Взять"
                for idx, item in enumerate(inventory.copy()):
                    button_rect = pygame.Rect(GAME_WIDTH + 20, 80 + idx * 30, 80, 25)
                    if button_rect.collidepoint(mouse_pos):
                        equip_item(idx)
                        inventory_running = False

        # Отрисовка инвентаря
        WIN.fill(WHITE)
        inventory_title = large_font.render("Инвентарь", True, BLACK)
        WIN.blit(inventory_title, (GAME_WIDTH + MAP_WIDTH//2 - inventory_title.get_width()//2, 20))

        for idx, item in enumerate(inventory):
            item_text = font.render(f"{idx+1}. {item['name']} ({item['type']}: {item['stat']} +{item['value']})", True, BLACK)
            WIN.blit(item_text, (GAME_WIDTH + 10, 80 + idx * 30))
            # Рисуем кнопку "Взять"
            button_rect = pygame.Rect(GAME_WIDTH + 20, 80 + idx * 30, 80, 25)
            pygame.draw.rect(WIN, GRAY, button_rect)
            button_text = font.render("Взять", True, BLACK)
            WIN.blit(button_text, (button_rect.x + 15, button_rect.y + 3))

            # Изменение цвета кнопки при наведении
            mouse_pos = pygame.mouse.get_pos()
            if button_rect.collidepoint(mouse_pos):
                pygame.draw.rect(WIN, YELLOW, button_rect, 2)

        # Отображение активного снаряжения
        equipped_text = font.render("Активное снаряжение:", True, BLACK)
        WIN.blit(equipped_text, (GAME_WIDTH + 10, GAME_HEIGHT - 160))
        swords = ', '.join([item['name'] for item in equipped['Меч']]) if equipped['Меч'] else "Нет"
        shield = equipped['Щит']['name'] if equipped['Щит'] else "Нет"
        armor = equipped['Доспех']['name'] if equipped['Доспех'] else "Нет"
        swords_text = font.render(f"Мечи: {swords}", True, BLACK)
        shield_text = font.render(f"Щит: {shield}", True, BLACK)
        armor_text = font.render(f"Доспех: {armor}", True, BLACK)
        WIN.blit(swords_text, (GAME_WIDTH + 30, GAME_HEIGHT - 140))
        WIN.blit(shield_text, (GAME_WIDTH + 30, GAME_HEIGHT - 120))
        WIN.blit(armor_text, (GAME_WIDTH + 30, GAME_HEIGHT - 100))

        pygame.display.update()

# Функция для экипировки предмета
def equip_item(idx):
    global equipped, inventory, console_message, attack, defense
    item = inventory.pop(idx)
    if item['type'] == 'Меч':
        if len(equipped['Меч']) < 2:
            equipped['Меч'].append(item)
            console_message = f"Вы экипировали меч: {item['name']} (Атака +{item['value']})"
        else:
            console_message = "Максимум два меча уже экипированы."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
    elif item['type'] == 'Щит':
        if equipped['Щит']:
            console_message = f"Сейчас экипирован щит: {equipped['Щит']['name']}. Смените его перед экипировкой нового щита."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
        else:
            equipped['Щит'] = item
            console_message = f"Вы экипировали щит: {item['name']} (Защита +{item['value']})"
    elif item['type'] == 'Доспех':
        if equipped['Доспех']:
            console_message = f"Сейчас экипированы доспехи: {equipped['Доспех']['name']}. Смените их перед экипировкой новых доспехов."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
        else:
            equipped['Доспех'] = item
            console_message = f"Вы экипировали доспехи: {item['name']} (Защита +{item['value']})"
    recalculate_stats()

# Функция для рисования карты
def draw_map():
    # Отрисовка карты на правой части окна
    pygame.draw.rect(WIN, DARK_GRAY, (GAME_WIDTH, 0, MAP_WIDTH, GAME_HEIGHT))
    map_title = large_font.render("Карта", True, WHITE)
    WIN.blit(map_title, (GAME_WIDTH + MAP_WIDTH//2 - map_title.get_width()//2, 10))

    # Отрисовка квадратов регионов
    for y in range(WORLD_SIZE):
        for x in range(WORLD_SIZE):
            region_coord = (x, y)
            color = GRAY  # По умолчанию тёмный "туман"
            if region_coord in terrain_map:
                color = WHITE  # Посещённые регионы
                if region_coord == current_region_coord:
                    color = YELLOW  # Текущий регион
            rect_x = GAME_WIDTH + 10 + x * MAP_TILE_SIZE
            rect_y = 40 + y * MAP_TILE_SIZE
            pygame.draw.rect(WIN, color, (rect_x, rect_y, MAP_TILE_SIZE, MAP_TILE_SIZE))
            pygame.draw.rect(WIN, BLACK, (rect_x, rect_y, MAP_TILE_SIZE, MAP_TILE_SIZE), 1)

    # Отображение координат текущего региона
    coord_text = map_font.render(f"Координаты: {current_region_coord}", True, WHITE)
    WIN.blit(coord_text, (GAME_WIDTH + 10, GAME_HEIGHT - 30))

# Функция для рисования основного окна
def draw_window():
    WIN.fill(WHITE)

    # Отрисовка текущей территории фоном
    territory = terrain_map.get(current_region_coord, "Засушливые земли")
    bg_color = terrain_backgrounds.get(territory, WHITE)
    pygame.draw.rect(WIN, bg_color, (0, 0, GAME_WIDTH, GAME_HEIGHT))

    # Рисуем территорию сверху
    territory_title = large_font.render(territory, True, BLACK)
    WIN.blit(territory_title, (GAME_WIDTH//2 - territory_title.get_width()//2, 10))

    # Рисуем NPC
    for npc in current_region.npcs:
        color = YELLOW if npc['task_solved'] else GREEN
        pygame.draw.rect(WIN, color, (npc['pos'][0], npc['pos'][1], 50, 50))
        # Отображаем имя, уровень и профессию
        npc_text = npc_font.render(f"{npc['profession']} (ур.{npc['level']}): {npc['name']}", True, BLACK)
        WIN.blit(npc_text, (npc['pos'][0], npc['pos'][1] - 20))

        # Если игрок рядом и можно получить предмет, показываем информацию
        if state == 'move' and not npc['task_solved']:
            player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
            npc_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
            if player_rect.colliderect(npc_rect.inflate(100, 100)):  # Увеличенный радиус
                # Предположим, что каждый NPC может дать один случайный предмет
                possible_item = generate_item(npc['level'])
                item_type = possible_item['type']
                item_name = possible_item['name']
                item_stat = possible_item['stat']
                stat_value = possible_item['value']
                info_text = f"{item_type} {item_name} ({item_stat} +{stat_value})"
                info_render = font.render(info_text, True, YELLOW)
                WIN.blit(info_render, (npc['pos'][0], npc['pos'][1] + 60))

    # Рисуем игрока
    pygame.draw.rect(WIN, player_color, (player_pos[0], player_pos[1], player_size, player_size))

    # Отображаем параметры игрока
    attack_text = font.render(f"Атака: {attack}", True, BLACK)
    defense_text = font.render(f"Защита: {defense}", True, BLACK)
    percentage_text = font.render(f"Процент верных: {calculate_percentage()}%", True, BLACK)
    knowledge_text = font.render(f"Уровень Знаний: {knowledge_level} ({tasks_solved}/{tasks_needed})", True, BLACK)
    WIN.blit(attack_text, (GAME_WIDTH - 200, 20))
    WIN.blit(defense_text, (GAME_WIDTH - 200, 50))
    WIN.blit(percentage_text, (GAME_WIDTH - 200, 80))
    WIN.blit(knowledge_text, (GAME_WIDTH - 200, 110))

    # Рисуем кнопку инвентаря под параметрами
    inventory_button = pygame.Rect(GAME_WIDTH - 120, 140, 100, 30)
    pygame.draw.rect(WIN, GRAY, inventory_button)
    inventory_text = font.render("Инвентарь", True, BLACK)
    WIN.blit(inventory_text, (inventory_button.x + 15, inventory_button.y + 5))
    # Проверка наведения на кнопку
    mouse_pos = pygame.mouse.get_pos()
    mouse_pressed = pygame.mouse.get_pressed()
    if inventory_button.collidepoint(mouse_pos):
        pygame.draw.rect(WIN, YELLOW, inventory_button, 2)
        if mouse_pressed[0]:
            open_inventory()

    # Рисуем карту
    draw_map()

    # Информационная консоль внизу
    pygame.draw.rect(WIN, LIGHT_BLUE, (0, GAME_HEIGHT, GAME_WIDTH, HEIGHT - GAME_HEIGHT))
    console_text = console_font.render(console_message, True, BLACK)
    WIN.blit(console_text, (10, GAME_HEIGHT + 10))

    pygame.display.update()

# Функция для генерации математического вопроса с уровнем сложности
def generate_math_question(npc_level):
    operations = ['+', '-', '*', '/', '**']
    if 1 <= npc_level <= 3:
        num_range = (1, 10)
        num_operations = 1
    elif 4 <= npc_level <=6:
        num_range = (10, 50)
        num_operations = 2
    elif 7 <= npc_level <=9:
        num_range = (50, 100)
        num_operations = 3
    else:
        num_range = (100, 1000)
        num_operations = 4

    question_parts = []
    operations_sequence = []
    num1 = random.randint(*num_range)
    question_parts.append(str(num1))
    for _ in range(num_operations):
        operation = random.choice(operations)
        operations_sequence.append(operation)
        num = random.randint(*num_range)
        question_parts.append(operation)
        question_parts.append(str(num))
    question = ' '.join(question_parts)
    try:
        # Безопасное вычисление, ограничим операции
        allowed_ops = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '**': operator.pow}
        expr = question
        # Рассчитываем ответ
        answer = eval(expr, {"__builtins__": None}, allowed_ops)
        if isinstance(answer, float):
            answer = round(answer, 2)  # Округляем до 2 знаков
    except:
        answer = None
    return question, answer

# Функция для генерации предмета на основе уровня NPC
def generate_item(npc_level):
    # Выбираем случайный тип предмета
    item_type_entry = random.choice(items_types)
    item_type = item_type_entry['type']
    item_name = random.choice(item_type_entry['category_names'])
    stat = item_type_entry['stat']
    # Увеличиваем значения параметров на основе уровня NPC
    stat_value = random.randint(
        item_type_entry['stat_value_range'][0] + npc_level,
        item_type_entry['stat_value_range'][1] + npc_level
    )
    return {
        'type': item_type,
        'name': item_name,
        'stat': stat,
        'value': stat_value
    }

# Функция обработки диалога с NPC
def handle_dialog(npc):
    global state, current_question, correct_answer, player_answer, inventory, console_message, tasks_solved, tasks_total
    # Генерируем вопрос
    current_question, correct_answer = generate_math_question(npc['level'])
    state = 'question'
    player_answer = ''
    # Определяем предмет, который даст NPC в зависимости от уровня знания
    received_item = generate_item(npc['level'])
    console_message = f"{npc['name']} предлагает вам {received_item['type']} {received_item['name']} ({received_item['stat']} +{received_item['value']}). Решите задачу, чтобы получить его."

    # Главный цикл диалога
    while state == 'question':
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    console_message = "Диалог завершён без решения задачи."
                    state = 'move'
                elif event.key == pygame.K_RETURN:
                    if player_answer:
                        try:
                            # Обрабатываем деление как целочисленное, если ответ целый
                            if '/' in current_question or '**' in current_question:
                                user_answer = float(player_answer)
                                if user_answer == correct_answer:
                                    inventory.append(received_item)
                                    console_message = f"Вы заслужили этот предмет: {received_item['name']}."
                                    tasks_solved +=1
                                    tasks_total +=1
                                    # Обновление процента верных
                                    update_level()
                                    # Обновляем статус NPC
                                    npc['task_solved'] = True
                                    state = 'move'
                                else:
                                    console_message = f"Неправильный ответ! Правильный ответ: {correct_answer}."
                                    tasks_total +=1
                                    state = 'move'
                            else:
                                user_answer = int(player_answer)
                                if user_answer == correct_answer:
                                    inventory.append(received_item)
                                    console_message = f"Вы заслужили этот предмет: {received_item['name']}."
                                    tasks_solved +=1
                                    tasks_total +=1
                                    # Обновление процента верных
                                    update_level()
                                    # Обновляем статус NPC
                                    npc['task_solved'] = True
                                    state = 'move'
                                else:
                                    console_message = f"Неправильный ответ! Правильный ответ: {correct_answer}."
                                    tasks_total +=1
                                    state = 'move'
                            player_answer = ''
                        except:
                            console_message = "Пожалуйста, введите корректный ответ."
                            player_answer = ''
                    else:
                        # Если нет ответа, выходим из диалога
                        console_message = "Диалог завершён без решения задачи."
                        state = 'move'
                elif event.key == pygame.K_BACKSPACE:
                    player_answer = player_answer[:-1]
                else:
                    if event.unicode.isdigit() or event.unicode in ['.', '-']:
                        player_answer += event.unicode

        # Рисуем основной экран
        draw_window()
        # Отображаем информацию диалога в консоли
        pygame.draw.rect(WIN, LIGHT_BLUE, (0, GAME_HEIGHT, GAME_WIDTH, HEIGHT - GAME_HEIGHT))
        # Первая строка: предложение предмета
        dialog_line1 = console_font.render(f"{npc['name']} предлагает вам {received_item['type']} {received_item['name']} ({received_item['stat']} +{received_item['value']}). Решите задачу, чтобы получить его.", True, BLACK)
        WIN.blit(dialog_line1, (10, GAME_HEIGHT + 10))
        # Вторая строка: сама задача
        if current_question and correct_answer is not None:
            dialog_line2 = console_font.render(f"Задача: {current_question}", True, BLACK)
            WIN.blit(dialog_line2, (10, GAME_HEIGHT + 40))
        # Третья строка: поле ввода ответа
        input_prompt = console_font.render("Ответ:", True, BLACK)
        input_field = console_font.render(player_answer, True, BLACK)
        WIN.blit(input_prompt, (10, GAME_HEIGHT + 70))
        WIN.blit(input_field, (80, GAME_HEIGHT + 70))

        pygame.display.update()

# Функция для взаимодействия с NPC
def interact_with_npc(npc):
    global state, current_npc
    current_npc = npc
    handle_dialog(npc)

# Функция для перехода между регионами
def move_to_region(new_x, new_y, direction):
    global current_region_coord, current_region, player_pos, console_message
    # Проверяем границы мира
    if not (0 <= new_x < WORLD_SIZE and 0 <= new_y < WORLD_SIZE):
        console_message = "Вы не можете выйти за пределы мира."
        return

    # Устанавливаем новую позицию игрока в зависимости от направления
    if direction == 'right':
        player_pos[0] = 0
    elif direction == 'left':
        player_pos[0] = GAME_WIDTH - player_size
    elif direction == 'down':
        player_pos[1] = 0
    elif direction == 'up':
        player_pos[1] = GAME_HEIGHT - player_size

    current_region_coord = (new_x, new_y)
    current_region = get_region(*current_region_coord)
    assign_territories(new_x, new_y)
    console_message = "Вы перешли в новый регион."

# Основной игровой цикл
def main():
    global state
    running = True
    while running:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        keys = pygame.key.get_pressed()
        if state == 'move':
            movement = False
            direction = None
            new_x, new_y = current_region_coord
            # Перемещение игрока
            if keys[pygame.K_LEFT]:
                if player_pos[0] - player_speed > 0:
                    player_pos[0] -= player_speed
                else:
                    # Переход в левый регион
                    new_x = current_region_coord[0] -1
                    new_y = current_region_coord[1]
                    direction = 'left'
                    movement = True
            if keys[pygame.K_RIGHT]:
                if player_pos[0] + player_speed + player_size < GAME_WIDTH:
                    player_pos[0] += player_speed
                else:
                    # Переход в правый регион
                    new_x = current_region_coord[0] +1
                    new_y = current_region_coord[1]
                    direction = 'right'
                    movement = True
            if keys[pygame.K_UP]:
                if player_pos[1] - player_speed > 0:
                    player_pos[1] -= player_speed
                else:
                    # Переход в верхний регион
                    new_x = current_region_coord[0]
                    new_y = current_region_coord[1] -1
                    direction = 'up'
                    movement = True
            if keys[pygame.K_DOWN]:
                if player_pos[1] + player_speed + player_size < GAME_HEIGHT:
                    player_pos[1] += player_speed
                else:
                    # Переход в нижний регион
                    new_x = current_region_coord[0]
                    new_y = current_region_coord[1] +1
                    direction = 'down'
                    movement = True

            if movement:
                move_to_region(new_x, new_y, direction)

            # Проверяем близость к NPC
            for npc in current_region.npcs:
                player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
                npc_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
                if player_rect.colliderect(npc_rect.inflate(100, 100)) and not npc['task_solved']:
                    interact_with_npc(npc)
                    break  # Один NPC за раз

        draw_window()

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()

Этот код надо дополнить. Нужно изменить следующее: 1)Размер окна может быть больше (на весь экран), 2)Квадратики, составляющие карту справа должны быть ещё меньше (в 2-4 раза), чтобы карта помещалась. 3)В задачах не должно быть примеров с нецелочисленным делением, 4)В задачах недолжно быть примеров с возведением более чем во вторую степень, 5)Местные жители должны быть дальше от границ экрана, чтобы персонаж переходя на соседний экран не попадал на них сразу, 6) Почему-то возникает ситуация, что при отмене диалога, тут же возникает другая задача. Вероятно из-за того, что персонаж находится в радиусе диалога с этим местным жителем. Можно решить эту проблему так: подсвечивать местного жителя оранжевым (как будто он обиделся, что его задачу не решают), после отказа решать, вступать с ним в диалог нельзя в течение минуты. Потом его окраска возвращается на зеленую. 7)Местные жители на первых экранах встречаются с маленькими уровнями, их уровень увеличивается по мере удаления от старта. 8)в диалог с персонажем можно войти только подойдя вплотную (3пикселя)