@
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.
@
This commit is contained in:
Leonid Pershin
2026-06-12 07:19:10 +03:00
parent 4ae730cafa
commit fd6343bd09
96 changed files with 2066 additions and 589 deletions
@@ -14,7 +14,15 @@ public sealed class AtlasBuilderTests : IDisposable
public void Dispose() => Directory.Delete(_root, recursive: true);
/// <summary>Writes a PNG filled with one RGBA color.</summary>
private void WritePng(string relativePath, int width, int height, byte r, byte g, byte b, byte a = 255)
private void WritePng(
string relativePath,
int width,
int height,
byte r,
byte g,
byte b,
byte a = 255
)
{
var fullPath = Path.Combine(SourceDir, relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
@@ -28,25 +36,37 @@ public sealed class AtlasBuilderTests : IDisposable
}
using var stream = File.Create(fullPath);
new ImageWriter().WritePng(data, width, height, StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha, stream);
new ImageWriter().WritePng(
data,
width,
height,
StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha,
stream
);
}
private AtlasBuildOptions Options(int groupDepth = 1, bool force = false) => new()
{
SourceDirectory = SourceDir,
OutputDirectory = OutputDir,
GroupDepth = groupDepth,
MaxPageSize = 128,
Padding = 2,
Force = force,
};
private AtlasBuildOptions Options(int groupDepth = 1, bool force = false) =>
new()
{
SourceDirectory = SourceDir,
OutputDirectory = OutputDir,
GroupDepth = groupDepth,
MaxPageSize = 128,
Padding = 2,
Force = force,
};
[Theory]
[InlineData("Terrain/Surfaces/Marsh.png", 1, "Terrain", "Terrain/Surfaces/Marsh")]
[InlineData("Terrain/Surfaces/Marsh.png", 2, "Terrain.Surfaces", "Terrain/Surfaces/Marsh")]
[InlineData("Terrain/Surfaces/Marsh.png", 0, "Root", "Terrain/Surfaces/Marsh")]
[InlineData("loose.png", 3, "Root", "loose")]
public void ClassifyPath_GroupsByDepth(string path, int depth, string expectedAtlas, string expectedKey)
public void ClassifyPath_GroupsByDepth(
string path,
int depth,
string expectedAtlas,
string expectedKey
)
{
var (atlas, key) = AtlasBuilder.ClassifyPath(path, depth, "Root");
@@ -67,13 +87,18 @@ public sealed class AtlasBuilderTests : IDisposable
Assert.Equal(2, group.RegionCount);
Assert.False(group.Skipped);
var metadata = AtlasMetadata.FromJson(File.ReadAllText(Path.Combine(OutputDir, "Terrain.atlas")));
var metadata = AtlasMetadata.FromJson(
File.ReadAllText(Path.Combine(OutputDir, "Terrain.atlas"))
);
Assert.Equal(["Terrain/Grass", "Terrain/Water"], metadata.Regions.Select(x => x.Key));
var grass = metadata.Regions.Single(x => x.Key == "Terrain/Grass");
var page = metadata.Pages[grass.Page];
using var stream = File.OpenRead(Path.Combine(OutputDir, page.File));
var pixels = ImageResult.FromStream(stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha);
var pixels = ImageResult.FromStream(
stream,
StbImageSharp.ColorComponents.RedGreenBlueAlpha
);
// Центральный пиксель региона должен быть цветом исходной картинки.
var center = ((grass.Y + 8) * pixels.Width + grass.X + 8) * 4;
@@ -101,7 +126,9 @@ public sealed class AtlasBuilderTests : IDisposable
AtlasBuilder.Build(Options());
File.SetLastWriteTimeUtc(
Path.Combine(SourceDir, "UI/button.png"), DateTime.UtcNow.AddMinutes(1));
Path.Combine(SourceDir, "UI/button.png"),
DateTime.UtcNow.AddMinutes(1)
);
var result = AtlasBuilder.Build(Options());
Assert.False(Assert.Single(result.Groups).Skipped);
@@ -192,8 +219,27 @@ public sealed class AtlasBuilderTests : IDisposable
Name = "Things.Pawn",
PageSize = 2048,
Padding = 2,
Pages = [new AtlasPage { File = "Things.Pawn.atlas.0.png", Width = 256, Height = 128 }],
Regions = [new AtlasRegion { Key = "Things/Pawn/Fox", Page = 0, X = 2, Y = 4, Width = 64, Height = 32 }],
Pages =
[
new AtlasPage
{
File = "Things.Pawn.atlas.0.png",
Width = 256,
Height = 128,
},
],
Regions =
[
new AtlasRegion
{
Key = "Things/Pawn/Fox",
Page = 0,
X = 2,
Y = 4,
Width = 64,
Height = 32,
},
],
};
var parsed = AtlasMetadata.FromJson(metadata.ToJson());
@@ -203,8 +249,10 @@ public sealed class AtlasBuilderTests : IDisposable
var page = Assert.Single(parsed.Pages);
Assert.Equal(("Things.Pawn.atlas.0.png", 256, 128), (page.File, page.Width, page.Height));
var region = Assert.Single(parsed.Regions);
Assert.Equal(("Things/Pawn/Fox", 0, 2, 4, 64, 32),
(region.Key, region.Page, region.X, region.Y, region.Width, region.Height));
Assert.Equal(
("Things/Pawn/Fox", 0, 2, 4, 64, 32),
(region.Key, region.Page, region.X, region.Y, region.Width, region.Height)
);
}
[Fact]
@@ -213,10 +261,26 @@ public sealed class AtlasBuilderTests : IDisposable
var metadata = new AtlasMetadata
{
Name = "Test",
Pages = [new AtlasPage { File = "Test.atlas.0.png", Width = 64, Height = 64 }],
Pages =
[
new AtlasPage
{
File = "Test.atlas.0.png",
Width = 64,
Height = 64,
},
],
Regions =
[
new AtlasRegion { Key = "a/b", Page = 0, X = 2, Y = 2, Width = 10, Height = 12 },
new AtlasRegion
{
Key = "a/b",
Page = 0,
X = 2,
Y = 2,
Width = 10,
Height = 12,
},
],
};