dreamteam / app /services /generator.py
Altanai's picture
Upload 40 files
9bb24f8 verified
"""
Сервис генерации судоку с использованием нейросети
"""
import numpy as np
import random
from typing import Tuple, Dict, Optional
import logging
from app.models.neural_solver import SudokuNeuralSolver
from app.models.validator import SudokuValidator
from app.core.config import settings
logger = logging.getLogger(__name__)
class SudokuGeneratorService:
"""Сервис для генерации судоку с использованием нейросети"""
def __init__(self):
"""Инициализация сервиса"""
logger.info("Initializing Sudoku Generator Service with Neural Network...")
self.solver = SudokuNeuralSolver() # Загружаем SudokuNet
self.validator = SudokuValidator()
self.empty_cells = {
"easy": settings.EMPTY_CELLS_EASY,
"medium": settings.EMPTY_CELLS_MEDIUM,
"hard": settings.EMPTY_CELLS_HARD
}
logger.info(f"✅ Generator service ready with {settings.MODEL_NAME}")
def generate_puzzle(
self,
difficulty: str = "medium"
) -> Tuple[np.ndarray, np.ndarray, Dict]:
"""
Генерация судоку с использованием нейросети
"""
try:
# Генерируем полное решение через нейросеть
logger.info("Generating solution using neural network...")
solution = self._generate_solution_neural()
# Создаем пазл, удаляя клетки
empty_count = self.empty_cells[difficulty]
logger.info(f"Creating puzzle with {empty_count} empty cells")
puzzle = self._create_puzzle_from_solution(solution, empty_count)
# Проверяем качество через нейросеть
neural_solution, confidence = self.solver.solve_with_confidence(puzzle)
# Если уверенность низкая, пробуем снова
max_attempts = 3
attempts = 0
while confidence < 0.9 and attempts < max_attempts:
logger.info(f"Regenerating puzzle (attempt {attempts + 1}), confidence: {confidence:.2f}")
puzzle = self._create_puzzle_from_solution(solution, empty_count)
neural_solution, confidence = self.solver.solve_with_confidence(puzzle)
attempts += 1
# Метаданные
metadata = {
"model": settings.MODEL_NAME,
"difficulty": difficulty,
"confidence": float(confidence),
"empty_cells": empty_count,
"attempts": attempts + 1,
"neural_network": "SudokuNet"
}
logger.info(f"✅ Generated {difficulty} puzzle with confidence {confidence:.2f}")
return puzzle, solution, metadata
except Exception as e:
logger.error(f"Error generating puzzle: {e}")
return self._get_fallback_puzzle(difficulty)
def _generate_solution_neural(self) -> np.ndarray:
"""
Генерирует решение через нейросеть
"""
# Начинаем с пустой сетки
grid = np.zeros((9, 9), dtype=int)
# Заполняем несколько клеток для инициализации
for _ in range(10): # Добавляем 10 случайных чисел
row, col = random.randint(0, 8), random.randint(0, 8)
num = random.randint(1, 9)
if self.validator._is_safe(grid, row, col, num):
grid[row, col] = num
# Решаем через нейросеть
try:
solution = self.solver.solve(grid)
logger.info("Neural network generated solution successfully")
return solution
except Exception as e:
logger.warning(f"Neural solution failed, using backtracking: {e}")
return self._generate_solution_backtracking()
def _generate_solution_backtracking(self) -> np.ndarray:
"""Запасной метод генерации через backtracking"""
grid = np.zeros((9, 9), dtype=int)
# Заполняем диагональные блоки
for i in range(0, 9, 3):
self._fill_box(grid, i, i)
# Решаем остальные клетки
self._solve_grid(grid)
return grid
def _fill_box(self, grid: np.ndarray, row: int, col: int):
"""Заполняет блок 3x3 случайными числами"""
nums = list(range(1, 10))
random.shuffle(nums)
for i in range(3):
for j in range(3):
grid[row + i, col + j] = nums[i * 3 + j]
def _solve_grid(self, grid: np.ndarray) -> bool:
"""Решает судоку через backtracking"""
empty = self.validator._find_empty(grid)
if not empty:
return True
row, col = empty
nums = list(range(1, 10))
random.shuffle(nums)
for num in nums:
if self.validator._is_safe(grid, row, col, num):
grid[row, col] = num
if self._solve_grid(grid):
return True
grid[row, col] = 0
return False
def _create_puzzle_from_solution(
self,
solution: np.ndarray,
empty_count: int
) -> np.ndarray:
"""Создает пазл из решения"""
puzzle = solution.copy()
positions = list(range(81))
random.shuffle(positions)
for idx in positions[:empty_count]:
row, col = idx // 9, idx % 9
puzzle[row, col] = 0
return puzzle
def generate_adaptive_puzzle(
self,
user_id: int,
difficulty: str = "medium"
) -> Tuple[np.ndarray, np.ndarray, Dict]:
"""
Генерация с учетом уровня игрока (адаптивная)
"""
# Получаем базовый пазл
puzzle, solution, metadata = self.generate_puzzle(difficulty)
# Адаптируем под игрока
empty_cells = np.sum(puzzle == 0)
# Здесь можно добавить логику на основе истории игрока
# Пока просто возвращаем с дополнительными метаданными
metadata["player_id"] = user_id
metadata["adaptive"] = True
metadata["empty_cells_actual"] = int(empty_cells)
return puzzle, solution, metadata
def _get_fallback_puzzle(self, difficulty: str) -> Tuple[np.ndarray, np.ndarray, Dict]:
"""Возвращает тестовое судоку в случае ошибки"""
logger.warning("Using fallback puzzle due to error")
puzzle = [
[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
[0,9,8,0,0,0,0,6,0],
[8,0,0,0,6,0,0,0,3],
[4,0,0,8,0,3,0,0,1],
[7,0,0,0,2,0,0,0,6],
[0,6,0,0,0,0,2,8,0],
[0,0,0,4,1,9,0,0,5],
[0,0,0,0,8,0,0,7,9]
]
solution = [
[5,3,4,6,7,8,9,1,2],
[6,7,2,1,9,5,3,4,8],
[1,9,8,3,4,2,5,6,7],
[8,5,9,7,6,1,4,2,3],
[4,2,6,8,5,3,7,9,1],
[7,1,3,9,2,4,8,5,6],
[9,6,1,5,3,7,2,8,4],
[2,8,7,4,1,9,6,3,5],
[3,4,5,2,8,6,1,7,9]
]
metadata = {
"model": "fallback",
"difficulty": difficulty,
"confidence": 1.0,
"empty_cells": 30,
"error": "Using fallback"
}
return np.array(puzzle), np.array(solution), metadata