/
Landilf
/
2413_Ryumin_CourseWork
Обзор
Документация
Войти
/
Landilf
/
2413_Ryumin_CourseWork
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
project
CourseWork/GameCoreTests/Tests.cs
476 строк
15 KB
Landilf
add project
27 янв 2026, 13:57
27 янв 2026, 13:57
753b3e7
Код
Авторство
О чём код?
using GameCore.Engine; using GameCore.Entities; using GameCore.Enums; using Xunit; namespace GameCoreTests; /// <summary> /// Набор юнит-тестов для проверки логики игрового движка и поведения сущностей. /// </summary> public class EngineTests { #region Вспомогательные методы /// <summary> /// Создает и инициализирует игровой движок, запуская игру с тестовым игроком. /// </summary> /// <returns>Экземпляр запущенного движка.</returns> private GameEngine CreateEngineAndStart() { var engine = new GameEngine(); // Карта 50x40 engine.StartGame("Tester"); return engine; } /// <summary> /// Эмулирует ход игрового времени, вызывая метод Update заданное количество раз. /// </summary> /// <param name="engine">Игровой движок.</param> /// <param name="count">Количество циклов обновления (тиков).</param> private void UpdateLoops(GameEngine engine, int count) { for (int i = 0; i < count; i++) { engine.Update(); } } /// <summary> /// Получает объект живого игрока-человека из списка игроков. /// </summary> /// <param name="engine">Игровой движок.</param> /// <returns>Объект игрока.</returns> private Player GetPlayer(GameEngine engine) => engine.Players.First(p => !p.IsBot); #endregion #region Тесты /// <summary> /// Проверяет, что при создании движка состояние игры установлено в Menu. /// </summary> [Fact] public void Game_Initialization_SetsCorrectState() { var engine = new GameEngine(); Assert.Equal(GameState.Menu, engine.State); } /// <summary> /// Проверяет, что запуск игры переводит состояние в Playing и создает игроков. /// </summary> [Fact] public void StartGame_ChangesStateToPlaying() { var engine = CreateEngineAndStart(); Assert.Equal(GameState.Playing, engine.State); Assert.NotEmpty(engine.Players); Assert.NotNull(GetPlayer(engine)); } /// <summary> /// Проверяет, что игрок начинает игру с начальной захваченной территорией (3x3). /// </summary> [Fact] public void Player_HasInitialTerritory() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Проверяем, что вокруг спавна есть захваченные клетки int ownedCount = 0; foreach (var cell in engine.Map) { if (cell.OwnerId == p.Id) ownedCount++; } Assert.True(ownedCount >= 9, "Player should start with at least 9 owned cells"); } /// <summary> /// Проверяет корректность перемещения игрока во всех направлениях. /// </summary> [Theory] [InlineData(Direction.Up, 0, -1)] [InlineData(Direction.Down, 0, 1)] [InlineData(Direction.Left, -1, 0)] [InlineData(Direction.Right, 1, 0)] public void Player_Moves_InCorrectDirection(Direction dir, int dx, int dy) { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Перемещаем игрока в безопасное место (центр), чтобы не врезался в стены p.Position = new Point(25, 20); p.X = 25; p.Y = 20; p.CurrentDirection = dir; p.NextDirection = dir; // Делаем 5 тиков (1 клетка при скорости 0.2) UpdateLoops(engine, 5); Assert.Equal(25 + dx, (int)Math.Round(p.X)); Assert.Equal(20 + dy, (int)Math.Round(p.Y)); } /// <summary> /// Проверяет невозможность мгновенного разворота на 180 градусов. /// </summary> [Fact] public void Player_CannotReverse_Instantly() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Движемся вправо p.CurrentDirection = Direction.Right; // Пытаемся пойти влево engine.ChangeDirection(p.Id, Direction.Left); engine.Update(); Assert.Equal(Direction.Right, p.NextDirection); // Направление не должно измениться на противоположное } /// <summary> /// Проверяет, что при выходе за пределы своей территории образуется хвост. /// </summary> [Fact] public void Moving_OutsideTerritory_CreatesTail() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Спавн обычно на (5,5). Территория 3x3. // Телепортируем на край территории и направляем наружу p.Position = new Point(10, 10); // Нейтральная зона p.X = 10; p.Y = 10; p.CurrentDirection = Direction.Right; p.NextDirection = Direction.Right; UpdateLoops(engine, 6); // Проходим 1+ клетку Assert.NotEmpty(p.Tail); // Хвост должен появиться Assert.Equal(p.Id, engine.Map[11, 10].TailOwnerId); // След на карте } /// <summary> /// Проверяет, что при движении внутри своей территории хвост не образуется. /// </summary> [Fact] public void Moving_InsideTerritory_DoesNotCreateTail() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Центр стартовой территории int startX = p.SpawnPoint.X; int startY = p.SpawnPoint.Y; p.X = startX; p.Y = startY; p.CurrentDirection = Direction.Right; // Двигаемся внутри 3x3 квадрата UpdateLoops(engine, 2); // Немного прошли Assert.Empty(p.Tail); // Хвоста быть не должно } /// <summary> /// Проверяет, что замыкание контура захватывает территорию и очищает хвост. /// </summary> [Fact] public void Capture_Territory_IncreasesScore() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); double initialScore = p.PercentCaptured; // Симулируем захват: Выходим и возвращаемся // 1. Ставим игрока на край своей зоны p.Position = new Point(5, 5); p.X = 5; p.Y = 5; p.CurrentDirection = Direction.Right; // 2. Имитируем хвост p.Tail.Add(new Point(6, 5)); engine.Map[6, 5].TailOwnerId = p.Id; // Возвращаемся домой в (5,5) // Для этого нужно быть в (5,6) и пойти вверх p.Position = new Point(5, 6); p.X = 5; p.Y = 6; p.CurrentDirection = Direction.Up; UpdateLoops(engine, 6); // Заходим в (5,5) (своя территория) Assert.Empty(p.Tail); // Хвост должен исчезнуть (захват произошел) } /// <summary> /// Проверяет, что столкновение со стеной убивает игрока. /// </summary> [Fact] public void Hitting_Wall_KillsPlayer() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Ставим вплотную к стене (0,0) и направляем в стену (Влево) p.Position = new Point(0, 5); p.X = 0; p.Y = 5; p.CurrentDirection = Direction.Left; p.NextDirection = Direction.Left; p.InvulnerabilityTicks = 0; // Убираем щит если есть UpdateLoops(engine, 5); // Пытаемся выйти за пределы Assert.False(p.IsAlive); Assert.Equal(GameState.GameOver, engine.State); } /// <summary> /// Проверяет, что режим неуязвимости спасает от смерти при ударе о стену. /// </summary> [Fact] public void GodMode_Prevents_WallDeath() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); p.Position = new Point(0, 5); p.X = 0; p.Y = 5; p.CurrentDirection = Direction.Left; p.NextDirection = Direction.Left; p.InvulnerabilityTicks = 100; // Включаем щит UpdateLoops(engine, 5); Assert.True(p.IsAlive); // Должен выжить Assert.Equal(0, p.X); // Должен упереться в стену, но не уйти за неё } /// <summary> /// Проверяет, что столкновение с собственным хвостом убивает игрока. /// </summary> [Fact] public void Hitting_OwnTail_KillsPlayer() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Создаем ситуацию: Хвост на (10,10), Игрок идет на (10,10) engine.Map[10, 10].TailOwnerId = p.Id; p.Tail.Add(new Point(10, 10)); p.Position = new Point(9, 10); p.X = 9; p.Y = 10; p.CurrentDirection = Direction.Right; p.InvulnerabilityTicks = 0; UpdateLoops(engine, 6); // Входим в клетку с хвостом Assert.False(p.IsAlive); } /// <summary> /// Проверяет механику убийства врага при пересечении его хвоста. /// </summary> [Fact] public void Cutting_EnemyTail_KillsEnemy() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Находим бота var bot = engine.Players.First(x => x.IsBot); bot.IsAlive = true; // Рисуем боту хвост на пути игрока engine.Map[15, 15].TailOwnerId = bot.Id; bot.Tail.Add(new Point(15, 15)); // Игрок идет на хвост бота p.Position = new Point(14, 15); p.X = 14; p.Y = 15; p.CurrentDirection = Direction.Right; UpdateLoops(engine, 6); Assert.True(p.IsAlive); // Игрок жив Assert.False(bot.IsAlive); // Бот мертв } /// <summary> /// Проверяет, что подбор бонуса скорости увеличивает счетчик ускорения. /// </summary> [Fact] public void Picking_SpeedBonus_IncreasesSpeed() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Спавним бонус прямо перед игроком engine.Bonuses.Clear(); var bonusPos = new Point(20, 20); engine.Bonuses.Add(new Bonus(bonusPos, BonusType.Speed, 1000)); p.Position = new Point(19, 20); p.X = 19; p.Y = 20; p.CurrentDirection = Direction.Right; UpdateLoops(engine, 6); // Подбираем Assert.True(p.SpeedupTicks > 0); Assert.Empty(engine.Bonuses); // Бонус исчез } /// <summary> /// Проверяет, что подбор бонуса неуязвимости активирует защиту. /// </summary> [Fact] public void Picking_InvulnerabilityBonus_SetsTicks() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); engine.Bonuses.Clear(); var bonusPos = new Point(20, 20); engine.Bonuses.Add(new Bonus(bonusPos, BonusType.Invulnerability, 1000)); p.Position = new Point(19, 20); p.X = 19; p.Y = 20; p.CurrentDirection = Direction.Right; UpdateLoops(engine, 6); Assert.True(p.InvulnerabilityTicks > 0); } /// <summary> /// Проверяет возможность добавления бонусов на карту. /// </summary> [Fact] public void Bonus_Spawn_Limits() { var engine = CreateEngineAndStart(); // В начале 0 бонусов Assert.Empty(engine.Bonuses); // Проверяем ручное добавление engine.Bonuses.Add(new Bonus(new Point(0, 0), BonusType.Speed, 100)); Assert.Single(engine.Bonuses); } /// <summary> /// Проверяет переход игры в состояние паузы. /// </summary> [Fact] public void PauseGame_ChangesState() { var engine = CreateEngineAndStart(); engine.PauseGame(); Assert.Equal(GameState.Paused, engine.State); } /// <summary> /// Проверяет возобновление игры после паузы. /// </summary> [Fact] public void ResumeGame_ChangesStateBack() { var engine = CreateEngineAndStart(); engine.PauseGame(); engine.ResumeGame(); Assert.Equal(GameState.Playing, engine.State); } /// <summary> /// Проверяет работу таймера обратного отсчета при снятии с паузы. /// </summary> [Fact] public void Timer_CountsDown_OnResume() { var engine = CreateEngineAndStart(); engine.PauseGame(); engine.ResumeGame(); Assert.True(engine.Session.IsCountdownActive); // Проверяем, что обновление уменьшает таймер int initial = engine.Session.CountdownValue; engine.Session.UpdateCountdown(); // Эмулируем несколько кадров for (int i = 0; i < 50; i++) engine.Session.UpdateCountdown(); Assert.True(engine.Session.CountdownValue < initial); } /// <summary> /// Проверяет, что боты создаются с назначенными стратегиями поведения. /// </summary> [Fact] public void Bots_AreInitialized_WithStrategies() { var engine = CreateEngineAndStart(); var bots = engine.Players.Where(x => x.IsBot).ToList(); Assert.Equal(3, bots.Count); foreach (var bot in bots) { Assert.NotNull(bot.BotStrategy); } } /// <summary> /// Проверяет, что стратегия бота возвращает валидное направление движения. /// </summary> [Fact] public void Bot_Strategy_ReturnsValidDirection() { var engine = CreateEngineAndStart(); var bot = engine.Players.First(x => x.IsBot); var nextDir = bot.BotStrategy!.CalculateNextMove(engine, bot); Assert.Contains(nextDir, new[] { Direction.Up, Direction.Down, Direction.Left, Direction.Right }); } /// <summary> /// Проверяет условие победы игрока (захват 100% территории). /// </summary> [Fact] public void Victory_Condition_PlayerWins_WhenBotsDead() { var engine = CreateEngineAndStart(); var p = GetPlayer(engine); // Убиваем всех ботов foreach (var bot in engine.Players.Where(x => x.IsBot).ToList()) { bot.IsAlive = false; } // Тест на 100% захват территории p.PercentCaptured = 100.0; engine.Update(); Assert.Equal(GameState.Victory, engine.State); } #endregion }