CI / build-test (push) Failing after 1m8s
Add MrGameEng.AI utility-AI module; format codebase with CSharpier New MrGameEng.AI module (ResponseCurve, Consideration, UtilityAction, UtilityAi selector, Blackboard) plus CSharpier formatting applied across the whole engine. Documents the CSharpier convention in CLAUDE.md. @
102 lines
3.2 KiB
C#
102 lines
3.2 KiB
C#
using Microsoft.Xna.Framework;
|
|
using Xunit;
|
|
|
|
namespace MrGameEng.Pathfinding.Tests;
|
|
|
|
public class FlowFieldTests
|
|
{
|
|
[Fact]
|
|
public void Build_DistancesGrowFromGoal_DirectionsDescend()
|
|
{
|
|
var grid = new TestGrid(".....", ".###.", ".....");
|
|
var builder = new FlowFieldBuilder(grid);
|
|
var field = new FlowField();
|
|
|
|
builder.Build([new Point(0, 0)], field);
|
|
|
|
Assert.Equal(0f, field.DistanceAt(0, 0));
|
|
Assert.True(field.DistanceAt(4, 2) > field.DistanceAt(1, 0));
|
|
|
|
// Из любой достижимой не-целевой клетки направление ведёт к клетке с меньшей дистанцией.
|
|
for (var y = 0; y < grid.Height; y++)
|
|
{
|
|
for (var x = 0; x < grid.Width; x++)
|
|
{
|
|
if (
|
|
!grid.IsPassable(x, y)
|
|
|| !field.IsReachable(x, y)
|
|
|| field.DistanceAt(x, y) == 0f
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var direction = field.DirectionAt(x, y);
|
|
Assert.NotEqual(Vector2.Zero, direction);
|
|
var nx = x + Math.Sign(MathF.Round(direction.X * 10f));
|
|
var ny = y + Math.Sign(MathF.Round(direction.Y * 10f));
|
|
Assert.True(
|
|
field.DistanceAt(nx, ny) < field.DistanceAt(x, y),
|
|
$"direction at ({x},{y}) does not descend"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_UnreachablePocket_IsFlagged()
|
|
{
|
|
var grid = new TestGrid("..#..", "..#..", "..#..");
|
|
var builder = new FlowFieldBuilder(grid);
|
|
var field = new FlowField();
|
|
|
|
builder.Build([new Point(0, 1)], field);
|
|
|
|
Assert.False(field.IsReachable(4, 1));
|
|
Assert.Equal(Vector2.Zero, field.DirectionAt(4, 1));
|
|
Assert.True(field.IsReachable(1, 2));
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_MultipleGoals_EachCellFlowsToNearest()
|
|
{
|
|
var grid = new TestGrid("..........");
|
|
var builder = new FlowFieldBuilder(grid);
|
|
var field = new FlowField();
|
|
|
|
builder.Build([new Point(0, 0), new Point(9, 0)], field);
|
|
|
|
Assert.True(field.DirectionAt(2, 0).X < 0f); // ближе к левой цели
|
|
Assert.True(field.DirectionAt(7, 0).X > 0f); // ближе к правой
|
|
Assert.Equal(0f, field.DistanceAt(9, 0));
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_Rebuild_OverwritesPreviousField()
|
|
{
|
|
var grid = new TestGrid(".....");
|
|
var builder = new FlowFieldBuilder(grid);
|
|
var field = new FlowField();
|
|
|
|
builder.Build([new Point(0, 0)], field);
|
|
Assert.True(field.DirectionAt(4, 0).X < 0f);
|
|
|
|
builder.Build([new Point(4, 0)], field);
|
|
Assert.True(field.DirectionAt(0, 0).X > 0f);
|
|
Assert.Equal(0f, field.DistanceAt(4, 0));
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_NoValidGoals_EverythingUnreachable()
|
|
{
|
|
var grid = new TestGrid(".#.");
|
|
var builder = new FlowFieldBuilder(grid);
|
|
var field = new FlowField();
|
|
|
|
builder.Build([new Point(1, 0)], field); // цель — стена
|
|
|
|
Assert.False(field.IsReachable(0, 0));
|
|
Assert.False(field.IsReachable(2, 0));
|
|
}
|
|
}
|