/
Dmitry_SH79
/
FeelWordGap
Обзор
Документация
Войти
/
Dmitry_SH79
/
FeelWordGap
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
backend/Controllers/GameController.cs
189 строк
7 KB
Dmitry_SH79
оно работает
21 май 2026, 17:20
21 май 2026, 17:20
c2626e7
Код
Авторство
О чём код?
using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using backend.Data; using backend.Models; using System.Text.Json; namespace backend.Controllers { [ApiController] [Route("api/[controller]")] public class GameController : ControllerBase { private readonly AppDbContext _context; private readonly Random _random = new(); public GameController(AppDbContext context) { _context = context; } [HttpGet("generate/{categoryId}")] public async Task<IActionResult> GenerateGrid( int categoryId, [FromQuery] int size = 10, [FromQuery] int userId = 1 ) { // Валидация размера для безопасности if (size < 4 || size > 15) return BadRequest("Размер сетки должен быть от 4 до 15."); // 1. Вытаскиваем все доступные слова из БД для этой категории var availableWords = await _context.Words .Where(w => w.CategoryId == categoryId) .Select(w => new { w.WordId, text = w.WordValue.ToLower() }) .ToListAsync(); if (!availableWords.Any()) return BadRequest("Нет слов в данной категории."); char[,] grid = new char[size, size]; bool[,] visited = new bool[size, size]; var placedWords = new List<WordPositionDto>(); // Перемешиваем слова случайным образом var shuffledWords = availableWords.OrderBy(_ => _random.Next()).ToList(); // 2. Алгоритм укладки змеек (Жадный Random Walk) foreach (var dbWord in shuffledWords) { string word = dbWord.text; if (word.Length > 12) continue; // Пропускаем слишком длинные слова для MVP // Пробуем найти место для слова (до 50 случайных попыток на слово) for (int attempt = 0; attempt < 50; attempt++) { int startX = _random.Next(size); int startY = _random.Next(size); if (visited[startX, startY]) continue; var path = TryPlaceWordSnake(word, startX, startY, size, visited); if (path != null) { // Ура, змейка поместилась! Фиксируем её на поле var wordPos = new WordPositionDto { WordId = dbWord.WordId, Word = word }; for (int i = 0; i < word.Length; i++) { //grid[path[i].X, path[i].Y] = word[i]; grid[path[i].Y, path[i].X] = word[i]; visited[path[i].X, path[i].Y] = true; wordPos.Coords.Add(new CellCoords { X = path[i].X, Y = path[i].Y }); } placedWords.Add(wordPos); break; } } } // 3. Заполняем оставшиеся пустые клетки (если алгоритм оставил дыры) случайными буквами string alphabet = "абвгдежзийклмнопрстуфхцчшщъыьэюя"; for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { // if (grid[x, y] == '\0') // { // grid[x, y] = alphabet[_random.Next(alphabet.Length)]; // } if (grid[y, x] == '\0') { grid[y, x] = alphabet[_random.Next(alphabet.Length)]; } } } // 4. Логируем игру в БД (в таблицы user_games и user_games_words по манифесту) var userGame = new UserGame { UserId = userId, CategoryId = categoryId, TimeStart = DateTime.UtcNow }; _context.UserGames.Add(userGame); await _context.SaveChangesAsync(); // Получаем game_id foreach (var pw in placedWords) { var gameWord = new UserGameWord { GameId = userGame.GameId, WordId = pw.WordId, Coords = JsonSerializer.Serialize(pw.Coords) // Складываем координаты в JSONB }; _context.UserGamesWords.Add(gameWord); } await _context.SaveChangesAsync(); // Нам неудобно передавать char[,] напрямую в JSON, поэтому превратим матрицу в массив строк var clientMatrix = new List<string>(); for (int i = 0; i < size; i++) { string row = ""; for (int j = 0; j < size; j++) row += grid[i, j]; clientMatrix.Add(row); } // Очищаем список слов от тех, которые не удалось полностью разместить на поле placedWords = placedWords.Where(w => { // Проверяем, что каждая координата слова в матрице действительно содержит нужную букву return w.Coords.All(c => grid[c.Y, c.X] == w.Word[w.Coords.IndexOf(c)]); }).ToList(); // Обновляем запись в БД, чтобы там тоже всё было честно // (Код логирования в БД можно оставить выше, но список placedWords для клиента теперь отфильтрован) return Ok(new { gameId = userGame.GameId, size = size, matrix = clientMatrix, words = placedWords // Массив выигрышных комбинаций для проверки на фронте }); } // Вспомогательный метод поиска пути для змейки (влево, вправо, вверх, вниз) private List<CellCoords>? TryPlaceWordSnake(string word, int startX, int startY, int size, bool[,] visited) { var path = new List<CellCoords> { new() { X = startX, Y = startY } }; bool[,] localVisited = (bool[,])visited.Clone(); localVisited[startX, startY] = true; int[] dx = { 0, 0, 1, -1 }; int[] dy = { 1, -1, 0, 0 }; for (int i = 1; i < word.Length; i++) { var current = path.Last(); var validNeighbors = new List<CellCoords>(); // Ищем свободные соседние клетки под 90 градусов for (int d = 0; d < 4; d++) { int nx = current.X + dx[d]; int ny = current.Y + dy[d]; if (nx >= 0 && nx < size && ny >= 0 && ny < size && !localVisited[nx, ny]) { validNeighbors.Add(new CellCoords { X = nx, Y = ny }); } } if (!validNeighbors.Any()) return null; // Тупик, змейка зажала сама себя // Шагаем в случайного свободного соседа var nextCell = validNeighbors[_random.Next(validNeighbors.Count)]; localVisited[nextCell.X, nextCell.Y] = true; path.Add(nextCell); } return path; } } }