Skip to content

Commit ecee7c6

Browse files
committed
Refactor plotting, tournament reload, and CSS fallback
- Added InvalidateTournament and improved reload logic in Manager - Enhanced engine logo rendering and layout in UI - Synced chart X-axes with opening moves; updated FEN after openings - Refactored LivePlot X-axis logic and margins; added SetStartMove - Added dev CSS isolation fallback in Program.cs for static files - Ensured TournamentService runner state is always fresh - Fixed chart update bug and improved X-axis scaling in chessInterop.js
1 parent acf0880 commit ecee7c6

7 files changed

Lines changed: 119 additions & 53 deletions

File tree

ChessLibrary/Tournament/Tournament.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ module Manager =
368368

369369
member _.Cancel() = cts.Cancel()
370370

371+
member _.InvalidateTournament() =
372+
tournamentLoaded <- false
373+
371374
member _.Tournament() =
372375
//check if tournament is empty and reload if necessary
373376
if not tournamentLoaded then

WebGUI/Components/Layout/TournamentLayout/EngineStatsModern.razor

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77
<!-- Row 1: Engine Headers & Main Logo -->
88
<div class="engine-header-cell @(Engine1ToMove ? "active" : "")">
99
<span class="side-label">White</span>
10-
<div class="engine-logo-wrapper" style="@LogoWrapperStyle">
11-
<img src="@Engine1Logo" alt="@Engine1Name" class="engine-logo" />
10+
<div class="engine-logo-wrapper" style="@(LogoWrapperStyle) overflow:hidden;">
11+
<img src="@Engine1Logo" alt="@Engine1Name" class="engine-logo" style="max-width:100%;max-height:100%;object-fit:contain;" />
1212
</div>
1313
<h3 class="engine-name">@Engine1Name</h3>
1414
<span class="engine-version">@Engine1Version</span>
1515
</div>
16-
<div class="main-logo-wrapper">
17-
<img src="@MainLogo" alt="EngineBattle" class="main-logo" />
16+
<div class="main-logo-wrapper" style="overflow:hidden;max-width:100%;">
17+
<img src="@MainLogo" alt="EngineBattle" class="main-logo" style="max-width:300px;max-height:200px;object-fit:contain;" />
1818
<span class="powered-by">Powered by EngineBattle</span>
1919
<span class="author">Lars E. Pedersen</span>
2020
</div>
2121
<div class="engine-header-cell @(Engine1ToMove ? "" : "active")">
2222
<span class="side-label">Black</span>
23-
<div class="engine-logo-wrapper" style="@LogoWrapperStyle">
24-
<img src="@Engine2Logo" alt="@Engine2Name" class="engine-logo" />
23+
<div class="engine-logo-wrapper" style="@(LogoWrapperStyle) overflow:hidden;">
24+
<img src="@Engine2Logo" alt="@Engine2Name" class="engine-logo" style="max-width:100%;max-height:100%;object-fit:contain;" />
2525
</div>
2626
<h3 class="engine-name">@Engine2Name</h3>
2727
<span class="engine-version">@Engine2Version</span>

WebGUI/Components/Pages/TournamentPages/Tournaments.razor

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,22 +1315,18 @@
13151315
moveHistory = string.Empty;
13161316
openingMoves = string.Empty;
13171317
var isWhite = true;
1318-
await evalList.ClearData(whitePlayer, blackPlayer);
1319-
await nodeList.ClearData(whitePlayer, blackPlayer);
1320-
await npsList.ClearData(whitePlayer, blackPlayer);
1321-
await epsList.ClearData(whitePlayer, blackPlayer);
1322-
await timeUsageList.ClearData(whitePlayer, blackPlayer);
1318+
var startMove = moves.Count > 0 ? (moves.Count + 1) / 2 + 1 : 1;
1319+
await evalList.ClearData(whitePlayer, blackPlayer, startMove);
1320+
await nodeList.ClearData(whitePlayer, blackPlayer, startMove);
1321+
await npsList.ClearData(whitePlayer, blackPlayer, startMove);
1322+
await epsList.ClearData(whitePlayer, blackPlayer, startMove);
1323+
await timeUsageList.ClearData(whitePlayer, blackPlayer, startMove);
13231324

13241325
if (moves.Count != 0)
13251326
{
13261327
foreach (var move in moves)
13271328
{
13281329
CreateOpeningMoves(move);
1329-
evalList.AddEvalData(isWhite, false, 0.0);
1330-
nodeList.AddData(isWhite, 0.0, false);
1331-
npsList.AddData(isWhite, 0.0, true);
1332-
epsList.AddData(isWhite, 0.0, true);
1333-
timeUsageList.AddData(isWhite, 0.0, false);
13341330
isWhite = !isWhite;
13351331
await Task.Delay(tournament.MinMoveTimeInMS);
13361332
await InvokeAsync(StateHasChanged);
@@ -1339,6 +1335,7 @@
13391335
await pvBoardDuo.SetPVMoveWithAnnotation(move, true);
13401336
}
13411337
}
1338+
13421339
if (moves.Count > 0)
13431340
{
13441341
var move = moves.Last();

WebGUI/Plotting/LivePlot.cs

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,43 @@ public class LivePlot
1313
public List<double> PlayerBlackData { get; set; } = new List<double>();
1414
public List<int> MoveElements { get; set; } = Enumerable.Range(1, maxNumberOfMoves).ToList();
1515

16+
public void SetStartMove(int moveNumber)
17+
{
18+
MoveElements = Enumerable.Range(moveNumber, maxNumberOfMoves).ToList();
19+
}
20+
21+
private object BuildXAxis(bool showLine = false, bool showZeroline = false)
22+
{
23+
var start = MoveElements.Count > 0 ? MoveElements[0] : 1;
24+
var hasData = PlayerWhiteData.Count > 0 || PlayerBlackData.Count > 0;
25+
if (hasData)
26+
return new
27+
{
28+
tickfont = new { size = fontSizeTickFont },
29+
showgrid = false,
30+
zeroline = false,
31+
showline = showLine,
32+
showzeroline = showZeroline,
33+
color = whiteColor,
34+
tick0 = 0,
35+
dtick = 1,
36+
rangemode = "nonnegative",
37+
};
38+
return new
39+
{
40+
tickfont = new { size = fontSizeTickFont },
41+
showgrid = false,
42+
zeroline = false,
43+
showline = showLine,
44+
showzeroline = showZeroline,
45+
color = whiteColor,
46+
tick0 = 0,
47+
dtick = 1,
48+
rangemode = "nonnegative",
49+
range = new[] { start - 1, start + 11 },
50+
};
51+
}
52+
1653
private const double maxEvalValue = 20;
1754
private const double minEvalValue = -20;
1855
private const int maxNumberOfMoves = 500;
@@ -46,7 +83,7 @@ public LivePlot(IJSObjectReference chessMod, ElementReference chart, string whit
4683
margin = new
4784
{
4885
l = 50,
49-
r = 15,
86+
r = 30,
5087
b = 30,
5188
t = 50,
5289
pad = 2,
@@ -95,7 +132,7 @@ public LivePlot(IJSObjectReference chessMod, ElementReference chart, string whit
95132

96133
}
97134

98-
public async Task ClearData(string white, string black)
135+
public async Task ClearData(string white, string black, int startMove = 1)
99136
{
100137
try
101138
{
@@ -105,7 +142,7 @@ public async Task ClearData(string white, string black)
105142
liveUpdateStarted = false;
106143
PlayerWhite = white;
107144
PlayerBlack = black;
108-
MoveElements = Enumerable.Range(1, maxNumberOfMoves).ToList();
145+
MoveElements = Enumerable.Range(startMove, maxNumberOfMoves).ToList();
109146
await Task.WhenAll(
110147
SetEvalChartData(),
111148
SetChartNodeData(),
@@ -187,13 +224,7 @@ public void AddData(bool isWhite, double yData, bool isNPS)
187224

188225
public async Task SetEvalChartData()
189226
{
190-
var xaxis = new
191-
{
192-
tickfont = new { size = fontSizeTickFont },
193-
showgrid = false,
194-
zeroline = false,
195-
color = whiteColor
196-
};
227+
var xaxis = BuildXAxis();
197228

198229
var yaxis = new
199230
{
@@ -309,13 +340,7 @@ public async Task SetChartNodeData()
309340

310341
public async Task SetSingleChartNodeData()
311342
{
312-
var xaxis = new
313-
{
314-
tickfont = new { size = fontSizeTickFont },
315-
showgrid = false,
316-
zeroline = false,
317-
color = whiteColor
318-
};
343+
var xaxis = BuildXAxis();
319344

320345
var yaxis = new
321346
{
@@ -412,15 +437,7 @@ public async Task SetSingleChartNodeData()
412437

413438
public async Task SetDoubleChartNodeData()
414439
{
415-
var xaxis = new
416-
{
417-
tickfont = new { size = fontSizeTickFont },
418-
showgrid = false,
419-
zeroline = false,
420-
showline = false,
421-
showzeroline = false,
422-
color = whiteColor
423-
};
440+
var xaxis = BuildXAxis(showLine: false, showZeroline: false);
424441

425442
var yaxis = new
426443
{
@@ -539,13 +556,7 @@ public async Task SetDoubleChartNodeData()
539556

540557
public async Task SetChartTimeUsageData()
541558
{
542-
var xaxis = new
543-
{
544-
tickfont = new { size = fontSizeTickFont },
545-
showgrid = false,
546-
zeroline = false,
547-
color = whiteColor
548-
};
559+
var xaxis = BuildXAxis();
549560

550561
var yaxis = new
551562
{

WebGUI/Program.cs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,36 @@ static int FindAvailablePort(int startPort)
2626
return 0;
2727
}
2828

29+
/// <summary>
30+
/// Finds the CSS isolation bundle directory in obj/ for the active build configuration.
31+
/// Returns empty string if not found (e.g. in published builds where the bundle is already in wwwroot).
32+
/// </summary>
33+
static string FindCssBundleDir(IWebHostEnvironment env)
34+
{
35+
var cssName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".styles.css";
36+
37+
// Published builds already have the CSS in wwwroot — no fallback needed
38+
if (File.Exists(Path.Combine(env.WebRootPath, cssName))) return "";
39+
40+
// In dev mode, search obj/ for the bundle directory matching the build configuration
41+
var contentRoot = env.ContentRootPath;
42+
#if DEBUG
43+
var configDir = Path.Combine(contentRoot, "obj", "Debug");
44+
#else
45+
var configDir = Path.Combine(contentRoot, "obj", "Release");
46+
#endif
47+
if (!Directory.Exists(configDir))
48+
configDir = Path.Combine(contentRoot, "obj");
49+
if (!Directory.Exists(configDir)) return "";
50+
51+
try
52+
{
53+
var source = Directory.GetFiles(configDir, cssName, SearchOption.AllDirectories).FirstOrDefault();
54+
return source != null ? Path.GetDirectoryName(source) : "";
55+
}
56+
catch { return ""; }
57+
}
58+
2959
static void EnsureTournamentJsonIsLoaded()
3060
{
3161
string currentDir = AppPaths.BaseDir;
@@ -129,10 +159,23 @@ static void EnsureTournamentJsonIsLoaded()
129159
// Get the full framework description
130160
Console.WriteLine("Framework: " + RuntimeInformation.FrameworkDescription);
131161

132-
app.UseStaticFiles();
162+
app.MapStaticAssets();
163+
164+
// CSS isolation fallback: serve CSS bundle directly from obj/ if not in wwwroot (dev mode with stale builds)
165+
var cssBundleDir = FindCssBundleDir(app.Environment);
166+
if (!string.IsNullOrEmpty(cssBundleDir))
167+
{
168+
app.UseStaticFiles(new StaticFileOptions
169+
{
170+
FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(cssBundleDir),
171+
ContentTypeProvider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider(
172+
new Dictionary<string, string> { [".css"] = "text/css" })
173+
});
174+
}
133175
app.UseAntiforgery();
134176
app.MapRazorComponents<App>()
135-
.AddInteractiveServerRenderMode();
177+
.AddInteractiveServerRenderMode()
178+
.WithStaticAssets();
136179
EnsureTournamentJsonIsLoaded();
137180

138181
bool hasExplicitUrls = args.Any(a => a.StartsWith("--urls", StringComparison.OrdinalIgnoreCase))

WebGUI/Services/TournamentService.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ public void Cancel()
5252

5353
public Tournament.Manager.Runner GetConfigRunner(ILogger logger)
5454
{
55-
if (_runner != null) return _runner;
55+
if (_runner != null)
56+
{
57+
if (!IsRunning) _runner.InvalidateTournament();
58+
return _runner;
59+
}
5660
_runner = new Tournament.Manager.Runner(logger, HandleUpdate, true, false);
5761
return _runner;
5862
}

WebGUI/wwwroot/js/chessInterop.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,9 +794,17 @@ export function updateLineEvalChartData(chart, trace, index) {
794794
try {
795795
var x = trace.x;
796796
var y = trace.y;
797-
var x0 = [[x[x.length - 1]]];
797+
var x0 = [[x[y.length - 1]]];
798798
var y0 = [[y[y.length - 1]]];
799799
Plotly.extendTraces(chart, { x: x0, y: y0 }, [index]);
800+
var lastX = x[y.length - 1];
801+
var firstX = x[0];
802+
var span = lastX - firstX + 2;
803+
var raw = span / 10;
804+
var nice = [1, 2, 5, 10, 20, 50, 100];
805+
var dtick = nice.find(n => n >= raw) || nice[nice.length - 1];
806+
if (chart.layout.xaxis.dtick !== dtick)
807+
Plotly.relayout(chart, { 'xaxis.dtick': dtick });
800808
} catch (error) {
801809
console.error(error);
802810
}

0 commit comments

Comments
 (0)