From ccdcad80c30553b667c35790d6761ad8933cf7a2 Mon Sep 17 00:00:00 2001 From: Chris Pilcher Date: Mon, 31 Jul 2023 09:05:32 -0400 Subject: [PATCH 1/2] basic implementation --- .gitignore | 2 + .../Chess-Challenge.Uci.csproj | 15 ++ Chess-Challenge.Uci/Program.cs | 11 ++ Chess-Challenge.Uci/UciEngine.cs | 144 ++++++++++++++++++ Chess-Challenge.sln | 7 + 5 files changed, 179 insertions(+) create mode 100644 Chess-Challenge.Uci/Chess-Challenge.Uci.csproj create mode 100644 Chess-Challenge.Uci/Program.cs create mode 100644 Chess-Challenge.Uci/UciEngine.cs 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..2bab65638 --- /dev/null +++ b/Chess-Challenge.Uci/Program.cs @@ -0,0 +1,11 @@ +// See https://aka.ms/new-console-template for more information + +using ChessChallenge.Bot; + +var engine = new UciEngine(); +var message = string.Empty; +while (message != "quit") +{ + message = Console.ReadLine(); + if (!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..ba5ed8049 --- /dev/null +++ b/Chess-Challenge.Uci/UciEngine.cs @@ -0,0 +1,144 @@ +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 MyBot _bot = new MyBot(); + private Board _currentBoard; + + private string _logFile = "./comm-log.txt"; + private string _uciLog = "uci-log.txt"; + private string _errorFile = "error.log"; + private const string _dateFormat = "yyyyMMddTHHmmss"; + + + private char GetPromotionCharacter(PieceType piece) => + piece switch + { + PieceType.None => 'q', + PieceType.Pawn => 'q', + PieceType.Knight => 'n', + PieceType.Bishop => 'b', + PieceType.Rook => 'r', + PieceType.Queen => 'q', + PieceType.King => 'q', + _ => throw new ArgumentOutOfRangeException(nameof(piece), piece, null) + }; + + private void WriteLineToDisk(string line, string file) + { + using StreamWriter outputFile = new StreamWriter(file, true); + + outputFile.WriteLine(line); + + } + public void 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); + break; + case IsReady: + Respond(ReadyOk); + break; + case NewGame: + _uciLog = $"./{_uciLog}"; + _currentBoard = new Board(); + _currentBoard.LoadPosition(botMatchStartFens); + break; + case Position: + ProcessPositionCommand(message); + break; + case Go: + ProcessGoCommand(message); + break; + case Stop: + // message = Quit; + // ProcessStopCommand(); + break; + case Quit: + break; + default: + message = Quit; + break; + } + } + catch (Exception ex) + { + if (ex.StackTrace != null) + { + var errorMessage = $"{DateTimeOffset.Now.ToString(_dateFormat)} -- {ex.Message}\n{ex.StackTrace}"; + WriteLineToDisk(errorMessage, _errorFile); + + } + } + + } + + 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) + { + // if (message.Split(' ').Length < 3) return; + // var moveStrings = message.Split(' ').Skip(3).ToArray(); + // var moves = new Move[moveStrings.Length]; + // for (var i = 0; i < moveStrings.Length; i++) + // { + // var str = moveStrings[i]; + // moves[i] = MoveUtility.GetMoveFromUCIName(str, _currentBoard); + // _currentBoard.MakeMove(moves[i], false); + // } + _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 From e3f11fd134d81fab0ffbe53cbe42949c5f6b8c71 Mon Sep 17 00:00:00 2001 From: Chris Pilcher Date: Mon, 31 Jul 2023 11:51:31 -0400 Subject: [PATCH 2/2] First cleanup pass --- Chess-Challenge.Uci/Program.cs | 5 +- Chess-Challenge.Uci/UciEngine.cs | 79 +++++++++++--------------------- 2 files changed, 29 insertions(+), 55 deletions(-) diff --git a/Chess-Challenge.Uci/Program.cs b/Chess-Challenge.Uci/Program.cs index 2bab65638..5d72b8a39 100644 --- a/Chess-Challenge.Uci/Program.cs +++ b/Chess-Challenge.Uci/Program.cs @@ -4,8 +4,9 @@ var engine = new UciEngine(); var message = string.Empty; -while (message != "quit") +var run = true; +while (run) { message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message)) engine.ReceiveCommand(message); + 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 index ba5ed8049..c42560f89 100644 --- a/Chess-Challenge.Uci/UciEngine.cs +++ b/Chess-Challenge.Uci/UciEngine.cs @@ -19,30 +19,15 @@ public class UciEngine 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 MyBot _bot = new MyBot(); + 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 string _logFile = "./comm-log.txt"; - private string _uciLog = "uci-log.txt"; - private string _errorFile = "error.log"; - private const string _dateFormat = "yyyyMMddTHHmmss"; - - - private char GetPromotionCharacter(PieceType piece) => - piece switch - { - PieceType.None => 'q', - PieceType.Pawn => 'q', - PieceType.Knight => 'n', - PieceType.Bishop => 'b', - PieceType.Rook => 'r', - PieceType.Queen => 'q', - PieceType.King => 'q', - _ => throw new ArgumentOutOfRangeException(nameof(piece), piece, null) - }; + 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) { @@ -51,53 +36,50 @@ private void WriteLineToDisk(string line, string file) outputFile.WriteLine(line); } - public void ReceiveCommand(string message) + 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); + WriteLineToDisk($"{DateTimeOffset.Now.ToString(DateFormat)} -- Received message {message}", LogFile); + WriteLineToDisk($"{DateTimeOffset.Now.ToString(DateFormat)}{message}", UciLog); try { switch (messageType) { case UciInit: Respond(UciOkay); - break; + return true; case IsReady: Respond(ReadyOk); - break; + return true; case NewGame: - _uciLog = $"./{_uciLog}"; _currentBoard = new Board(); - _currentBoard.LoadPosition(botMatchStartFens); - break; + _currentBoard.LoadPosition(BotMatchStartFens); + return true; case Position: ProcessPositionCommand(message); - break; + return true; case Go: ProcessGoCommand(message); - break; + return true; case Stop: - // message = Quit; - // ProcessStopCommand(); - break; + return true; case Quit: - break; default: - message = Quit; - break; + return false; } } catch (Exception ex) { if (ex.StackTrace != null) { - var errorMessage = $"{DateTimeOffset.Now.ToString(_dateFormat)} -- {ex.Message}\n{ex.StackTrace}"; - WriteLineToDisk(errorMessage, _errorFile); + var errorMessage = $"{DateTimeOffset.Now.ToString(DateFormat)} -- {ex.Message}\n{ex.StackTrace}"; + WriteLineToDisk(errorMessage, ErrorFile); } } - + + return false; + } private void ProcessStopCommand() @@ -115,18 +97,9 @@ private void ProcessGoCommand(string message) } private void ProcessPositionCommand(string message) - { - // if (message.Split(' ').Length < 3) return; - // var moveStrings = message.Split(' ').Skip(3).ToArray(); - // var moves = new Move[moveStrings.Length]; - // for (var i = 0; i < moveStrings.Length; i++) - // { - // var str = moveStrings[i]; - // moves[i] = MoveUtility.GetMoveFromUCIName(str, _currentBoard); - // _currentBoard.MakeMove(moves[i], false); - // } + { _currentBoard = new Board(); - _currentBoard.LoadPosition(botMatchStartFens); + _currentBoard.LoadPosition(BotMatchStartFens); var moveStrings = message.Split(' '); if (moveStrings[^1] == "startpos") return; for (var i = 3; i < moveStrings.Length; i++) @@ -138,7 +111,7 @@ private void ProcessPositionCommand(string message) private void Respond(string response) { - WriteLineToDisk($"Responding: {response}", _logFile); + WriteLineToDisk($"Responding: {response}", LogFile); Console.WriteLine(response); } } \ No newline at end of file