diff --git a/JoshHeaps.Net/Services/Implementations/ChessEngineFactory.cs b/JoshHeaps.Net/Services/Implementations/ChessEngineFactory.cs
index 2ccd590..6d18441 100644
--- a/JoshHeaps.Net/Services/Implementations/ChessEngineFactory.cs
+++ b/JoshHeaps.Net/Services/Implementations/ChessEngineFactory.cs
@@ -19,6 +19,16 @@ public sealed class ChessEngineOptions
public const string SectionName = "ChessEngine";
public ChessEngineKind Engine { get; set; } = ChessEngineKind.Stockfish;
+
+ ///
+ /// Absolute path to the learned-weights file. Leave null to default to
+ /// {ContentRoot}/chess-data/learned-weights.txt (fine for local dev). In
+ /// production set this to a stable, service-writable location OUTSIDE the deploy
+ /// directory (e.g. /var/lib/joshheaps/chess-data/learned-weights.txt) so the
+ /// trained weights survive deploys and avoid deploy-user vs service-user permission
+ /// clashes. Override via the ChessEngine__WeightsPath environment variable.
+ ///
+ public string? WeightsPath { get; set; }
}
/// Creates the configured per game.
diff --git a/JoshHeaps.Net/Services/Implementations/LearnedWeightsStore.cs b/JoshHeaps.Net/Services/Implementations/LearnedWeightsStore.cs
index 992f387..1a1f72b 100644
--- a/JoshHeaps.Net/Services/Implementations/LearnedWeightsStore.cs
+++ b/JoshHeaps.Net/Services/Implementations/LearnedWeightsStore.cs
@@ -1,5 +1,6 @@
using JoshHeaps.Net.Models;
using JoshHeaps.Net.Services.Interfaces;
+using Microsoft.Extensions.Options;
namespace JoshHeaps.Net.Services.Implementations;
@@ -16,11 +17,30 @@ public sealed class LearnedWeightsStore : ILearnedWeightsStore
public string WeightsFilePath { get; }
- public LearnedWeightsStore(IHostEnvironment env)
+ public LearnedWeightsStore(IHostEnvironment env, IOptions options, ILogger logger)
{
- WeightsFilePath = Path.Combine(env.ContentRootPath, "chess-data", "learned-weights.txt");
- Directory.CreateDirectory(Path.GetDirectoryName(WeightsFilePath)!);
- CustomChessEngine.NativeMethods.learned_load(WeightsFilePath);
+ // Prefer the configured path (production points this outside the deploy dir); fall
+ // back to the content root for local dev.
+ var configured = options.Value.WeightsPath;
+ WeightsFilePath = string.IsNullOrWhiteSpace(configured)
+ ? Path.Combine(env.ContentRootPath, "chess-data", "learned-weights.txt")
+ : configured;
+
+ // A missing/unwritable/misconfigured path must not take down the whole app — the
+ // learned engine just plays from a neutral table and can't persist training.
+ try
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(WeightsFilePath)!);
+ CustomChessEngine.NativeMethods.learned_load(WeightsFilePath);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex,
+ "Could not initialize the learned-weights store at {Path}. The learned engine will " +
+ "play from a neutral table and training will not persist. In production set " +
+ "ChessEngine:WeightsPath (env ChessEngine__WeightsPath) to a service-writable directory.",
+ WeightsFilePath);
+ }
}
public nint CreateTrainer() => CustomChessEngine.NativeMethods.trainer_create();