diff --git a/src/Aiursoft.ChessServer/Aiursoft.ChessServer.csproj b/src/Aiursoft.ChessServer/Aiursoft.ChessServer.csproj
new file mode 100644
index 0000000..9902a79
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Aiursoft.ChessServer.csproj
@@ -0,0 +1,17 @@
+
+
+ Exe
+ net8.0
+ Aiursoft.ChessServer
+ Aiursoft.ChessServer
+ false
+ false
+ enable
+ enable
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Aiursoft.ChessServer/Aiursoft.ChessServer.sln b/src/Aiursoft.ChessServer/Aiursoft.ChessServer.sln
new file mode 100644
index 0000000..ddd1537
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Aiursoft.ChessServer.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aiursoft.ChessServer", "Aiursoft.ChessServer.csproj", "{D98D0049-4854-441F-80EB-CE5CB6A6F82E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D98D0049-4854-441F-80EB-CE5CB6A6F82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D98D0049-4854-441F-80EB-CE5CB6A6F82E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D98D0049-4854-441F-80EB-CE5CB6A6F82E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D98D0049-4854-441F-80EB-CE5CB6A6F82E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {69CD7AC4-0BEF-4640-A27A-3F0B15E89D04}
+ EndGlobalSection
+EndGlobal
diff --git a/src/Aiursoft.ChessServer/Controllers/GamesController.cs b/src/Aiursoft.ChessServer/Controllers/GamesController.cs
new file mode 100644
index 0000000..319acfe
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Controllers/GamesController.cs
@@ -0,0 +1,85 @@
+using Aiursoft.ChessServer.Data;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Aiursoft.ChessServer.Controllers;
+
+public class GamesController : ControllerBase
+{
+ private readonly InMemoryDatabase _database;
+
+ public GamesController(InMemoryDatabase database)
+ {
+ _database = database;
+ }
+
+ [Route("games")]
+ public IActionResult GetAll()
+ {
+ var games = _database.GetActiveGames();
+ return Ok(games);
+ }
+
+ [Route("games/{id}")]
+ public IActionResult GetInfo([FromRoute] int id)
+ {
+ var game = _database.GetOrAdd(id);
+ return Ok(new
+ {
+ Turn = game.Turn.AsChar,
+ Ended = game.IsEndGame,
+ End = game.EndGame?.EndgameType,
+ Won = game.EndGame?.WonSide,
+ game.MoveIndex,
+ game.WhiteKingChecked,
+ game.BlackKingChecked,
+ links = new Dictionary
+ {
+ { "ascii", $"game/{id}/ascii"},
+ { "fen", $"game/{id}/fen"},
+ { "pgn", $"game/{id}/pgn"},
+ { "move", $"game/{id}/move"}
+ }
+ });
+ }
+
+ [Route("games/{id}/ascii")]
+ public IActionResult GetAscii([FromRoute] int id)
+ {
+ var game = _database.GetOrAdd(id);
+ return Ok(game.ToAscii());
+ }
+
+ [Route("games/{id}/fen")]
+ public IActionResult GetFen([FromRoute] int id)
+ {
+ var game = _database.GetOrAdd(id);
+ return Ok(game.ToFen());
+ }
+
+ [Route("games/{id}/pgn")]
+ public IActionResult GetPgn([FromRoute]int id)
+ {
+ var game = _database.GetOrAdd(id);
+ return Ok(game.ToPgn());
+ }
+
+ [HttpPost]
+ [Route("games/{id}/move")]
+ public IActionResult Move([FromRoute]int id, [FromQuery]string move)
+ {
+ var game = _database.GetOrAdd(id);
+ try
+ {
+ if (game.IsValidMove(move) && !game.IsEndGame)
+ {
+ game.Move(move);
+ return Ok();
+ }
+ return BadRequest();
+ }
+ catch
+ {
+ return BadRequest();
+ }
+ }
+}
diff --git a/src/Aiursoft.ChessServer/Controllers/HomeController.cs b/src/Aiursoft.ChessServer/Controllers/HomeController.cs
new file mode 100644
index 0000000..3cfc726
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Controllers/HomeController.cs
@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Aiursoft.ChessServer.Controllers;
+
+public class HomeController : ControllerBase
+{
+ public IActionResult Index()
+ {
+ return this.Ok("Welcome to chess server! Please go to '/games/12345' to view more.");
+ }
+}
diff --git a/src/Aiursoft.ChessServer/Data/InMemoryDatabase.cs b/src/Aiursoft.ChessServer/Data/InMemoryDatabase.cs
new file mode 100644
index 0000000..f127f22
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Data/InMemoryDatabase.cs
@@ -0,0 +1,34 @@
+using Aiursoft.Scanner.Abstractions;
+using Chess;
+using System.Collections.Concurrent;
+
+namespace Aiursoft.ChessServer.Data;
+
+public class Game
+{
+ public bool Ended { get; set; }
+ public int Key { get; set; }
+}
+
+public class InMemoryDatabase : ISingletonDependency
+{
+ public ConcurrentDictionary Boards { get; private set; } = new ConcurrentDictionary();
+
+ public Game[] GetActiveGames()
+ {
+ return Boards.Select(t => new Game
+ {
+ Ended = t.Value.IsEndGame,
+ Key = t.Key
+ }).ToArray();
+ }
+
+ public ChessBoard GetOrAdd(int id)
+ {
+ if (!Boards.ContainsKey(id))
+ {
+ Boards.TryAdd(id, new ChessBoard());
+ }
+ return Boards[id];
+ }
+}
diff --git a/src/Aiursoft.ChessServer/Program.cs b/src/Aiursoft.ChessServer/Program.cs
new file mode 100644
index 0000000..02477b4
--- /dev/null
+++ b/src/Aiursoft.ChessServer/Program.cs
@@ -0,0 +1,32 @@
+using System.Reflection;
+using Aiursoft.Scanner;
+using Aiursoft.WebTools;
+using Aiursoft.WebTools.Models;
+
+namespace Aiursoft.ChessServer;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var app = Aiursoft.WebTools.Extends.App(args);
+ await app.RunAsync();
+ }
+}
+
+public class Startup : IWebStartup
+{
+ public void ConfigureServices(IConfiguration configuration, IWebHostEnvironment environment, IServiceCollection services)
+ {
+ services.AddLibraryDependencies();
+ services
+ .AddControllers()
+ .AddApplicationPart(Assembly.GetExecutingAssembly());
+ }
+
+ public void Configure(WebApplication app)
+ {
+ app.UseRouting();
+ app.MapDefaultControllerRoute();
+ }
+}
diff --git a/src/Aiursoft.ChessServer/appsettings.json b/src/Aiursoft.ChessServer/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/src/Aiursoft.ChessServer/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}