示例#1
0
 /// <summary>
 /// Send a command to the chess engine.  Commands are serialized on another thread
 /// </summary>
 /// <param name="commandString">command to send</param>
 /// <param name="expectedResponse">response we expect to get.  If this is
 /// an empty string, then sync up after the command with the engine</param>
 void IChessEngine.SendCommandAsync(string commandString, string expectedResponse)
 {
     // Under lock - update the queue of commands to execute and signal
     // our running worker thread (or create it the first time)
     lock (queue_lock)
     {
         CommandExecutionParameters cep =
             new CommandExecutionParameters(commandString, expectedResponse);
         Debug.WriteLine(String.Format("Enqueueing command pair (\"{0}\", \"{1}\")", commandString, expectedResponse));
         commandQueue.Enqueue(cep);
         // Signal the worker thread
         newCommandAddedToQueue.Set();
     }
 }
示例#2
0
        /// <summary>
        /// Worker Thread Method responsible for processing commands to the chess engine.
        /// When first launched, the thread will attempt to launch the engine process,
        /// then enter a wait state until work is sent (added to the queue).
        ///
        /// When signalled for work, the thread will take the next queueded item (if any)
        /// and send the command to the engine, and wait for the response triggered in
        /// the stdout callback OnDataReceieved.  This will repeat so long as items
        /// are in the queue.
        ///
        /// If a command has no response (e.g. 'position' uci command) a pair of
        /// "IsReady"/"ReadyOk" command/response is sent to sync up.
        ///
        /// This thread exists for the lifetime of the owning ChessGame object
        /// </summary>
        public void CommandQueueExecutionMethod()
        {
            // The loader and path are injected, so the UCIChessEngine object doesn't
            // need to care how the interface was created/obtained/loaded from disk
            engineProcess = engineProcessLoader.Start(fullPathToEngine, OnDataReceived);

            AutoResetEvent[] workerEvents = { newCommandAddedToQueue, shutdownCommandQueue };
            int  waitResult = WaitHandle.WaitTimeout;
            bool exitThread = false;
            bool moreWork   = false;

            while (!exitThread)
            {
                lock (queue_lock)
                {
                    moreWork = (commandQueue.Count() > 0);
                }

                if (!moreWork)
                {
                    waitResult = WaitHandle.WaitAny(workerEvents);
                }
                else
                {
                    waitResult = 0; // Process more items
                }

                if (waitResult == 1) // shutdownCommandQueue
                {
                    exitThread = true;
                }
                else
                {
                    // Set to an empty one since it's a value type...but we only care
                    // if we actually find one.
                    string dummyCommand            = "{CommandQueueExecutionMethod}";
                    CommandExecutionParameters cep = new CommandExecutionParameters(dummyCommand, "");

                    // There should be new work to do
                    lock (queue_lock)
                    {
                        if (commandQueue.Count() > 0)
                        {
                            // Do not remove the item yet - wait until it's processed
                            cep = commandQueue.Peek();
                        }
                    }

                    if ((String.Compare(cep.Command, dummyCommand) != 0) && (engineProcess.InputStream != null))
                    {
                        // Found something - write it to the process,

                        Debug.WriteLine(String.Concat("=>Engine: ", cep.Command));
                        engineProcess.InputStream.WriteLine(cep.Command);

                        // If this is true, the above command has no response, and we'd wait here forever
                        if (cep.NeedsIsReadySync)
                        {
                            StreamWriter      writer         = engineProcess.InputStream;
                            UciIsReadyCommand isReadyCommand = new UciIsReadyCommand(ref writer);
                            isReadyCommand.Execute(this);
                        }

                        // Wait on the exit event as well, so we can bail as fast as needed
                        AutoResetEvent[] commandEvents = { uciCommandFinished, shutdownCommandQueue };
                        int commandWait = WaitHandle.WaitAny(commandEvents);
                        if (commandWait == 1)
                        {
                            exitThread = true;
                        }
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        /// Event handler for process stdout.  This is where we parse responses from the chess engine
        /// </summary>
        /// <param name="sender">ignored</param>
        /// <param name="e">The string sent to stdout</param>
        public void OnDataReceived(object sender, DataReceivedEventArgs e)
        {
            // This level gets all responses from the engine, thinking lines, options, debug, etc
            // However if we get a null event, filter it out since there is nothing we can do
            if (e.Data == null)
            {
                return;
            }

            string data = e.Data;

            SendVerboseEvent(data); // let the verbose handler get the empty lines

            if (data == String.Empty)
            {
                return; // don't bother, nothing to do
            }

            // Spike 1.1
            if (String.Compare(data, "Error: Fatal no best move") == 0)
            {
                // Convert to something approaching a real protocol response
                data = "bestmove (none)";
                SendVerboseEvent(String.Format("Fixed formatting to \"{0}\"", data));
            }
            // MadChess
            else if (String.Compare(data, "bestmove Null") == 0)
            {
                data = "bestmove (none)";
                SendVerboseEvent(String.Format("Fixed formatting to \"{0}\"", data));
            }

            // Set to an empty one since it's a value type...but we only care
            // if we actually find one.
            string dummyCommand            = "{OnDataReceived}";
            CommandExecutionParameters cep = new CommandExecutionParameters("", "");

            // Don't need a lock here, just need to know if it's > 0, and only this
            // method Dequeues
            if (commandQueue.Count() > 0)
            {
                cep = commandQueue.Peek();
            }
            bool foundCommand = (String.Compare(dummyCommand, cep.Command) != 0);
            bool dequeue      = false;

            if (foundCommand)
            {
                // First check if we need to wait on a ReadyOK response, this happens
                // when there is no expected response when queued
                if (cep.NeedsIsReadySync)
                {
                    if (String.Compare(data, ReadyOk) == 0)
                    {
                        dequeue         = true; // This dequeues the original command
                        commandResponse = data; // nothing was actually queued for the "IsReady" write
                    }
                }
                else if (data.StartsWith(cep.Expected))
                {
                    dequeue         = true; // Mark command for dequeue and
                    commandResponse = data; // save the response

                    // If we're asking for a move - then save the response we care about
                    // the SAN for the move - it comes right after "bestmove"
                    // If no move (e.g. mate) will return 'bestmove (none)'
                    if (data.StartsWith(BestMoveResponse))
                    {
                        string[] parts = data.Split(' ');
                        bestMove = parts[1];
                    }
                }
            }

            // Remove item if processed and notify listeners
            if (dequeue)
            {
                lock (queue_lock)
                {
                    commandQueue.Dequeue();
                }

                SendReceivedEvent(CommandResponse);
                uciCommandFinished.Set();
            }
        }