diff --git a/.gitignore b/.gitignore index 8a30d258e..5b34c2917 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,5 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +**/out/ +**/.idea/ diff --git a/Chess-Challenge.Uci/Chess-Challenge.Uci.csproj b/Chess-Challenge.Uci/Chess-Challenge.Uci.csproj new file mode 100644 index 000000000..d08f1b977 --- /dev/null +++ b/Chess-Challenge.Uci/Chess-Challenge.Uci.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + ChessChallenge.Bot + + + + + + + diff --git a/Chess-Challenge.Uci/Program.cs b/Chess-Challenge.Uci/Program.cs new file mode 100644 index 000000000..5d72b8a39 --- /dev/null +++ b/Chess-Challenge.Uci/Program.cs @@ -0,0 +1,12 @@ +// See https://aka.ms/new-console-template for more information + +using ChessChallenge.Bot; + +var engine = new UciEngine(); +var message = string.Empty; +var run = true; +while (run) +{ + message = Console.ReadLine(); + run = string.IsNullOrEmpty(message) || engine.ReceiveCommand(message); +} \ No newline at end of file diff --git a/Chess-Challenge.Uci/UciEngine.cs b/Chess-Challenge.Uci/UciEngine.cs new file mode 100644 index 000000000..c42560f89 --- /dev/null +++ b/Chess-Challenge.Uci/UciEngine.cs @@ -0,0 +1,117 @@ +using ChessChallenge.API; +using ChessChallenge.Chess; +using Board = ChessChallenge.Chess.Board; +using Move = ChessChallenge.Chess.Move; +using Timer = ChessChallenge.API.Timer; + +namespace ChessChallenge.Bot; + +public class UciEngine +{ + private const string UciInit = "uci"; + private const string UciOkay = "uciok"; + private const string IsReady = "isready"; + private const string ReadyOk = "readyok"; + private const string NewGame = "ucinewgame"; + private const string Position = "position"; + private const string BestMove = "bestmove"; + private const string Go = "go"; + private const string Stop = "stop"; + private const string Quit = "quit"; + + private const string BotMatchStartFens = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + + private readonly MyBot _bot = new MyBot(); + private Board _currentBoard; + + private const string LogFile = "./comm-log.txt"; + private const string UciLog = "./uci-log.txt"; + private const string ErrorFile = "./error.log"; + private const string DateFormat = "yyyyMMddTHHmmss"; + + private void WriteLineToDisk(string line, string file) + { + using StreamWriter outputFile = new StreamWriter(file, true); + + outputFile.WriteLine(line); + + } + public bool ReceiveCommand(string message) + { + var messageType = message.Split(' ')[0]; + WriteLineToDisk($"{DateTimeOffset.Now.ToString(DateFormat)} -- Received message {message}", LogFile); + WriteLineToDisk($"{DateTimeOffset.Now.ToString(DateFormat)}{message}", UciLog); + try + { + switch (messageType) + { + case UciInit: + Respond(UciOkay); + return true; + case IsReady: + Respond(ReadyOk); + return true; + case NewGame: + _currentBoard = new Board(); + _currentBoard.LoadPosition(BotMatchStartFens); + return true; + case Position: + ProcessPositionCommand(message); + return true; + case Go: + ProcessGoCommand(message); + return true; + case Stop: + return true; + case Quit: + default: + return false; + } + } + catch (Exception ex) + { + if (ex.StackTrace != null) + { + var errorMessage = $"{DateTimeOffset.Now.ToString(DateFormat)} -- {ex.Message}\n{ex.StackTrace}"; + WriteLineToDisk(errorMessage, ErrorFile); + + } + } + + return false; + + } + + private void ProcessStopCommand() + { + throw new NotImplementedException(); + } + + private void ProcessGoCommand(string message) + { + var split = message.Split(' '); + var millis = int.Parse(split[2]); + var newMove = new Move(_bot.Think(new(_currentBoard), new (millis)).RawValue); + var moveNameUci = MoveUtility.GetMoveNameUCI(newMove); + Respond($"{BestMove} {moveNameUci}"); + } + + private void ProcessPositionCommand(string message) + { + _currentBoard = new Board(); + _currentBoard.LoadPosition(BotMatchStartFens); + var moveStrings = message.Split(' '); + if (moveStrings[^1] == "startpos") return; + for (var i = 3; i < moveStrings.Length; i++) + { + var newMove = MoveUtility.GetMoveFromUCIName(moveStrings[i], _currentBoard); + _currentBoard.MakeMove(newMove, false); + } + } + + private void Respond(string response) + { + WriteLineToDisk($"Responding: {response}", LogFile); + Console.WriteLine(response); + } +} \ No newline at end of file diff --git a/Chess-Challenge.sln b/Chess-Challenge.sln index 396059030..22e49f22c 100644 --- a/Chess-Challenge.sln +++ b/Chess-Challenge.sln @@ -10,6 +10,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chess-Challenge.Uci", "Chess-Challenge.Uci\Chess-Challenge.Uci.csproj", "{75253019-D4F1-4560-859C-F7C2AC61052A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,6 +23,10 @@ Global {2803E64F-15AC-430B-A5A2-69C00EA7505F}.Debug|Any CPU.Build.0 = Debug|Any CPU {2803E64F-15AC-430B-A5A2-69C00EA7505F}.Release|Any CPU.ActiveCfg = Release|Any CPU {2803E64F-15AC-430B-A5A2-69C00EA7505F}.Release|Any CPU.Build.0 = Release|Any CPU + {75253019-D4F1-4560-859C-F7C2AC61052A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75253019-D4F1-4560-859C-F7C2AC61052A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75253019-D4F1-4560-859C-F7C2AC61052A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75253019-D4F1-4560-859C-F7C2AC61052A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE