Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b1e0ccf
Upgrade project to .NET 10
claude Nov 15, 2025
55d6a92
Fix package versions for .NET 10 compatibility
claude Nov 15, 2025
822e2a5
Fix GitHub Actions workflow for modern standards
claude Nov 15, 2025
4f6cf7b
Fix MSTest analyzer warning MSTEST0037
claude Nov 15, 2025
d11211a
Add XML documentation to all public APIs and remove NoWarn directive
claude Nov 15, 2025
94ddecf
Add XML documentation to Brushfire namespace classes
claude Nov 15, 2025
cc52422
Update System.Drawing.Common to version 10.0.0
claude Nov 15, 2025
395ba89
Install libgdiplus for System.Drawing.Common on Linux
claude Nov 15, 2025
fbf499c
Update all NuGet packages to latest versions
claude Nov 15, 2025
1bbd792
Fix security vulnerabilities in transitive dependencies
claude Nov 15, 2025
67a0cd5
Add ldconfig after libgdiplus installation
claude Nov 15, 2025
02840ac
Add EnableWindowsTargeting to test project for cross-platform System.…
claude Nov 15, 2025
eb7866b
Create symbolic link for libgdiplus to fix System.Drawing.Common on L…
claude Nov 15, 2025
a748856
Enable System.Drawing.Common Unix support via runtime configuration
claude Nov 15, 2025
467bbf4
Use RuntimeHostConfigurationOption for System.Drawing Unix support
claude Nov 18, 2025
274f383
Fix build warnings
claude Nov 18, 2025
b2d775c
Use environment variable for System.Drawing Unix support
claude Nov 19, 2025
70f847b
Add ModuleInitializer to enable System.Drawing Unix support
claude Nov 19, 2025
e0bd7e7
Convert StringFormat to lazy initialization
claude Nov 19, 2025
4b9a8d3
Skip System.Drawing visualization tests on non-Windows platforms
claude Nov 19, 2025
9cf19d5
Use System.Drawing.Common 6.0.0 for cross-platform support
claude Nov 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 20 additions & 22 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,32 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: 10.0.x

- name: Check Tag
id: check-tag
run: |
if [[ v${{ github.event.ref }} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo ::set-output name=match::true
echo "match=true" >> $GITHUB_OUTPUT
fi


- name: Install libgdiplus
run: |
sudo apt-get update
sudo apt-get install -y libgdiplus
sudo ldconfig
# Create symbolic link for .NET runtime to find the library
sudo ln -sf /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll

- name: Run Unit Tests
run: |
dotnet restore
dotnet build
export DOTNET_SYSTEM_DRAWING_ENABLEUNIXSUPPORT=1
dotnet test test/NosCore.PathFinder.Tests -v m

- name: Build Artifact
Expand All @@ -39,23 +48,12 @@ jobs:
dotnet build -c Release
dotnet pack -c Release -o /tmp/nupkgs -v m -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
dotnet nuget push /tmp/nupkgs/NosCore.PathFinder.${{github.event.ref}}.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}}
echo ::set-output name=ARTIFACT_PATH::/tmp/nupkgs/NosCore.PathFinder.${{github.event.ref}}.nupkg
echo ::set-output name=ARTIFACT_NAME::NosCore.PathFinder.${{github.event.ref}}.nupkg

- name: Gets Latest Release
if: steps.check-tag.outputs.match == 'true'
id: latest_release_info
uses: jossef/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
echo "ARTIFACT_PATH=/tmp/nupkgs/NosCore.PathFinder.${{github.event.ref}}.nupkg" >> $GITHUB_OUTPUT
echo "ARTIFACT_NAME=NosCore.PathFinder.${{github.event.ref}}.nupkg" >> $GITHUB_OUTPUT

- name: Upload Release Asset
if: steps.check-tag.outputs.match == 'true'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
uses: softprops/action-gh-release@v2
with:
upload_url: ${{ steps.latest_release_info.outputs.upload_url }}
asset_path: ${{ steps.build_artifact.outputs.ARTIFACT_PATH }}
asset_name: ${{ steps.build_artifact.outputs.ARTIFACT_NAME }}
asset_content_type: application/zip
files: ${{ steps.build_artifact.outputs.ARTIFACT_PATH }}
token: ${{ secrets.REPO_TOKEN }}
4 changes: 4 additions & 0 deletions src/NosCore.PathFinder.Gui/Database/DataAccessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public class DataAccessHelper
/// </summary>
public DbContext CreateContext()
{
if (_option == null)
{
throw new InvalidOperationException("Database options must be initialized before creating a context. Call Initialize() first.");
}
return new NosCoreContext(_option);
}

Expand Down
2 changes: 1 addition & 1 deletion src/NosCore.PathFinder.Gui/Database/NosCoreContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace NosCore.PathFinder.Gui.Database
{
public class NosCoreContext : DbContext
{
public NosCoreContext(DbContextOptions? options) : base(options)
public NosCoreContext(DbContextOptions options) : base(options)
{
}

Expand Down
11 changes: 6 additions & 5 deletions src/NosCore.PathFinder.Gui/NosCore.PathFinder.Gui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<TieredCompilation>true</TieredCompilation>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
Expand All @@ -14,13 +14,14 @@

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.2.0" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.14.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<PackageReference Include="NosCore.Dao" Version="2.0.0" />
<PackageReference Include="NosCore.Shared" Version="3.4.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0-rc.2" />
<PackageReference Include="OpenTK.NetStandard" Version="1.0.5.32" />
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
</ItemGroup>

<ItemGroup>
Expand Down
29 changes: 28 additions & 1 deletion src/NosCore.PathFinder/Brushfire/BrushFire.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@

namespace NosCore.PathFinder.Brushfire
{
/// <summary>
/// Represents a brushfire pathfinding data structure containing pre-computed distance information.
/// </summary>
public readonly struct BrushFire
{

/// <summary>
/// Initializes a new instance of the <see cref="BrushFire"/> struct.
/// </summary>
/// <param name="origin">The origin point of the brushfire.</param>
/// <param name="brushFireGrid">The grid containing computed node information.</param>
/// <param name="width">The width of the grid.</param>
/// <param name="length">The length of the grid.</param>
public BrushFire((short X, short Y) origin, Dictionary<(short X, short Y), Node?> brushFireGrid, short width,
short length)
{
Expand All @@ -20,14 +29,32 @@ public BrushFire((short X, short Y) origin, Dictionary<(short X, short Y), Node?
Width = width;
}

/// <summary>
/// Gets the origin point of the brushfire.
/// </summary>
public (short X, short Y) Origin { get; }

/// <summary>
/// Gets the grid containing computed node information.
/// </summary>
public Dictionary<(short X, short Y), Node?> Grid { get; }

/// <summary>
/// Gets the length of the grid.
/// </summary>
public short Length { get; }

/// <summary>
/// Gets the width of the grid.
/// </summary>
public short Width { get; }

/// <summary>
/// Gets the value at the specified coordinates.
/// </summary>
/// <param name="x">The X coordinate.</param>
/// <param name="y">The Y coordinate.</param>
/// <returns>The value at the specified position, or null if not found.</returns>
public double? this[short x, short y] => Grid.ContainsKey((x, y)) ? Grid[(x, y)]?.Value : null;
}
}
18 changes: 18 additions & 0 deletions src/NosCore.PathFinder/Brushfire/IMapGridExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

namespace NosCore.PathFinder.Brushfire
{
/// <summary>
/// Extension methods for IMapGrid interface providing neighbor and brushfire functionality.
/// </summary>
public static class IMapGridExtension
{
private static readonly List<(short X, short Y)> Neighbours = new List<(short, short)> {
Expand All @@ -18,6 +21,13 @@ public static class IMapGridExtension
(-1, 1), (0, 1), (1, 1)
};

/// <summary>
/// Gets the neighboring cells of a given cell in the grid.
/// </summary>
/// <param name="grid">The map grid.</param>
/// <param name="cell">The cell to get neighbors for.</param>
/// <param name="includeWalls">If true, includes non-walkable cells; otherwise, only returns walkable neighbors.</param>
/// <returns>An enumerable sequence of neighboring cell positions.</returns>
public static IEnumerable<(short X, short Y)> GetNeighbors(this IMapGrid grid, (short X, short Y) cell, bool includeWalls = false)
{
return Neighbours.Where(delta =>
Expand All @@ -31,6 +41,14 @@ public static class IMapGridExtension
}).Select(delta => ((short)(cell.X + delta.X), (short)(cell.Y + delta.Y)));
}

/// <summary>
/// Computes a brushfire distance map from a given origin point.
/// </summary>
/// <param name="mapGrid">The map grid to compute the brushfire on.</param>
/// <param name="user">The origin point for the brushfire.</param>
/// <param name="heuristic">The heuristic to use for distance calculations.</param>
/// <param name="maxDistance">The maximum distance to compute (default is 22).</param>
/// <returns>A BrushFire structure containing pre-computed distance information.</returns>
public static BrushFire LoadBrushFire(this IMapGrid mapGrid, (short X, short Y) user, IHeuristic heuristic, short maxDistance = 22)
{
if (user.X < 0 || user.X >= mapGrid.Width ||
Expand Down
18 changes: 18 additions & 0 deletions src/NosCore.PathFinder/Brushfire/JumpNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,34 @@

namespace NosCore.PathFinder.Brushfire
{
/// <summary>
/// Represents a node used in Jump Point Search pathfinding algorithm.
/// Extends the base Node class with additional properties for JPS.
/// </summary>
public class JumpNode : Node
{
/// <summary>
/// Initializes a new instance of the <see cref="JumpNode"/> class.
/// </summary>
/// <param name="position">The position of the node in the grid.</param>
/// <param name="value">The value associated with the node.</param>
public JumpNode((short X, short Y) position, double? value) : base(position, value)
{
}

/// <summary>
/// Gets or sets the G score (cost from start to this node).
/// </summary>
public double G { get; set; }

/// <summary>
/// Gets or sets the H score (heuristic cost from this node to the goal).
/// </summary>
public double? H { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this node has been opened for evaluation.
/// </summary>
public bool Opened { get; set; }
}
}
25 changes: 24 additions & 1 deletion src/NosCore.PathFinder/Brushfire/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,45 @@

namespace NosCore.PathFinder.Brushfire
{
/// <summary>
/// Represents a node in the pathfinding grid with position and value information.
/// </summary>
public class Node
{
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class.
/// </summary>
/// <param name="position">The position of the node in the grid.</param>
/// <param name="value">The value associated with the node.</param>
public Node((short X, short Y) position, double? value)
{
Value = value;
Position = position;
}


/// <summary>
/// Gets or sets a value indicating whether this node has been processed and closed.
/// </summary>
public bool Closed { get; set; }

/// <summary>
/// Gets or sets the parent node in the path.
/// </summary>
public Node? Parent { get; set; }

/// <summary>
/// Gets or sets the F score (G + H) used in A* pathfinding.
/// </summary>
public double F { get; set; }

/// <summary>
/// Gets or sets the position of the node in the grid.
/// </summary>
public (short X, short Y) Position { get; set; }

/// <summary>
/// Gets or sets the value associated with this node.
/// </summary>
public double? Value { get; set; }
}
}
8 changes: 8 additions & 0 deletions src/NosCore.PathFinder/Heuristic/OctileDistanceHeuristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@

namespace NosCore.PathFinder.Heuristic
{
/// <summary>
/// Heuristic that calculates octile distance (diagonal distance allowing 8-directional movement).
/// This heuristic is optimal for grids that allow diagonal movement at sqrt(2) cost.
/// </summary>
public class OctileDistanceHeuristic : IHeuristic
{
/// <summary>
/// The square root of 2, used as the cost multiplier for diagonal movement.
/// </summary>
public readonly double Sqrt2 = Math.Sqrt(2);

/// <inheritdoc />
public double GetDistance((short X, short Y) fromValuedCell, (short X, short Y) toValuedCell)
{
var iDx = Math.Abs(fromValuedCell.X - toValuedCell.X);
Expand Down
9 changes: 9 additions & 0 deletions src/NosCore.PathFinder/Interfaces/IHeuristic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@

namespace NosCore.PathFinder.Interfaces
{
/// <summary>
/// Interface for heuristic distance calculation used in pathfinding algorithms.
/// </summary>
public interface IHeuristic
{
/// <summary>
/// Calculates the estimated distance between two points.
/// </summary>
/// <param name="from">The starting point coordinates.</param>
/// <param name="to">The ending point coordinates.</param>
/// <returns>The estimated distance between the two points.</returns>
public double GetDistance((short X, short Y) from, (short X, short Y) to);
}
}
24 changes: 24 additions & 0 deletions src/NosCore.PathFinder/Interfaces/IMapGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,35 @@

namespace NosCore.PathFinder.Interfaces
{
/// <summary>
/// Interface representing a grid-based map for pathfinding.
/// </summary>
public interface IMapGrid
{
/// <summary>
/// Gets the width of the map grid.
/// </summary>
public short Width { get; }

/// <summary>
/// Gets the height of the map grid.
/// </summary>
public short Height { get; }

/// <summary>
/// Gets the cell value at the specified coordinates.
/// </summary>
/// <param name="x">The X coordinate.</param>
/// <param name="y">The Y coordinate.</param>
/// <returns>The byte value representing the cell at the given position.</returns>
public byte this[short x, short y] { get; }

/// <summary>
/// Determines whether the specified position is walkable.
/// </summary>
/// <param name="currentX">The X coordinate to check.</param>
/// <param name="currentY">The Y coordinate to check.</param>
/// <returns>True if the position is walkable; otherwise, false.</returns>
bool IsWalkable(short currentX, short currentY);
}
}
9 changes: 9 additions & 0 deletions src/NosCore.PathFinder/Interfaces/IPathfinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@

namespace NosCore.PathFinder.Interfaces
{
/// <summary>
/// Interface for pathfinding algorithms that find paths on a grid-based map.
/// </summary>
public interface IPathfinder
{
/// <summary>
/// Finds a path between two points on the map grid.
/// </summary>
/// <param name="start">The starting coordinates (X, Y).</param>
/// <param name="end">The ending coordinates (X, Y).</param>
/// <returns>An enumerable sequence of coordinates representing the path from start to end.</returns>
IEnumerable<(short X, short Y)> FindPath((short X, short Y) start, (short X, short Y) end);
}
}
Loading
Loading