Chess Explorer Hangs Communicating With .NET 4 Engine

Get your specific HIARCS/Junior support questions answered here as well as up-to-the-minute news!

Moderators: Watchman, Mark Uniacke, mrudolf

Post Reply
User avatar
emadsen
Member
Posts: 8
Joined: Fri Jul 31, 2015 3:28 am

Chess Explorer Hangs Communicating With .NET 4 Engine

Post by emadsen »

Hi,

I'm a paying customer of Hiarcs Chess Explorer. I'm also the author of a chess engine, MadChess.

Last night when I started Chess Explorer I noticed a prompt to download a version compatible with Windows 10. I'm still running Windows 8.1, however I downloaded the update because I hoped it would resolve an issue causing Chess Explorer to hang when communicating with my engine. It didn't.

I am working on a beta version of MadChess 2.0, written in C# for .NET 4. When I load the engine in Chess Explorer and click the start engine button, no analysis appears. The log file reveals all communication ceases after the uci / uciok handshake. The Shredder GUI runs the engine without any errors. If I recompile the code for .NET 3.5, Chess Explorer runs the engine without any errors.

I'm really struggling with this one. Since I don't have access to the Chess Explorer code, I'm uncertain what's wrong. I am hoping you can help.

I've provided source code for the class that reads from / writes to standard input / output. I can provide full source if requested. In addition, I've provided log files of scenarios that work and don't work.

Regards,
Erik

MadChess .NET 4.6 Chess Explorer

Code: Select all

Waiting to read console line
00:00:00.005   In   uci
00:00:00.005   Out  id name MadChess 2.0
00:00:00.008   Out  id author Erik Madsen
00:00:00.008   Out  option name Debug type check default false
00:00:00.008   Out  option name Hash type spin default 128 min 0 max 1024
00:00:00.008   Out  option name MultiPV type spin default 1 min 1 max 160
00:00:00.008   Out  option name UCI_AnalyseMode type check default false
00:00:00.008   Out  uciok
00:00:00.008   Out  
00:00:00.009   In   Waiting to read console line
00:00:00.009
Hangs. No more communication.

MadChess .NET 3.5 Chess Explorer

Code: Select all

Waiting to read console line
00:00:00.008   In   uci
00:00:00.009   Out  id name MadChess 2.0
00:00:00.016   Out  id author Erik Madsen
00:00:00.017   Out  option name Debug type check default false
00:00:00.017   Out  option name Hash type spin default 128 min 0 max 1024
00:00:00.017   Out  option name MultiPV type spin default 1 min 1 max 160
00:00:00.017   Out  option name UCI_AnalyseMode type check default false
00:00:00.017   Out  uciok
00:00:00.017   Out  
00:00:00.017   In   Waiting to read console line
00:00:00.017   In   setoption name Hash value 1024
00:00:00.090   Out  
00:00:03.230   In   Waiting to read console line
00:00:03.230   In   isready
00:00:03.230   Out  readyok
00:00:03.231   Out  
00:00:03.231   In   Waiting to read console line
00:00:03.231   In   ucinewgame
00:00:03.257   Out  
00:00:03.609   In   Waiting to read console line
00:00:03.609   In   isready
00:00:03.609   Out  readyok
00:00:03.609   Out  
00:00:03.609   In   Waiting to read console line
00:00:03.609   In   setoption name UCI_AnalyseMode value true
00:00:03.695   Out  
00:00:03.695   In   Waiting to read console line
00:00:03.695   In   position startpos
00:00:03.695   Out  
00:00:03.711   In   Waiting to read console line
00:00:03.711   In   go infinite
00:00:03.711   In   Waiting to read console line
00:00:03.712   Out  info multipv 1 depth 1 seldepth 1 score cp 61 time 157 nodes 54 nps 343 pv e2e4 
00:00:03.878   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:03.879   Out  info multipv 1 depth 2 seldepth 4 score upperbound 36 time 169 nodes 150 nps 887
00:00:03.887   Out  info multipv 1 depth 2 seldepth 4 score cp 0 time 170 nodes 270 nps 1587 pv e2e4 e7e5 
00:00:03.888   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:03.888   Out  info multipv 1 depth 3 seldepth 8 score lowerbound 25 time 177 nodes 469 nps 2647
00:00:03.895   Out  info multipv 1 depth 3 seldepth 8 score cp 54 time 178 nodes 699 nps 3934 pv e2e4 e7e5 b1c3 
00:00:03.895   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:03.895   Out  info multipv 1 depth 4 seldepth 9 score upperbound 29 time 179 nodes 1449 nps 8090
00:00:03.897   Out  info multipv 1 depth 4 seldepth 9 score cp 29 time 180 nodes 1698 nps 9458 pv e2e4 d7d5 b1c3 e7e6 
00:00:23.411 
MadChess 2.0 .NET 4.6 Shredder GUI

Code: Select all

Waiting to read console line
00:00:00.005   In   uci
00:00:00.005   Out  id name MadChess 2.0
00:00:00.010   Out  id author Erik Madsen
00:00:00.010   Out  option name Debug type check default false
00:00:00.010   Out  option name Hash type spin default 128 min 0 max 1024
00:00:00.010   Out  option name MultiPV type spin default 1 min 1 max 160
00:00:00.010   Out  option name UCI_AnalyseMode type check default false
00:00:00.010   Out  uciok
00:00:00.010   Out  
00:00:00.010   In   Waiting to read console line
00:00:00.010   In   setoption name UCI_AnalyseMode value false
00:00:00.011   Out  
00:00:00.011   In   Waiting to read console line
00:00:00.011   In   isready
00:00:00.012   Out  readyok
00:00:00.012   Out  
00:00:00.012   In   Waiting to read console line
00:00:00.012   In   ucinewgame
00:00:05.910   Out  
00:00:05.949   In   Waiting to read console line
00:00:05.949   In   isready
00:00:05.949   Out  readyok
00:00:05.949   Out  
00:00:05.949   In   Waiting to read console line
00:00:05.949   In   setoption name UCI_AnalyseMode value true
00:00:05.949   Out  
00:00:05.949   In   Waiting to read console line
00:00:05.949   In   position startpos
00:00:05.949   Out  
00:00:05.960   In   Waiting to read console line
00:00:05.960   In   go infinite
00:00:05.960   In   Waiting to read console line
00:00:05.960   Out  info multipv 1 depth 1 seldepth 1 score cp 61 time 81 nodes 60 nps 745 pv e2e4 
00:00:06.046   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:06.046   Out  info multipv 1 depth 2 seldepth 4 score upperbound 36 time 89 nodes 162 nps 1819
00:00:06.053   Out  info multipv 1 depth 2 seldepth 4 score cp 0 time 91 nodes 310 nps 3421 pv e2e4 e7e5 
00:00:06.054   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:06.054   Out  info multipv 1 depth 3 seldepth 8 score lowerbound 25 time 101 nodes 523 nps 5176
00:00:06.065   Out  info multipv 1 depth 3 seldepth 8 score cp 54 time 102 nodes 819 nps 8026 pv e2e4 e7e5 b1c3 
00:00:06.066   Out  info hashfull 0 currmove a2a4 currmovenumber 20
00:00:06.066   Out  info multipv 1 depth 4 seldepth 9 score upperbound 29 time 105 nodes 1608 nps 15372
00:00:06.068   Out  info multipv 1 depth 4 seldepth 9 score cp 0 time 111 nodes 3601 nps 32506 pv e2e4 e7e5 b1c3 b8c6
User avatar
emadsen
Member
Posts: 8
Joined: Fri Jul 31, 2015 3:28 am

UciStream.cs Source Code

Post by emadsen »

Standard input is read in MonitorInputStream().

Code: Select all

//  +------------------------------------------------------------------------------+
//  |                                                                              |
//  |     MadChess is developed by Erik Madsen.  Copyright 2014.                   |
//  |     MadChess is free software.  It is distributed under the GNU General      |
//  |     Public License Version 3 (GPLv3).  See License.txt for details.          |
//  |                                                                              |
//  +------------------------------------------------------------------------------+
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading;


namespace MadChess
{
    public sealed class UciStream : IDisposable
    {
        private const int _cacheSizeMegabytes = 128;
        private const string _startPositionFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
        public const long MovesInfoInterval = 250000;
        public const long MovesTimeInterval = 100;
        private readonly StringBuilder _stringBuilder;
        private readonly Board _board;        
        private readonly Cache _cache;
        private readonly KillerMoves _killerMoves;
        private readonly MoveHistory _moveHistory;
        private readonly Search _search;
        private readonly AutoResetEvent _asyncSignal;
        private readonly Queue<string> _asyncQueue;
        private readonly object _logLock;
        private readonly Stopwatch _mainStopwatch;
        private readonly Stopwatch _commandStopwatch;
        private Thread _asyncThread;
        private FileStream _logStream;
        private StreamWriter _logWriter;
        private bool _log;
        private bool _lastMessageIncludedTimestamp;
        private bool _disposed;
        public bool Debug;


        &#91;System.Diagnostics.CodeAnalysis.SuppressMessage&#40;"Microsoft.Security", "CA2122&#58;DoNotIndirectlyExposeMethodsWithLinkDemands"&#41;&#93;
        public bool Log
        &#123;
            private get &#123; return _log; &#125;
            set
            &#123;
                _log = value;
                if &#40;_log&#41;
                &#123;
                    lock &#40;_logLock&#41;
                    &#123;
                        if &#40;_logStream == null&#41;
                        &#123;
                            // Create or append to log file.
                            // Include process ID in log filename to avoid multiple engines interleaving lines in a single log file.
                            string strEngineLocation = Assembly.GetEntryAssembly&#40;&#41;.Location;
                            int intProcessId = Process.GetCurrentProcess&#40;&#41;.Id;
                            string strFile = Path.GetFileNameWithoutExtension&#40;strEngineLocation&#41; + "-" + intProcessId + ".log";
                            _logStream = File.Open&#40;strFile, FileMode.Append, FileAccess.Write, FileShare.Read&#41;;
                            _logWriter = new StreamWriter&#40;_logStream&#41;
                            &#123;
                                AutoFlush = true
                            &#125;;
                        &#125;
                    &#125;
                &#125;
            &#125;
        &#125;


        public UciStream&#40;&#41;
        &#123;
            _stringBuilder = new StringBuilder&#40;&#41;;
            _log = false;
            _lastMessageIncludedTimestamp = false;
            _disposed = false;
            // Create board, cache, killer moves, move history, and search.
            _board = new Board&#40;&#41;;
            _cache = new Cache &#123;Capacity = _cacheSizeMegabytes * Cache.PositionsPerMegabyte&#125;;
            _killerMoves = new KillerMoves&#40;_board, Search.MaxHorizon&#41;;
            _moveHistory = new MoveHistory&#40;_board&#41;;
            _search = new Search&#40;this, _board, _cache, _killerMoves, _moveHistory&#41; &#123;MultiPv = 1&#125;;
            // Create synchronization and diagnostic objects.
            _asyncSignal = new AutoResetEvent&#40;false&#41;;
            _asyncQueue = new Queue<string>&#40;&#41;;
            _logLock = new object&#40;&#41;; 
            _mainStopwatch = new Stopwatch&#40;&#41;;
            _commandStopwatch = new Stopwatch&#40;&#41;;
            _mainStopwatch.Start&#40;&#41;;
        &#125;


        ~UciStream&#40;&#41;
        &#123;
            Dispose&#40;false&#41;;
        &#125;


        public void Dispose&#40;&#41;
        &#123;
            Dispose&#40;true&#41;;
            GC.SuppressFinalize&#40;this&#41;;
        &#125;


        private void Dispose&#40;bool Disposing&#41;
        &#123;
            if &#40;_disposed&#41; return;
            if &#40;Disposing&#41;
            &#123;
                // Release managed resources.
                _search.Dispose&#40;&#41;;
            &#125;
            // Release unmanaged resources.
            if &#40;_logStream != null&#41;
            &#123;
                _logStream.Close&#40;&#41;;
                _logStream.Dispose&#40;&#41;;
            &#125;
            if &#40;_asyncSignal != null&#41;
            &#123;
                _asyncSignal.Close&#40;&#41;;
            &#125;
            _disposed = true;
        &#125;


        public void Run&#40;&#41;
        &#123;
            Thread.CurrentThread.Name = "UCI Synchronous";
            // Create async thread.
            _asyncThread = new Thread&#40;MonitorQueue&#41; &#123; Name = "UCI Asynchronous", IsBackground = true &#125;;
            _asyncThread.Start&#40;&#41;;
            // Monitor input stream.
            MonitorInputStream&#40;&#41;;
        &#125;
        
        
        private void MonitorInputStream&#40;&#41;
        &#123;
            try
            &#123;
                do
                &#123;
                    // Read command.
                    if &#40;Log&#41;
                    &#123;
                        LogMessage&#40;CommandDirection.In, "Waiting to read console line", true&#41;;
                    &#125;
                    string command = Console.ReadLine&#40;&#41;;
                    if &#40;Log&#41;
                    &#123;
                        // Log command.
                        LogMessage&#40;CommandDirection.In, command, true&#41;;
                    &#125;
                    // Dispatch command.
                    DispatchCommand&#40;command&#41;;
                &#125; while &#40;true&#41;;
            &#125;
            catch &#40;Exception exception&#41;
            &#123;
                HandleException&#40;exception&#41;;
            &#125;
        &#125;


        private void DispatchCommand&#40;string Command&#41;
        &#123;
            if &#40;Command == null&#41; return;
            // ParseLongAlgebraic command into tokens.
            string&#91;&#93; tokens = Tokens.Parse&#40;Command, ' ', '"'&#41;;
            // Do not convert to lowercase because this invalidates FEN strings &#40;where case differentiates white and black pieces&#41;.
            if &#40;tokens.Length == 0&#41;
            &#123;
                // No tokens found.
                return;
            &#125;
            // Determine whether to dispatch command on main thread or async thread.
            switch &#40;tokens&#91;0&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41;&#41;
            &#123;
                case "go"&#58;
                    DispatchOnAsyncThread&#40;tokens&#41;;
                    break;
                default&#58;
                    DispatchOnMainThread&#40;tokens&#41;;
                    break;
            &#125;
        &#125;


        private void DispatchOnMainThread&#40;string&#91;&#93; Tokens&#41;
        &#123;
            try
            &#123;
                switch &#40;Tokens&#91;0&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41;&#41;
                &#123;
                    case "debug"&#58;
                        SetDebug&#40;Tokens&#41;;
                        break;
                    case "uci"&#58;
                        Uci&#40;&#41;;
                        break;                    
                    case "isready"&#58;
                        IsReady&#40;&#41;;
                        break;
                    case "setoption"&#58;
                        SetOption&#40;Tokens&#41;;
                        break;
                    case "ucinewgame"&#58;
                        UciNewGame&#40;&#41;;
                        break;
                    case "position"&#58;
                        Position&#40;Tokens&#41;;
                        break;
                    case "showboard"&#58;
                        ShowBoard&#40;&#41;;
                        break;
                    case "countmoves"&#58;
                        CountMoves&#40;Tokens&#41;;
                        break;
                    case "dividemoves"&#58;
                        DivideMoves&#40;Tokens&#41;;
                        break;
                    case "listmoves"&#58;
                        ListMoves&#40;&#41;;
                        break;
                    case "exchangescore"&#58;
                        ExchangeScore&#40;Tokens&#41;;
                        break;
                    case "test"&#58;
                        Test&#40;&#41;;
                        break;
                    case "analyzepositions"&#58;
                        AnalyzePositions&#40;Tokens&#41;;
                        break;
                    case "stop"&#58;
                        Stop&#40;&#41;;
                        break;
                    case "quit"&#58;
                        Quit&#40;0&#41;;
                        break;
                    default&#58;
                        WriteMessageLine&#40;Tokens&#91;0&#93; + " command not supported."&#41;;
                        break;
                &#125;
                WriteMessageLine&#40;null&#41;;
            &#125;
            catch &#40;Exception exception&#41;
            &#123;
                HandleException&#40;exception&#41;;
            &#125;
        &#125;


        private void DispatchOnAsyncThread&#40;string&#91;&#93; Tokens&#41;
        &#123;
            lock &#40;_asyncQueue&#41;
            &#123;
                // Queue command.
                _asyncQueue.Enqueue&#40;Tokens&#41;;
                // Signal async queue.
                _asyncSignal.Set&#40;&#41;;
            &#125;
        &#125;


        private void MonitorQueue&#40;&#41;
        &#123;
            try
            &#123;
                do
                &#123;
                    // Wait for signal.
                    _asyncSignal.WaitOne&#40;&#41;;
                    string&#91;&#93; tokens = null;
                    lock &#40;_asyncQueue&#41;
                    &#123;
                        if &#40;_asyncQueue.Count > 0&#41;
                        &#123;
                            tokens = _asyncQueue.Dequeue&#40;&#41;;
                        &#125;
                    &#125;
                    if &#40;&#40;tokens != null&#41; && &#40;tokens.Length > 0&#41;&#41;
                    &#123;
                        // Process command.
                        switch &#40;tokens&#91;0&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41;&#41;
                        &#123;
                            case "go"&#58;
                                Go&#40;tokens&#41;;
                                break;
                            default&#58;
                                throw new InvalidOperationException&#40;"Cannot process " + tokens&#91;0&#93; + " command on asynchronous thread."&#41;;
                        &#125;
                        WriteMessageLine&#40;null&#41;;
                    &#125;
                &#125; while &#40;true&#41;;
            &#125;
            catch &#40;Exception exception&#41;
            &#123;
                HandleException&#40;exception&#41;;
            &#125;
        &#125;


        private void SetDebug&#40;string&#91;&#93; Tokens&#41;
        &#123;
            string debug = Tokens&#91;1&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41;;
            Debug = &#40;debug == "on"&#41; || &#40;debug == "true"&#41;;
        &#125;


        private void Uci&#40;&#41;
        &#123;
            // Engine name and author
            WriteMessageLine&#40;"id name MadChess 2.0"&#41;;
            WriteMessageLine&#40;"id author Erik Madsen"&#41;;
            // Engine options
            WriteMessageLine&#40;"option name Debug type check default false"&#41;;
            WriteMessageLine&#40;"option name Hash type spin default 128 min 0 max 1024"&#41;;
            WriteMessageLine&#40;"option name MultiPV type spin default 1 min 1 max " + MadChess.Position.MaxMoves&#41;;
            WriteMessageLine&#40;"option name UCI_AnalyseMode type check default false"&#41;;
            WriteMessageLine&#40;"uciok"&#41;;
        &#125;


        private void IsReady&#40;&#41;
        &#123;
            WriteMessageLine&#40;"readyok"&#41;;
        &#125;


        private void SetOption&#40;string&#91;&#93; Tokens&#41;
        &#123;
            string optionName = Tokens&#91;2&#93;;
            string optionValue = Tokens&#91;4&#93;;
            switch &#40;optionName.ToLower&#40;CultureInfo.InvariantCulture&#41;&#41;
            &#123;
                case "debug"&#58;
                    SetDebug&#40;Tokens&#41;;
                    break;
                case "hash"&#58;
                    int cacheMegabytes = int.Parse&#40;optionValue&#41;;
                    _cache.Capacity = cacheMegabytes * Cache.PositionsPerMegabyte;
                    break;
                case "multipv"&#58;
                    _search.MultiPv = int.Parse&#40;optionValue&#41;;
                    break;
                case "uci_analysemode"&#58;
                    // TODO&#58; Implement analysis mode.
                    break;
                default&#58;
                    WriteMessageLine&#40;Tokens&#91;0&#93; + " option not supported."&#41;;
                    break;
            &#125;
        &#125;


        private void UciNewGame&#40;&#41;
        &#123;
            // Reset cache.
            _cache.Reset&#40;&#41;;
            // Reset killer moves and move history.
            _killerMoves.Reset&#40;&#41;;
            _moveHistory.Reset&#40;&#41;;
        &#125;


        private void Position&#40;string&#91;&#93; Tokens&#41;
        &#123;
            // ParseLongAlgebraic FEN.
            string fen = Tokens&#91;1&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41; == "startpos"
                ? _startPositionFen
                &#58; string.Join&#40;" ", Tokens, 2, Tokens.Length - 2&#41;;
            // Determine if position specifies moves.
            int moveIndex = Tokens.Length;
            for &#40;int index = 2; index < Tokens.Length; index++&#41;
            &#123;
                if &#40;Tokens&#91;index&#93;.ToLower&#40;CultureInfo.InvariantCulture&#41; == "moves"&#41;
                &#123;
                    // Position specifies moves.
                    if &#40;index == &#40;Tokens.Length - 1&#41;&#41;
                    &#123;
                        // Remove empty move list from FEN.
                        fen = fen.Substring&#40;0, fen.Length - 6&#41;;
                    &#125;
                    moveIndex = index + 1;
                    break;
                &#125;
            &#125;
            // Setup position.
            _board.SetupPosition&#40;fen&#41;;
            while &#40;moveIndex <Tokens>= _board.MovesInfoUpdate&#41;
            &#123;
                // Update move count.
                double movesPerSecond = _board.Moves / _commandStopwatch.Elapsed.TotalSeconds;
                WriteMessageLine&#40;"Counted " + _board.MovesInfoUpdate.ToString&#40;"n0"&#41; + " moves &#40;" + movesPerSecond.ToString&#40;"n0"&#41; + " moves per second&#41;."&#41;;
                long intervals = _board.Moves / MovesInfoInterval;
                _board.MovesInfoUpdate = MovesInfoInterval * &#40;intervals + 1&#41;;
            &#125;
            if &#40;&#40;Horizon - Depth&#41; == 0&#41;
            &#123;
                return 1L;
            &#125;
            // Generate moves.
            _board.GenerateMoves&#40;false&#41;;
            long moves = 0L;
            for &#40;int moveIndex = 0; moveIndex < _board.CurrentPosition.MoveIndex; moveIndex++&#41;
            &#123;
                ulong move = _board.CurrentPosition.Moves&#91;moveIndex&#93;;
                // Determine if move is legal.     
                if &#40;!_board.IsMoveLegal&#40;ref move&#41;&#41;
                &#123;
                    // Skip illegal move.
                    continue;
                &#125;
                // Play move.
                _board.PlayMove&#40;move&#41;;
                // Recurse.
                moves += CountMoves&#40;Depth + 1, Horizon&#41;;
                // Undo move.
                _board.UndoMove&#40;&#41;;
            &#125;
            return moves;
        &#125;


        private void DivideMoves&#40;string&#91;&#93; Tokens&#41;
        &#123;
            int horizon = int.Parse&#40;Tokens&#91;1&#93;.Trim&#40;&#41;&#41;;
            _board.Moves = 0L;
            _board.MovesInfoUpdate = MovesInfoInterval;
            _commandStopwatch.Reset&#40;&#41;;
            _commandStopwatch.Start&#40;&#41;;
            // Generate moves.
            _board.GenerateMoves&#40;false&#41;;
            // Count moves for each root move.
            long&#91;&#93; rootMoves = new long&#91;_board.RootPosition.MoveIndex&#93;;
            for &#40;int moveIndex = 0; moveIndex < _board.RootPosition.MoveIndex; moveIndex++&#41;
            &#123;
                ulong move = _board.RootPosition.Moves&#91;moveIndex&#93;;
                if &#40;!_board.IsMoveLegal&#40;ref move&#41;&#41;
                &#123;
                    // Skip illegal move.
                    continue;
                &#125;
                // Play move.
                _board.PlayMove&#40;move&#41;;
                // Count moves.
                rootMoves&#91;moveIndex&#93; = CountMoves&#40;0, horizon - 1&#41;;
                // Undo move.
                _board.UndoMove&#40;&#41;;
            &#125;
            _commandStopwatch.Stop&#40;&#41;;
            // Display move count for each root move.
            int legalMoves = 0;
            WriteMessageLine&#40;"Root Move    Moves"&#41;;
            WriteMessageLine&#40;"=========  ======="&#41;;
            for &#40;int moveIndex = 0; moveIndex < _board.RootPosition.MoveIndex; moveIndex++&#41;
            &#123;
                ulong move = _board.RootPosition.Moves&#91;moveIndex&#93;;
                if &#40;!_board.IsMoveLegal&#40;ref move&#41;&#41;
                &#123;
                    // Skip illegal move.
                    continue;
                &#125;
                legalMoves++;
                WriteMessageLine&#40;Move.ToLongAlgebraic&#40;_board, move&#41;.PadRight&#40;9&#41; + "  " + rootMoves&#91;moveIndex&#93;.ToString&#40;CultureInfo.InvariantCulture&#41;.PadLeft&#40;7&#41;&#41;;
            &#125;
            WriteMessageLine&#40;&#41;;
            WriteMessageLine&#40;legalMoves + " legal root moves"&#41;;
        &#125;


        private void ListMoves&#40;&#41;
        &#123;
            _commandStopwatch.Reset&#40;&#41;;
            _commandStopwatch.Start&#40;&#41;;
            // Get cached position.
            CachedPosition cachedPosition = _cache.GetPosition&#40;_board.RootPosition.Key, false&#41;;
            ulong bestMove = 0ul;
            if &#40;&#40;cachedPosition != null&#41; && &#40;cachedPosition.BestMoveFrom != Square.Illegal&#41;&#41;
            &#123;
                // Cached position specifies a best move.
                Move.SetFrom&#40;ref bestMove, cachedPosition.BestMoveFrom&#41;;
                Move.SetTo&#40;ref bestMove, cachedPosition.BestMoveTo&#41;;
                Move.SetPromotedPiece&#40;ref bestMove, cachedPosition.BestMovePromotedPiece&#41;;
            &#125;
            // Generate and sort moves.
            _board.GenerateMoves&#40;false&#41;;
            _search.SortMovesByPriority&#40;_board.RootPosition.Moves, 0, _board.RootPosition.MoveIndex - 1, true, bestMove, 0&#41;;
            WriteMessageLine&#40;"Rank   Move  Best  Cap Victim  Cap Attacker  Killer  History  Check              Priority"&#41;;
            WriteMessageLine&#40;"====  =====  ====  ==========  ============  ======  =======  =====  ===================="&#41;;
            int legalMoveNumber = 0;
            for &#40;int moveIndex = 0; moveIndex < _board.RootPosition.MoveIndex; moveIndex++&#41;
            &#123;
                ulong move = _board.RootPosition.Moves&#91;moveIndex&#93;;
                // Determine if move is legal.
                if &#40;_board.IsMoveLegal&#40;ref move&#41;&#41;
                &#123;
                    // Move is legal.
                    legalMoveNumber++;
                &#125;
                else
                &#123;
                    // Skip illegal move.                    
                    continue;
                &#125;
                WriteMessage&#40;legalMoveNumber.ToString&#40;"00"&#41;.PadLeft&#40;4&#41; + "  "&#41;;
                WriteMessage&#40;Move.ToLongAlgebraic&#40;_board, move&#41;.PadLeft&#40;5&#41; + "  "&#41;;
                WriteMessage&#40;Move.IsBest&#40;move&#41; ? "True  " &#58; "      "&#41;;
                WriteMessage&#40;Piece.GetNameFromCode&#40;Move.CaptureVictim&#40;move&#41;&#41;.PadLeft&#40;10&#41; + "  "&#41;;
                WriteMessage&#40;Piece.GetNameFromCode&#40;Move.CaptureAttacker&#40;move&#41;&#41;.PadLeft&#40;12&#41; + "  "&#41;;
                WriteMessage&#40;Move.Killer&#40;move&#41;.ToString&#40;CultureInfo.InvariantCulture&#41;.PadLeft&#40;6&#41; + "  "&#41;;
                WriteMessage&#40;Move.History&#40;move&#41;.ToString&#40;CultureInfo.InvariantCulture&#41;.PadLeft&#40;7&#41; + "  "&#41;;
                WriteMessage&#40;Move.IsCheck&#40;move&#41; ? " True  " &#58; "       "&#41;;
                WriteMessage&#40;move.ToString&#40;CultureInfo.InvariantCulture&#41;.PadLeft&#40;20&#41;&#41;;
                WriteMessageLine&#40;&#41;;
            &#125;
            WriteMessageLine&#40;&#41;;
            WriteMessageLine&#40;legalMoveNumber + " legal moves"&#41;;
            _commandStopwatch.Stop&#40;&#41;;
        &#125;


        private void ExchangeScore&#40;string&#91;&#93; Tokens&#41;
        &#123;
            ulong move = Move.ParseLongAlgebraic&#40;Tokens&#91;1&#93;&#41;;
            WriteMessageLine&#40;_search.GetExchangeScore&#40;move&#41;.ToString&#40;CultureInfo.InvariantCulture&#41;&#41;;
        &#125;


        private void Test&#40;&#41;
        &#123;
            // Verify move counts of test positions.
            // Thanks to Martin Sedlak for providing test positions and correct legal move counts.  See http&#58;//talkchess.com/forum/viewtopic.php?t=47318.
            Stream stream = Assembly.GetExecutingAssembly&#40;&#41;.GetManifestResourceStream&#40;"MadChess.TestPositions.txt"&#41;;
            if &#40;stream == null&#41;
            &#123;
                throw new MissingManifestResourceException&#40;"Failed to load test positions from internal resources."&#41;;
            &#125;
            WriteMessageLine&#40;"Number                                                                     Position  Depth     Expected        Moves  Correct    Pct"&#41;;
            WriteMessageLine&#40;"======  ===========================================================================  =====  ===========  ===========  =======  ====="&#41;;
            _board.Moves = 0L;
            _board.MovesInfoUpdate = MovesInfoInterval;
            int positions = 0;
            int correctPositions = 0;
            _commandStopwatch.Reset&#40;&#41;;
            _commandStopwatch.Start&#40;&#41;;
            using &#40;StreamReader reader = new StreamReader&#40;stream&#41;&#41;
            &#123;
                while &#40;!reader.EndOfStream&#41;
                &#123;
                    // Load position, horizon, and correct move count.
                    string line = reader.ReadLine&#40;&#41;;
                    if &#40;line == null&#41; continue;
                    positions++;
                    string&#91;&#93; tokens = Tokens.Parse&#40;line, '|', '"'&#41;;
                    string fen = tokens&#91;0&#93;;
                    int horizon = int.Parse&#40;tokens&#91;1&#93;, CultureInfo.InvariantCulture&#41;;
                    long expectedMoves = long.Parse&#40;tokens&#91;2&#93;, CultureInfo.InvariantCulture&#41;;
                    // Setup position.  Preserve move count.
                    _board.SetupPosition&#40;fen, true&#41;;
                    WriteMessage&#40;positions.ToString&#40;&#41;.PadLeft&#40;6&#41; + "  " + fen.PadLeft&#40;75&#41; + "  " + horizon.ToString&#40;"0"&#41;.PadLeft&#40;5&#41; + "  " + expectedMoves.ToString&#40;"n0"&#41;.PadLeft&#40;11&#41; + "  "&#41;;
                    // Count moves.  Do not update move count.
                    _board.MovesInfoUpdate = long.MaxValue;
                    long moves = CountMoves&#40;0, horizon&#41;;
                    bool correct = moves == expectedMoves;
                    if &#40;correct&#41;
                    &#123;
                        correctPositions++;
                    &#125;
                    double percent = &#40;&#40;100d * correctPositions&#41; / positions&#41;;
                    WriteMessageLine&#40;moves.ToString&#40;"n0"&#41;.PadLeft&#40;11&#41; + "  " + correct.ToString&#40;&#41;.PadLeft&#40;7&#41; + "  " + percent.ToString&#40;"0.0"&#41;.PadLeft&#40;5&#41;&#41;;
                &#125;
            &#125;
            _commandStopwatch.Stop&#40;&#41;;
            // Update move count.
            double movesPerSecond = _board.Moves / _commandStopwatch.Elapsed.TotalSeconds;
            WriteMessageLine&#40;&#41;;
            WriteMessageLine&#40;"Counted " + _board.Moves.ToString&#40;"n0"&#41; + " moves &#40;" + movesPerSecond.ToString&#40;"n0"&#41; + " moves per second&#41;."&#41;;
        &#125;


        private void AnalyzePositions&#40;string&#91;&#93; Tokens&#41;
        &#123;
            string file = Tokens&#91;1&#93;.Trim&#40;&#41;;
            int moveTimeMilliseconds = int.Parse&#40;Tokens&#91;2&#93;.Trim&#40;&#41;&#41;;
            int positions = 0;
            int correctPositions = 0;
            using &#40;StreamReader reader = File.OpenText&#40;file&#41;&#41;
            &#123;
                WriteMessageLine&#40;"Number                                                                     Position  Solution    Expected Moves   Move  Correct    Pct"&#41;;
                WriteMessageLine&#40;"======  ===========================================================================  ========  ================  =====  =======  ====="&#41;;
                _board.Moves = 0L;
                _board.MovesInfoUpdate = MovesInfoInterval;
                _commandStopwatch.Reset&#40;&#41;;
                _commandStopwatch.Start&#40;&#41;;
                while &#40;!reader.EndOfStream&#41;
                &#123;
                    // Load position, horizon, and correct move count.
                    string line = reader.ReadLine&#40;&#41;;
                    if &#40;line == null&#41; continue;
                    positions++;
                    string&#91;&#93; tokens = MadChess.Tokens.Parse&#40;line, ' ', '"'&#41;;
                    PositionSolution positionSolution = PositionSolution.Unknown;
                    int solutionIndex = -1;
                    int expectedMovesIndex = -1;
                    for &#40;int index = 0; index < tokens.Length; index++&#41;
                    &#123;
                        string token = tokens&#91;index&#93;.Trim&#40;&#41;.ToLower&#40;&#41;;
                        switch &#40;token&#41;
                        &#123;
                            case "bm"&#58;
                                positionSolution = PositionSolution.BestMoves;
                                solutionIndex = index;
                                break;
                            case "am"&#58;
                                positionSolution = PositionSolution.AvoidMoves;
                                solutionIndex = index;
                                break;
                        &#125;
                        if &#40;token.EndsWith&#40;";"&#41;&#41;
                        &#123;
                            expectedMovesIndex = index;
                            break;
                        &#125;
                    &#125;
                    if &#40;solutionIndex == -1&#41;
                    &#123;
                        throw new InvalidOperationException&#40;"Position does not specify a best moves or avoid moves solution."&#41;;
                    &#125;
                    if &#40;expectedMovesIndex == -1&#41;
                    &#123;
                        throw new InvalidOperationException&#40;"Position does not terminate the expected moves with a semicolon."&#41;;
                    &#125;
                    int correctMoves = expectedMovesIndex - solutionIndex;
                    string fen = string.Join&#40;" ", tokens, 0, solutionIndex&#41;.Trim&#40;&#41;;
                    string expectedMovesListStandardAlgebraic = string.Join&#40;" ", tokens, solutionIndex + 1, correctMoves&#41;.Trim&#40;&#41;.TrimEnd&#40;";".ToCharArray&#40;&#41;&#41;;
                    string&#91;&#93; expectedMovesStandardAlgebraic = expectedMovesListStandardAlgebraic.Split&#40;" ".ToCharArray&#40;&#41;&#41;;
                    ulong&#91;&#93; expectedMoves = new ulong&#91;expectedMovesStandardAlgebraic.Length&#93;;
                    string&#91;&#93; expectedMovesLongAlgebraic = new string&#91;expectedMovesStandardAlgebraic.Length&#93;;
                    // Setup position.
                    _board.SetupPosition&#40;fen, true&#41;;
                    for &#40;int moveIndex = 0; moveIndex < expectedMovesStandardAlgebraic.Length; moveIndex++&#41;
                    &#123;
                        string expectedMoveStandardAlgebraic = expectedMovesStandardAlgebraic&#91;moveIndex&#93;;
                        ulong expectedMove = Move.ParseStandardAlgebraic&#40;_board, expectedMoveStandardAlgebraic&#41;;
                        expectedMoves&#91;moveIndex&#93; = expectedMove;
                        expectedMovesLongAlgebraic&#91;moveIndex&#93; = Move.ToLongAlgebraic&#40;_board, expectedMove&#41;;
                    &#125;
                    string solution = positionSolution == PositionSolution.BestMoves ? "Best" &#58; "Avoid";
                    WriteMessage&#40;positions.ToString&#40;&#41;.PadLeft&#40;6&#41; + "  " + fen.PadLeft&#40;75&#41; + "  " + solution.PadLeft&#40;8&#41; + "  " + string.Join&#40;" ", expectedMovesLongAlgebraic&#41;.PadLeft&#40;16&#41; + "  "&#41;;
                    // Reset search.
                    UciNewGame&#40;&#41;;
                    _search.Reset&#40;&#41;;
                    // Find best move.  Do not update move count or PV.
                    _board.MovesInfoUpdate = long.MaxValue;
                    _search.PvInfoUpdate = false;
                    _search.MoveTimeSoftLimit = TimeSpan.MaxValue;
                    _search.MoveTimeHardLimit = TimeSpan.FromMilliseconds&#40;moveTimeMilliseconds&#41;;
                    ulong bestMove = _search.FindBestMove&#40;&#41;;
                    // Signal search has stopped.
                    _search.Signal.Set&#40;&#41;;
                    bool correct;
                    switch &#40;positionSolution&#41;
                    &#123;
                        case PositionSolution.BestMoves&#58;
                            correct = false;
                            foreach &#40;ulong expectedMove in expectedMoves&#41;
                            &#123;
                                if &#40;Move.Equals&#40;bestMove, expectedMove&#41;&#41;
                                &#123;
                                    correct = true;
                                    break;
                                &#125;
                            &#125;
                            break;
                        case PositionSolution.AvoidMoves&#58;
                            correct = true;
                            foreach &#40;ulong expectedMove in expectedMoves&#41;
                            &#123;
                                if &#40;Move.Equals&#40;bestMove, expectedMove&#41;&#41;
                                &#123;
                                    correct = false;
                                    break;
                                &#125;
                            &#125;
                            break;
                        default&#58;
                            throw new Exception&#40;positionSolution + " position solution not supported."&#41;;
                    &#125;
                    if &#40;correct&#41;
                    &#123;
                        correctPositions++;
                    &#125;
                    double percent = &#40;&#40;100d * correctPositions&#41; / positions&#41;;
                    WriteMessageLine&#40;Move.ToLongAlgebraic&#40;_board, bestMove&#41;.PadLeft&#40;5&#41; + "  " + correct.ToString&#40;&#41;.PadLeft&#40;7&#41; + "  " + percent.ToString&#40;"0.0"&#41;.PadLeft&#40;5&#41;&#41;;
                &#125;
            &#125;
            _commandStopwatch.Stop&#40;&#41;;
            // Update score.
            WriteMessageLine&#40;&#41;;
            WriteMessageLine&#40;"Solved " + correctPositions + " of " + positions + " positions in " + _commandStopwatch.Elapsed.TotalSeconds.ToString&#40;"0"&#41; + " seconds." &#41;;
            // Update move count.
            double movesPerSecond = _board.Moves / _commandStopwatch.Elapsed.TotalSeconds;
            WriteMessageLine&#40;"Counted " + _board.Moves.ToString&#40;"n0"&#41; + " moves &#40;" + movesPerSecond.ToString&#40;"n0"&#41; + " moves per second&#41;."&#41;;
        &#125;


        private void Go&#40;string&#91;&#93; Tokens&#41;
        &#123;
            _commandStopwatch.Reset&#40;&#41;;
            _commandStopwatch.Start&#40;&#41;;
            // Reset search.
            _search.Reset&#40;&#41;;
            // Reset killer moves and move history.
            _killerMoves.Reset&#40;&#41;;
            _moveHistory.Reset&#40;&#41;;
            for &#40;int tokenIndex = 1; tokenIndex < Tokens.Length; tokenIndex++&#41;
            &#123;
                string token = Tokens&#91;tokenIndex&#93;;
                switch &#40;token.ToLower&#40;CultureInfo.InvariantCulture&#41;&#41;
                &#123;
                    case "wtime"&#58;
                        _search.WhiteTimeRemaining = TimeSpan.FromMilliseconds&#40;int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;&#41;;
                        break;
                    case "btime"&#58;
                        _search.BlackTimeRemaining = TimeSpan.FromMilliseconds&#40;int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;&#41;;
                        break;
                    case "winc"&#58;
                        _search.WhiteTimeIncrement = TimeSpan.FromMilliseconds&#40;int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;&#41;;
                        break;
                    case "binc"&#58;
                        _search.BlackTimeIncrement = TimeSpan.FromMilliseconds&#40;int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;&#41;;
                        break;
                    case "movestogo"&#58;
                        _search.MovesToTimeControl = int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;;
                        break;
                    case "depth"&#58;
                        _search.HorizonLimit = int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;;
                        break;
                    case "nodes"&#58;
                        _search.NodeLimit = long.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;;
                        break;
                    case "mate"&#58;
                        _search.MateInMoves = int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;;
                        break;
                    case "movetime"&#58;
                        _search.MoveTimeSoftLimit = TimeSpan.MaxValue;
                        _search.MoveTimeHardLimit = TimeSpan.FromMilliseconds&#40;int.Parse&#40;Tokens&#91;tokenIndex + 1&#93;, CultureInfo.InvariantCulture&#41;&#41;;
                        break;
                    case "infinite"&#58;
                        _search.MoveTimeHardLimit = TimeSpan.MaxValue;
                        break;
                &#125;
            &#125;
            // Find best move.
            ulong bestMove = _search.FindBestMove&#40;&#41;;
            // Signal search has stopped.
            _commandStopwatch.Stop&#40;&#41;;
            _search.Signal.Set&#40;&#41;;
            // Respond with best move.
            WriteMessageLine&#40;"bestmove " + Move.ToLongAlgebraic&#40;_board, bestMove&#41;&#41;;
        &#125;


        private void Stop&#40;&#41;
        &#123;
            _search.Continue = false;
            // Wait for search to complete.
            _search.Signal.WaitOne&#40;&#41;;
        &#125;


        private void Quit&#40;int ExitCode&#41;
        &#123;
            Dispose&#40;true&#41;;
            Environment.Exit&#40;ExitCode&#41;;
        &#125;


        public void WriteMessage&#40;string Message&#41;
        &#123;
            Console.Write&#40;Message&#41;;
            if &#40;Log&#41;
            &#123;
                // Log message.
                LogMessage&#40;CommandDirection.Out, Message, false&#41;;
            &#125;
        &#125;


        public void WriteMessageLine&#40;&#41;
        &#123;
            Console.WriteLine&#40;&#41;;
            if &#40;Log&#41;
            &#123;
                // Log message.
                LogMessage&#40;CommandDirection.Out, null, true&#41;;
            &#125;
        &#125;


        public void WriteMessageLine&#40;string Message&#41;
        &#123;
            Console.WriteLine&#40;Message&#41;;
            if &#40;Log&#41;
            &#123;
                // Log message.
                LogMessage&#40;CommandDirection.Out, Message, true&#41;;
            &#125;
        &#125;


        private void LogMessage&#40;int Direction, string Message, bool IncludeTimestamp&#41;
        &#123;
            lock &#40;_logLock&#41;
            &#123;
                if &#40;_lastMessageIncludedTimestamp&#41;
                &#123;
                    _logWriter.Write&#40;Direction == CommandDirection.In ? " In   " &#58; " Out  "&#41;;
                &#125;
                _logWriter.Write&#40;Message&#41;;
                if &#40;IncludeTimestamp&#41;
                &#123;
                    _lastMessageIncludedTimestamp = true;
                    TimeSpan elapsed = _mainStopwatch.Elapsed;
                    _logWriter.WriteLine&#40;&#41;;
                    _logWriter.Write&#40;elapsed.Hours.ToString&#40;"00", CultureInfo.InvariantCulture&#41; + "&#58;" + elapsed.Minutes.ToString&#40;"00", CultureInfo.InvariantCulture&#41; + "&#58;" +
                        elapsed.Seconds.ToString&#40;"00", CultureInfo.InvariantCulture&#41; + "." + elapsed.Milliseconds.ToString&#40;"000", CultureInfo.InvariantCulture&#41; + "  "&#41;;
                &#125;
                else
                &#123;
                    _lastMessageIncludedTimestamp = false;
                &#125;
            &#125;
        &#125;


        public void HandleException&#40;Exception Exception&#41;
        &#123;
            Log = true;
            _stringBuilder.Length = 0;
            Exception exception = Exception;
            do
            &#123;
                // Display message and write to log.
                _stringBuilder.AppendLine&#40;exception.Message&#41;;
                _stringBuilder.AppendLine&#40;&#41;;
                _stringBuilder.AppendLine&#40;exception.StackTrace&#41;;
                _stringBuilder.AppendLine&#40;&#41;;
                exception = exception.InnerException;
            &#125; while &#40;exception != null&#41;;
            WriteMessageLine&#40;_stringBuilder.ToString&#40;&#41;&#41;;
            Quit&#40;-1&#41;;
        &#125;
    &#125;
&#125;
User avatar
Mark Uniacke
Hiarcs Author
Posts: 1458
Joined: Sun Jul 29, 2007 1:32 pm
Location: United Kingdom
Contact:

Post by Mark Uniacke »

I will try to look at this next week.
Best wishes,
Mark

https://www.hiarcs.com
User avatar
emadsen
Member
Posts: 8
Joined: Fri Jul 31, 2015 3:28 am

Post by emadsen »

Mark Uniacke wrote:I will try to look at this next week.
I appreciate it Mark. I have confirmed the .NET 4.6 version of my engine functions correctly in the Shredder 12 and Fritz 14 GUIs. The .NET 3.5 version functions correctly in the Hiarcs GUI, but not the .NET 4.6 version.

I'll send you a PM with a link to a MadChess .exe compiled for .NET 4.6.
User avatar
Mark Uniacke
Hiarcs Author
Posts: 1458
Joined: Sun Jul 29, 2007 1:32 pm
Location: United Kingdom
Contact:

Post by Mark Uniacke »

We notice that Microsoft have introduced some issues with .NET4 (for those poor souls who use it ;-))

https://social.msdn.microsoft.com/Forum ... arpgeneral

"There is indeed a change that was introduced in .NET 4, when reading from a console stream it first tries to wait on the stdout handle before actually reading from it"

Somehow the Madchess 2.0 you sent me works with our 1.9 candidate but Naum 4.6 does not work.
Best wishes,
Mark

https://www.hiarcs.com
User avatar
Mark Uniacke
Hiarcs Author
Posts: 1458
Joined: Sun Jul 29, 2007 1:32 pm
Location: United Kingdom
Contact:

Post by Mark Uniacke »

We have tested with more .NET4 & 4.5 engines and v1.9 works with them ok.
Best wishes,
Mark

https://www.hiarcs.com
User avatar
emadsen
Member
Posts: 8
Joined: Fri Jul 31, 2015 3:28 am

Post by emadsen »

Mark Uniacke wrote:We notice that Microsoft have introduced some issues with .NET4 (for those poor souls who use it ;-))

https://social.msdn.microsoft.com/Forum ... arpgeneral

"There is indeed a change that was introduced in .NET 4, when reading from a console stream it first tries to wait on the stdout handle before actually reading from it"

Somehow the Madchess 2.0 you sent me works with our 1.9 candidate but Naum 4.6 does not work.
Thanks for the update Mark. Are you using a new version of Qt in HCE 1.9? Perhaps the new version of Qt and .NET 4 play together well? Or did you change any I/O code in HCE 1.9?

I didn't think Naum was a .NET engine. So the issue may not be confined to .NET.
User avatar
Mark Uniacke
Hiarcs Author
Posts: 1458
Joined: Sun Jul 29, 2007 1:32 pm
Location: United Kingdom
Contact:

Post by Mark Uniacke »

v1.9 uses a more recent version of Qt.
Best wishes,
Mark

https://www.hiarcs.com
Post Reply