From ae95b15ee600df7ed5d16e8bb3cf578771d94054 Mon Sep 17 00:00:00 2001 From: jheaps Date: Mon, 4 Aug 2025 14:20:08 -0600 Subject: [PATCH] Revert "Create db and db access. Add json converter for board array. Add tests for json conversion" This reverts commit 7a532e52e13e760764174f95d6e27e197fcf838d. --- .../JoshHeaps.Net.Tests.csproj | 28 ---- .../JsonTests/JsonConversionTests.cs | 100 -------------- JoshHeaps.Net.sln | 6 - JoshHeaps.Net/Controllers/ChessController.cs | 6 +- JoshHeaps.Net/DAL/ChessDbAccess.cs | 40 ------ JoshHeaps.Net/DAL/ChessDbContext.cs | 23 ---- JoshHeaps.Net/Databases/chess.db | Bin 4096 -> 0 bytes JoshHeaps.Net/Databases/chess.db-shm | Bin 32768 -> 0 bytes JoshHeaps.Net/Databases/chess.db-wal | Bin 28872 -> 0 bytes JoshHeaps.Net/JoshHeaps.Net.csproj | 10 +- JoshHeaps.Net/Models/GameState.cs | 8 -- JoshHeaps.Net/Models/GameStateEntity.cs | 8 -- JoshHeaps.Net/Program.cs | 10 -- .../Services/Implementations/Stockfish.cs | 129 ++++++++++++++++- .../Utilities/ChessBoardConverter.cs | 80 ----------- JoshHeaps.Net/Utilities/StockfishHelpers.cs | 130 ------------------ JoshHeaps.Net/appsettings.json | 5 +- Tests/Tests.csproj | 10 -- 18 files changed, 132 insertions(+), 461 deletions(-) delete mode 100644 JoshHeaps.Net.Tests/JoshHeaps.Net.Tests.csproj delete mode 100644 JoshHeaps.Net.Tests/JsonTests/JsonConversionTests.cs delete mode 100644 JoshHeaps.Net/DAL/ChessDbAccess.cs delete mode 100644 JoshHeaps.Net/DAL/ChessDbContext.cs delete mode 100644 JoshHeaps.Net/Databases/chess.db delete mode 100644 JoshHeaps.Net/Databases/chess.db-shm delete mode 100644 JoshHeaps.Net/Databases/chess.db-wal delete mode 100644 JoshHeaps.Net/Models/GameStateEntity.cs delete mode 100644 JoshHeaps.Net/Utilities/ChessBoardConverter.cs delete mode 100644 JoshHeaps.Net/Utilities/StockfishHelpers.cs diff --git a/JoshHeaps.Net.Tests/JoshHeaps.Net.Tests.csproj b/JoshHeaps.Net.Tests/JoshHeaps.Net.Tests.csproj deleted file mode 100644 index 18e5875..0000000 --- a/JoshHeaps.Net.Tests/JoshHeaps.Net.Tests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net8.0 - enable - enable - - false - true - - - - - - - - - - - - - - - - - - - diff --git a/JoshHeaps.Net.Tests/JsonTests/JsonConversionTests.cs b/JoshHeaps.Net.Tests/JsonTests/JsonConversionTests.cs deleted file mode 100644 index dde03e0..0000000 --- a/JoshHeaps.Net.Tests/JsonTests/JsonConversionTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using JoshHeaps.Net.Models; -using JoshHeaps.Net.Services.Implementations; -using JoshHeaps.Net.Utilities; -using System.Text.Json; - -namespace JoshHeaps.Net.Tests.JsonTests; - -internal class JsonConversionTests -{ - [Test] - public void ConvertBoard_WhenGivenStartingBoard_CanConvertToAndFromJson() - { - // Arrange - var gameState = new ChessService().CreateNewGame(); - - // Act - var serializedGameState = JsonSerializer.Serialize(gameState); - var deserializedGameState = JsonSerializer.Deserialize(serializedGameState); - - Assert.That(deserializedGameState, Is.Not.Null); - - var originBoard = gameState.Board; - var convertedBoard = deserializedGameState!.Board; - - // Assert - AssertBoardEquality(originBoard, convertedBoard); - } - - [Test] - public void ConvertBoard_WhenGivenValidBoard_CanConvertToAndFromJson() - { - // Arrange - ChessPiece?[,] originBoard = new ChessPiece?[8,8]; - - originBoard[0, 0] = new ChessPiece( - "rook1", - PieceType.Rook, - PieceColor.White, - new Position(0, 0) - ); - originBoard[4,4] = new ChessPiece( - "pawn1", - PieceType.Pawn, - PieceColor.Black, - new Position(4, 4) - ); - - // Act - var serializedBoard = JsonSerializer.Serialize(originBoard, _options); - var convertedBoard = JsonSerializer.Deserialize(serializedBoard, _options); - - // Assert - AssertBoardEquality(originBoard, convertedBoard!); - } - - private static void AssertBoardEquality(ChessPiece?[,]? original, ChessPiece?[,]? converted) - { - Assert.Multiple(() => - { - Assert.That(original, Is.Not.Null); - Assert.That(converted, Is.Not.Null); - }); - - Assert.That(original!.LongLength, Is.EqualTo(converted!.LongLength)); - - for (int i = 0; i < original.GetLength(0); i++) - { - for (int j = 0; j < original.GetLength(1); j++) - { - var originalPiece = original[i, j]; - var convertedPiece = converted[i, j]; - AssertPieceEquality(originalPiece, convertedPiece); - } - } - } - - private static void AssertPieceEquality(ChessPiece? original, ChessPiece? converted) - { - if (original is null) - { - Assert.That(converted, Is.Null); - return; - } - - Assert.Multiple(() => - { - Assert.That(converted, Is.Not.Null); - Assert.That(original!.Id, Is.EqualTo(converted!.Id)); - Assert.That(original.Type, Is.EqualTo(converted.Type)); - Assert.That(original.Color, Is.EqualTo(converted.Color)); - Assert.That(original.Position.Row, Is.EqualTo(converted.Position.Row)); - Assert.That(original.Position.Col, Is.EqualTo(converted.Position.Col)); - }); - } - - private static readonly JsonSerializerOptions _options = new() - { - Converters = { new ChessBoardConverter() } - }; -} diff --git a/JoshHeaps.Net.sln b/JoshHeaps.Net.sln index 05f0a75..1d468c4 100644 --- a/JoshHeaps.Net.sln +++ b/JoshHeaps.Net.sln @@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JoshHeaps.Net", "JoshHeaps. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{82206AD3-19DF-4DDA-9647-02B691B78CE8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JoshHeaps.Net.Tests", "JoshHeaps.Net.Tests\JoshHeaps.Net.Tests.csproj", "{769155E1-1686-402C-9618-272D20DC3F96}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,10 +21,6 @@ Global {82206AD3-19DF-4DDA-9647-02B691B78CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU {82206AD3-19DF-4DDA-9647-02B691B78CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU {82206AD3-19DF-4DDA-9647-02B691B78CE8}.Release|Any CPU.Build.0 = Release|Any CPU - {769155E1-1686-402C-9618-272D20DC3F96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {769155E1-1686-402C-9618-272D20DC3F96}.Debug|Any CPU.Build.0 = Debug|Any CPU - {769155E1-1686-402C-9618-272D20DC3F96}.Release|Any CPU.ActiveCfg = Release|Any CPU - {769155E1-1686-402C-9618-272D20DC3F96}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JoshHeaps.Net/Controllers/ChessController.cs b/JoshHeaps.Net/Controllers/ChessController.cs index e582ea2..b19b3a0 100644 --- a/JoshHeaps.Net/Controllers/ChessController.cs +++ b/JoshHeaps.Net/Controllers/ChessController.cs @@ -9,10 +9,7 @@ namespace JoshHeaps.Net.Controllers; [ApiController] [Route("api/[controller]")] -public class ChessController( - IChessService chessService, - IHubContext chessHub, - IBackgroundTaskQueue queue) : ControllerBase +public class ChessController(IChessService chessService, IHubContext chessHub, IBackgroundTaskQueue queue) : ControllerBase { /// /// Store of ongoing games. @@ -34,7 +31,6 @@ public class ChessController( gameState.IsVsComputer = true; gameState.WhiteJoined = true; gameState.BlackJoined = true; - gameState.ComputerDifficulty = difficulty; Guid playerId = Guid.NewGuid(); Guid computerId = Guid.NewGuid(); var isWhite = Random.Shared.Next(2) == 0; diff --git a/JoshHeaps.Net/DAL/ChessDbAccess.cs b/JoshHeaps.Net/DAL/ChessDbAccess.cs deleted file mode 100644 index 828bcd9..0000000 --- a/JoshHeaps.Net/DAL/ChessDbAccess.cs +++ /dev/null @@ -1,40 +0,0 @@ -using JoshHeaps.Net.Models; -using System.Text.Json; - -namespace JoshHeaps.Net.DAL; - -public class ChessDbAccess(ChessDbContext db) -{ - private static readonly JsonSerializerOptions opts = - new(JsonSerializerDefaults.Web) { WriteIndented = false }; - - public async Task SaveAsync(GameState state, CancellationToken ct = default) - { - var json = JsonSerializer.Serialize(state, opts); - var entity = await db.Games.FindAsync([state.GameId], ct) - ?? new GameStateEntity { GameId = state.GameId }; - - entity.SerializedState = json; - entity.LastMoveUtc = DateTime.UtcNow; - - db.Update(entity); - await db.SaveChangesAsync(ct); - } - - public async Task LoadAsync(Guid id, CancellationToken ct = default) - { - var e = await db.Games.FindAsync([id], ct); - return e is null - ? null - : JsonSerializer.Deserialize(e.SerializedState, opts); - } - - public async Task DeleteAsync(Guid id, CancellationToken ct = default) - { - if (await db.Games.FindAsync([id], ct) is { } e) - { - db.Remove(e); - await db.SaveChangesAsync(ct); - } - } -} diff --git a/JoshHeaps.Net/DAL/ChessDbContext.cs b/JoshHeaps.Net/DAL/ChessDbContext.cs deleted file mode 100644 index 49fad1a..0000000 --- a/JoshHeaps.Net/DAL/ChessDbContext.cs +++ /dev/null @@ -1,23 +0,0 @@ -using JoshHeaps.Net.Models; -using Microsoft.EntityFrameworkCore; - -namespace JoshHeaps.Net.DAL; - -public class ChessDbContext : DbContext -{ - public ChessDbContext(DbContextOptions opts) : base(opts) { } - - public DbSet Games => Set(); - - protected override void OnModelCreating(ModelBuilder b) - { - // Primary key - b.Entity().HasKey(g => g.GameId); - - // Board + pieces serialized as JSON blobs - b.Entity() - .Property(g => g.SerializedState) - .HasColumnType("TEXT"); - } -} - diff --git a/JoshHeaps.Net/Databases/chess.db b/JoshHeaps.Net/Databases/chess.db deleted file mode 100644 index 0de02ecf623141161c863ee065d9f7dd83cbe849..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|@t|AVoG{WYDWB;*e#Z~}LT1Gs^;YiR2X4j@8YMDIe1RDzwD?*}g& z$C11D4e*NRGNkllWg$#P&H8+Jtf#ldW?b#d-KpBnujS>qn%#>p&!>N<3}d}osW)4n zcz4K~O6y*%^ro~Y1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk|0WYy%-Hu`1h@-o@ z=-$hq{(%tkHiYg9q8l#?EidX{h@kh(%{CpjOP$}BZJhJ&eP7P|*|~Y&Jo$H0~HN5s8{2G8PVtfoNpZj0I)A@Qw^6s`VdRD>=XD6{ayMhbj)&>}r?jg>`;uzaXoK_{z7(exOxfOW?8_~7+_t!7p{U?v0 zJa53>S13QPJ&&NJOL4m0GLOLRcK713xKB - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + @@ -22,7 +17,6 @@ - @@ -30,7 +24,7 @@ - + diff --git a/JoshHeaps.Net/Models/GameState.cs b/JoshHeaps.Net/Models/GameState.cs index ca6b6d4..6e8f468 100644 --- a/JoshHeaps.Net/Models/GameState.cs +++ b/JoshHeaps.Net/Models/GameState.cs @@ -1,8 +1,4 @@ using JoshHeaps.Net.Services.Implementations; -using JoshHeaps.Net.Utilities; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text.Json.Serialization; namespace JoshHeaps.Net.Models; @@ -12,7 +8,6 @@ public class GameState // 8x8 board of references. Null if no piece is present. // row 0 at top -> row 7 at bottom (typical 0-based array). - [JsonConverter(typeof(ChessBoardConverter))] public ChessPiece?[,] Board { get; set; } // Whose turn is it? @@ -47,10 +42,7 @@ public class GameState public Guid BlackPlayerId { get; set; } public bool IsVsComputer { get; set; } = false; - public int ComputerDifficulty { get; set; } = 20; - [JsonIgnore] // <-- exclude from JSON - [NotMapped] // <-- EF won't try to persist it either public Stockfish? Computer { get; set; } // optional: convenience diff --git a/JoshHeaps.Net/Models/GameStateEntity.cs b/JoshHeaps.Net/Models/GameStateEntity.cs deleted file mode 100644 index 266f6f0..0000000 --- a/JoshHeaps.Net/Models/GameStateEntity.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace JoshHeaps.Net.Models; - -public class GameStateEntity -{ - public Guid GameId { get; set; } - public string SerializedState { get; set; } = ""; // JSON - public DateTime LastMoveUtc { get; set; } -} diff --git a/JoshHeaps.Net/Program.cs b/JoshHeaps.Net/Program.cs index 16d31a0..c430c45 100644 --- a/JoshHeaps.Net/Program.cs +++ b/JoshHeaps.Net/Program.cs @@ -1,8 +1,6 @@ -using JoshHeaps.Net.DAL; using JoshHeaps.Net.Hubs; using JoshHeaps.Net.Services.Implementations; using JoshHeaps.Net.Services.Interfaces; -using Microsoft.EntityFrameworkCore; namespace JoshHeaps.Net; @@ -29,17 +27,9 @@ public class Program builder.Services.AddSingleton(); builder.Services.AddSingleton(); - var cs = builder.Configuration.GetConnectionString("ChessDatabase"); - builder.Services.AddDbContext(o => o.UseSqlite(cs)); var app = builder.Build(); - using (var scope = app.Services.CreateScope()) - { - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Database.Migrate(); - } - // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { diff --git a/JoshHeaps.Net/Services/Implementations/Stockfish.cs b/JoshHeaps.Net/Services/Implementations/Stockfish.cs index c50d563..0f68109 100644 --- a/JoshHeaps.Net/Services/Implementations/Stockfish.cs +++ b/JoshHeaps.Net/Services/Implementations/Stockfish.cs @@ -1,10 +1,11 @@ using JoshHeaps.Net.Hubs; using JoshHeaps.Net.Models; using JoshHeaps.Net.Services.Interfaces; -using JoshHeaps.Net.Utilities; using Microsoft.AspNetCore.SignalR; using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; +using System.Text; using System.Threading.Channels; namespace JoshHeaps.Net.Services.Implementations; @@ -123,4 +124,130 @@ public sealed class Stockfish : IAsyncDisposable await chessHub.Clients.Group(state.GameId.ToString()).SendAsync("ReceiveMoveUpdate", state.GameId.ToString(), moveDto, result); } +} + +public static class StockfishHelpers +{ + public static MoveDto ToMoveDto( + this string uci, + GameState gameState, + Guid playerId) + { + int fCol = uci[0] - 'a', fRow = 7 - (uci[1] - '1'); + int tCol = uci[2] - 'a', tRow = 7 - (uci[3] - '1'); + + var piece = gameState.Board[fRow, fCol] + ?? throw new Exception("No piece at source square"); + + PieceType? promo = uci.Length == 5 ? uci[4] switch + { + 'q' => PieceType.Queen, + 'r' => PieceType.Rook, + 'b' => PieceType.Bishop, + 'n' => PieceType.Knight, + _ => null + } : null; + + return new MoveDto + { + GameId = gameState.GameId, + PlayerId = playerId, + PieceId = piece.Id, + TargetRow = tRow, + TargetCol = tCol, + PromotionChoice = promo, + SourceCol = fCol, + SourceRow = fRow, + }; + } + + /// + /// Convert a 2-D board array (rank 8 = row 0, file a = col 0) to a FEN string. + /// Only piece placement + active colour + castling are computed; the rest use + /// safe defaults (-, 0, 1). That is all Stockfish needs. + /// + public static string ToFen(this GameState gs) + { + var sb = new StringBuilder(64); + + /* 1) piece placement */ + for (int row = 0; row < 8; row++) + { + int empty = 0; + + for (int col = 0; col < 8; col++) + { + var p = gs.Board[row, col]; + + if (p is null) + { + empty++; + } + else + { + if (empty > 0) { sb.Append(empty); empty = 0; } + sb.Append(ToFenChar(p)); // ← unchanged helper + } + } + + if (empty > 0) sb.Append(empty); + if (row < 7) sb.Append('/'); + } + + /* 2) active colour */ + sb.Append(gs.CurrentPlayer == PieceColor.White ? " w " : " b "); + + /* 3) castling rights (from GameState flags) */ + sb.Append(GetCastlingFlags(gs)); + + /* 4) en-passant target square */ + sb.Append(' '); + sb.Append(gs.EnPassantTarget.HasValue + ? Alg(gs.EnPassantTarget.Value) + : "-"); + + /* 5-6) half-move clock + full-move number */ + int fullMoves = gs.MoveHistory.Count / 2 + 1; + sb.Append(" 0 ").Append(fullMoves); + + return sb.ToString(); + } + + /* ---------- helpers ---------- */ + + private static string GetCastlingFlags(GameState gs) + { + var flags = new StringBuilder(4); + + if (gs.WhiteCanCastleKingside) flags.Append('K'); + if (gs.WhiteCanCastleQueenside) flags.Append('Q'); + if (gs.BlackCanCastleKingside) flags.Append('k'); + if (gs.BlackCanCastleQueenside) flags.Append('q'); + + return flags.Length == 0 ? "-" : flags.ToString(); + } + + private static string Alg(Position p) + { + char file = (char)('a' + p.Col); + int rank = 8 - p.Row; + return $"{file}{rank}"; + } + + private static char ToFenChar(ChessPiece p) => p switch + { + { Type: PieceType.Pawn, Color: PieceColor.White } => 'P', + { Type: PieceType.Pawn, Color: PieceColor.Black } => 'p', + { Type: PieceType.Knight, Color: PieceColor.White } => 'N', + { Type: PieceType.Knight, Color: PieceColor.Black } => 'n', + { Type: PieceType.Bishop, Color: PieceColor.White } => 'B', + { Type: PieceType.Bishop, Color: PieceColor.Black } => 'b', + { Type: PieceType.Rook, Color: PieceColor.White } => 'R', + { Type: PieceType.Rook, Color: PieceColor.Black } => 'r', + { Type: PieceType.Queen, Color: PieceColor.White } => 'Q', + { Type: PieceType.Queen, Color: PieceColor.Black } => 'q', + { Type: PieceType.King, Color: PieceColor.White } => 'K', + { Type: PieceType.King, Color: PieceColor.Black } => 'k', + _ => throw new ArgumentOutOfRangeException(nameof(p)) + }; } \ No newline at end of file diff --git a/JoshHeaps.Net/Utilities/ChessBoardConverter.cs b/JoshHeaps.Net/Utilities/ChessBoardConverter.cs deleted file mode 100644 index 354159d..0000000 --- a/JoshHeaps.Net/Utilities/ChessBoardConverter.cs +++ /dev/null @@ -1,80 +0,0 @@ -using JoshHeaps.Net.Models; -using System.Text.Json.Serialization; -using System.Text.Json; - -namespace JoshHeaps.Net.Utilities; - -public sealed class ChessBoardConverter - : JsonConverter -{ - public override ChessPiece?[,] Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - List pieces = []; - - if (reader.TokenType != JsonTokenType.StartArray) - throw new JsonException($"Expected start of an array, instead received {reader.TokenType}"); - - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - if (reader.TokenType == JsonTokenType.Null) - { - pieces.Add(null); - continue; - } - - if (reader.TokenType != JsonTokenType.StartObject) - throw new JsonException($"Expected start of an object, instead received {reader.TokenType}"); - - var piece = JsonSerializer.Deserialize(ref reader, options) ?? throw new JsonException("Deserialized piece was null"); - pieces.Add(piece); - - if (reader.TokenType != JsonTokenType.EndObject) - throw new JsonException($"Expected end of an object, instead received {reader.TokenType}"); - } - - if (reader.TokenType != JsonTokenType.EndArray) - throw new JsonException($"Expected end of an array, instead received {reader.TokenType}"); - - var boardSize = Math.Sqrt(pieces.Count); - - if (boardSize % 1 != 0) - throw new JsonException($"Expected a square number of pieces, instead received {pieces.Count}"); - - ChessPiece?[,] board = new ChessPiece?[(int)boardSize, (int)boardSize]; - - foreach (var piece in pieces) - { - if (piece is null) - continue; - - board[piece.Position.Row, piece.Position.Col] = piece; - } - - return board; - } - - public override void Write( - Utf8JsonWriter writer, - ChessPiece?[,] value, - JsonSerializerOptions options) - { - writer.WriteStartArray(); - - foreach (var piece in value) - { - if (piece is null) - { - writer.WriteNullValue(); - continue; - } - - JsonSerializer.Serialize(writer, piece); - } - - writer.WriteEndArray(); - } -} - diff --git a/JoshHeaps.Net/Utilities/StockfishHelpers.cs b/JoshHeaps.Net/Utilities/StockfishHelpers.cs deleted file mode 100644 index ac1bb8f..0000000 --- a/JoshHeaps.Net/Utilities/StockfishHelpers.cs +++ /dev/null @@ -1,130 +0,0 @@ -using JoshHeaps.Net.Models; -using System.Text; - -namespace JoshHeaps.Net.Utilities; - -public static class StockfishHelpers -{ - public static MoveDto ToMoveDto( - this string uci, - GameState gameState, - Guid playerId) - { - int fCol = uci[0] - 'a', fRow = 7 - (uci[1] - '1'); - int tCol = uci[2] - 'a', tRow = 7 - (uci[3] - '1'); - - var piece = gameState.Board[fRow, fCol] - ?? throw new Exception("No piece at source square"); - - PieceType? promo = uci.Length == 5 ? uci[4] switch - { - 'q' => PieceType.Queen, - 'r' => PieceType.Rook, - 'b' => PieceType.Bishop, - 'n' => PieceType.Knight, - _ => null - } : null; - - return new MoveDto - { - GameId = gameState.GameId, - PlayerId = playerId, - PieceId = piece.Id, - TargetRow = tRow, - TargetCol = tCol, - PromotionChoice = promo, - SourceCol = fCol, - SourceRow = fRow, - }; - } - - /// - /// Convert a 2-D board array (rank 8 = row 0, file a = col 0) to a FEN string. - /// Only piece placement + active colour + castling are computed; the rest use - /// safe defaults (-, 0, 1). That is all Stockfish needs. - /// - public static string ToFen(this GameState gs) - { - var sb = new StringBuilder(64); - - /* 1) piece placement */ - for (int row = 0; row < 8; row++) - { - int empty = 0; - - for (int col = 0; col < 8; col++) - { - var p = gs.Board[row, col]; - - if (p is null) - { - empty++; - } - else - { - if (empty > 0) { sb.Append(empty); empty = 0; } - sb.Append(ToFenChar(p)); // ← unchanged helper - } - } - - if (empty > 0) sb.Append(empty); - if (row < 7) sb.Append('/'); - } - - /* 2) active colour */ - sb.Append(gs.CurrentPlayer == PieceColor.White ? " w " : " b "); - - /* 3) castling rights (from GameState flags) */ - sb.Append(GetCastlingFlags(gs)); - - /* 4) en-passant target square */ - sb.Append(' '); - sb.Append(gs.EnPassantTarget.HasValue - ? Alg(gs.EnPassantTarget.Value) - : "-"); - - /* 5-6) half-move clock + full-move number */ - int fullMoves = gs.MoveHistory.Count / 2 + 1; - sb.Append(" 0 ").Append(fullMoves); - - return sb.ToString(); - } - - /* ---------- helpers ---------- */ - - private static string GetCastlingFlags(GameState gs) - { - var flags = new StringBuilder(4); - - if (gs.WhiteCanCastleKingside) flags.Append('K'); - if (gs.WhiteCanCastleQueenside) flags.Append('Q'); - if (gs.BlackCanCastleKingside) flags.Append('k'); - if (gs.BlackCanCastleQueenside) flags.Append('q'); - - return flags.Length == 0 ? "-" : flags.ToString(); - } - - private static string Alg(Position p) - { - char file = (char)('a' + p.Col); - int rank = 8 - p.Row; - return $"{file}{rank}"; - } - - private static char ToFenChar(ChessPiece p) => p switch - { - { Type: PieceType.Pawn, Color: PieceColor.White } => 'P', - { Type: PieceType.Pawn, Color: PieceColor.Black } => 'p', - { Type: PieceType.Knight, Color: PieceColor.White } => 'N', - { Type: PieceType.Knight, Color: PieceColor.Black } => 'n', - { Type: PieceType.Bishop, Color: PieceColor.White } => 'B', - { Type: PieceType.Bishop, Color: PieceColor.Black } => 'b', - { Type: PieceType.Rook, Color: PieceColor.White } => 'R', - { Type: PieceType.Rook, Color: PieceColor.Black } => 'r', - { Type: PieceType.Queen, Color: PieceColor.White } => 'Q', - { Type: PieceType.Queen, Color: PieceColor.Black } => 'q', - { Type: PieceType.King, Color: PieceColor.White } => 'K', - { Type: PieceType.King, Color: PieceColor.Black } => 'k', - _ => throw new ArgumentOutOfRangeException(nameof(p)) - }; -} diff --git a/JoshHeaps.Net/appsettings.json b/JoshHeaps.Net/appsettings.json index d87f3fc..10f68b8 100644 --- a/JoshHeaps.Net/appsettings.json +++ b/JoshHeaps.Net/appsettings.json @@ -5,8 +5,5 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*", - "ConnectionStrings": { - "ChessDatabase": "Data Source=Databases/chess.db" - } + "AllowedHosts": "*" } diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 7fc6a17..2150e37 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -7,14 +7,4 @@ enable - - - - - - - - - -