PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197
Details on How to Use Named Pipe for Communicating with Chess Engine-VBForums
Results 1 to 22 of 22

Thread: Details on How to Use Named Pipe for Communicating with Chess Engine

  1. #1

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Details on How to Use Named Pipe for Communicating with Chess Engine

    I'm looking to communicate my VB6 program with the Stockfish (UCI) chess engine. I found the following code which I saved into a module called "ConsolePipe.bas".

    Code:
    Option Explicit
    Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
    Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
    Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
    Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long
    Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
    
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    
    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
    End Type
    
    Private Type PROCESS_INFORMATION
        hProcess As Long
        hThread As Long
        dwProcessId As Long
        dwThreadId As Long
    End Type
    
    Private Type STARTUPINFO
        cb As Long
        lpReserved As Long
        lpDesktop As Long
        lpTitle As Long
        dwX As Long
        dwY As Long
        dwXSize As Long
        dwYSize As Long
        dwXCountChars As Long
        dwYCountChars As Long
        dwFillAttribute As Long
        dwFlags As Long
        wShowWindow As Integer
        cbReserved2 As Integer
        lpReserved2 As Byte
        hStdInput As Long
        hStdOutput As Long
        hStdError As Long
        End Type
        
        Private Type OVERLAPPED
        ternal As Long
        ternalHigh As Long
        offset As Long
        OffsetHigh As Long
        hEvent As Long
    End Type
    
    Private Const STARTF_USESHOWWINDOW = &H1
    Private Const STARTF_USESTDHANDLES = &H100
    Private Const SW_HIDE = 0
    Private Const EM_SETSEL = &HB1
    Private Const EM_REPLACESEL = &HC2
    
    Private Sub Command1_Click()
        Command1.Enabled = False
        Redirect Text1.Text, Text2
        Command1.Enabled = True
    End Sub
    
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
        If Command1.Enabled = False Then Cancel = True
    End Sub
    
    Sub Redirect(CmdLine As String, objTarget As Object)
        Dim i%, t$
        Dim pa As SECURITY_ATTRIBUTES
        Dim pra As SECURITY_ATTRIBUTES
        Dim tra As SECURITY_ATTRIBUTES
        Dim pi As PROCESS_INFORMATION
        Dim sui As STARTUPINFO
        Dim hRead As Long
        Dim hWrite As Long
        
        Dim bRead As Long
        Dim lpBuffer(1024) As Byte
        pa.nLength = Len(pa)
        pa.lpSecurityDescriptor = 0
        pa.bInheritHandle = True
        
        pra.nLength = Len(pra)
        tra.nLength = Len(tra)
    
        If CreatePipe(hRead, hWrite, pa, 0) <> 0 Then
            sui.cb = Len(sui)
            GetStartupInfo sui
            
            sui.hStdOutput = hWrite
            sui.hStdError = hWrite
            sui.dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
            sui.wShowWindow = SW_HIDE
            If CreateProcess(vbNullString, CmdLine, pra, tra, True, 0, 0, vbNullString, sui, pi) <> 0 Then
    
                    SetWindowText objTarget.hwnd, ""
    
                    Do
                        Erase lpBuffer()
    
                        If ReadFile(hRead, lpBuffer(0), 1023, bRead, ByVal 0&) Then
                            SendMessage objTarget.hwnd, EM_SETSEL, -1, 0
                            SendMessage objTarget.hwnd, EM_REPLACESEL, False, lpBuffer(0)
                            DoEvents
                        Else
                            CloseHandle pi.hThread
                            CloseHandle pi.hProcess
                            Exit Do
                        End If
                        CloseHandle hWrite
                    Loop
                    CloseHandle hRead
            End If
        End If
    
    End Sub
    What I need to do is:

    1. Call this program which operates from the Windows 10 command prompt. This can be done using: Shell "stockfish.exe".
    2. Once it starts, automatically enter "fen position etc. etc. etc." and then ENTER (carriage return).
    3. Then automatically enter "depth 30 movetime 15000" and then ENTER (carriage return).
    4. Wait 15 seconds.
    5. Read the output from Stockfish which appears in the console.
    6. Close the command prompt window. Being able to make it invisible right from the beginning is the main intention, actually.

    The trouble is:

    1. I don't know EXACTLY what to call from the module above and EXACTLY how to call it. If someone could show me in actual code with the actual words (e.g. "stockfish.exe" "fen position etc.") that would be a big help. I've tried messing around with the contents of "Command1_Click" above but nothing seems to work. I'm totally new to this piping business. Thanks in advance.

  2. #2
    PowerPoster
    Join Date
    Feb 2006
    Posts
    20,625

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Sounds like you are talking about anonymous pipes, a separate but related topic.

    Maybe look at VB6 - ShellPipe "Shell with I/O Redirection" control

  3. #3

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    That doesn't work for me either. I just need to do Steps 1-6 above.

  4. #4
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    There is no "fen position etc. etc. etc." command. There is no "depth 30 movetime 15000" command too. You are making it hard for anyone to help.

    Here is the Stockfish 10 for Windows console app: https://stockfishchess.org/download/

    Here is UCI protocol chess engines are using: http://wbec-ridderkerk.nl/html/UCIProtocol.html

    Real commands:

    position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
    go depth 30 movetime 15000

    cheers,
    </wqw>

  5. #5

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Yes, I know. The question was, how do I get something like "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" and then "go depth 30 movetime 15000" into the command window (with Stockfish running) using VB6? Right now, all I can do is start stockfish.exe. That's it. Nothing else can be passed to it after that.

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,504

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Well, I'm not going to figure out how to do everything you want to do, but as a minimum, I figured I would give dilettante's code a quick test.
    Turns out I had downloaded it already sometime in the past.
    I downloaded a copy of stockfishchess from the link wqweto provided.

    As a quick test, I figured I would do a minimal change to dilettante's code. I wanted to try sending the commands more than once, so I added another button to the form, and I commented out dilettante's test code, and didn't close the Standard Pipe object after opening it.

    I tested dilettante's example before making any changes to ensure it ran unaltered, so made some assumptions that it would continue to work after my changes.


    So, the modifications were.

    Note: When I extracted stockfish, I got and extra directory level and didn't move it up before running the test, so the path is longer than necessary as I have the same directory twice...
    In the cmdGo button's click event, change the SP.Run line to run stockfish instead of the vbscript.
    Also, commented out dil's test and didn't close the pipe.
    Code:
         SPResult = SP.Run("c:\c\tmp\stockfish-10-win\stockfish-10-win\Windows\stockfish_10_x64.exe")
        Select Case SPResult
            Case SP_SUCCESS
    '            Do Until EOF(1)
    '                Line Input #1, TextLine
    '                SP.SendLine TextLine
    '            Loop
    '
    '            SP.ClosePipe
    Then, to watch the return data interactively, change the Print #2 to a Debug.Print, so could watch the response in the Immediate window.
    Code:
    Private Sub SP_DataArrival(ByVal CharsTotal As Long)
        With SP
            Do While .HasLine
     '           Print #2, .GetLine()
                Debug.Print .GetLine()
            Loop
        End With
    End Sub
    As I said, I added a command button to test the strings wqweto provided to send to the engine.
    Code:
    Private Sub Command1_Click()
        Dim TextLine As String
    
        TextLine = "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
        SP.SendLine TextLine
        TextLine = "go depth 30 movetime 15000"
        SP.SendLine TextLine
    
    End Sub
    And that was it.

    I started the program, and hit the Go button.
    I didn't get an error, so it started the chess engine without issue.

    I pressed the Command1 button, and the immediate window showed the chess engine spewing out its evaluations and eventually printing the line "bestmove e2e4 ponder e7e6".

    After the "bestmove" was shown , I pressed the common button again, to verify it would repeat and process the input.

    That is about a far as I will go, and it appears to work reasonably well to me.
    Last edited by passel; Oct 11th, 2019 at 08:51 AM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  7. #7

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Okay, thanks. There appears to be some progress. I've adapted the material above to the following function and sub in my program.

    Code:
    Public Function Invoke_UCI_Engine(engine_path As String) As String
    
    Dim SPResult As SP_RESULTS
    Dim TextLine As String
    
    
    On Error Resume Next
    
    SPResult = SP.Run(engine_path)
    
    Do
    DoEvents
    Loop Until SPResult = SP_SUCCESS
    
    TextLine = "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    SP.SendLine TextLine
    TextLine = "go depth 30 movetime 15000"
    SP.SendLine TextLine
    
    End Function

    Code:
    Private Sub SP_DataArrival(ByVal CharsTotal As Long)
    
    With SP
        Do While .HasLine
            Engine_Analysis = Engine_Analysis & .GetLine() & vbNewLine
        Loop
    End With
    
    End Sub
    The trouble is, when I replace "Debug.Print .GetLine()" with "Engine_Analysis = Engine_Analysis & .GetLine() & vbNewLine", nothing is being stored in the Engine_Analysis global variable. The lines only appear in the Immediate window if I use "Debug.Print .GetLine()". Is there no way to capture what appears in the Immediate (debug) window so I use the information elsewhere in my program?

  8. #8
    PowerPoster
    Join Date
    Jun 2013
    Posts
    4,403

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    FWIW - here's a demo-snippet, which makes use of a "Process-Object" (created by "Shell-Exec"),
    and which implements StdIn, StdOut and StdErr-Objects (all 3 implementing the Interface of the TextStream-Object).

    So, normally no API-calls are needed - the APIs I'm using in the snippet below,
    are only to "hide the Console-Window" after starting-up the StockFish-Process via Shell.Exec(...).
    (sadly the Shell.Exec-Command does not support "visibility-args" for the shelled process)

    Code:
    Option Explicit
     
    Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal Cmd As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, PID As Long) As Long
    Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal Cmd As Long) As Long
    
    Private oP As Object
    
    Private Sub Form_Load()
      Set oP = CreateObject("WScript.Shell").Exec(App.Path & "\stockfish_10_x64.exe")
      SendCommand "" 'initialize the engine
      HideProcess oP, Me.hWnd
    End Sub
    
    Private Sub Form_Click() 'the command-call-examples here, all behave synchronous
      Caption = "Command-Sequence started..."
      
        SendCommand "uci", "uciok"
        SendCommand "ucinewgame"
        SendCommand "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
        SendCommand "go depth 30 movetime 15000", "bestmove" '<- this can take a while, until the call returns...
        
      Caption = "Command-Sequence finished!"
    End Sub
     
    Private Function SendCommand(sCmd As String, Optional sWaitFor$ = "readyok") As String
      If oP.Status = 0 And Len(sCmd) > 0 Then oP.StdIn.WriteLine sCmd
      If oP.Status = 0 And sWaitFor = "readyok" Then oP.StdIn.WriteLine "isready"
      Dim sLine As String
      Do Until oP.Status Or InStr(sLine, sWaitFor) = 1
         sLine = oP.StdOut.ReadLine
         SendCommand = SendCommand & sLine & vbCrLf
         If Len(sCmd) Then ResponseLineCallback sCmd, sLine
      Loop
    End Function
    
    Private Sub ResponseLineCallback(sCmd As String, sLine As String)
      Debug.Print Split(sCmd, " ")(0), sLine
      DoEvents
    End Sub
    
    'just a helper, to hide the (by default started "non-hidden") StockFish-Process-ConsoleWindow
    Private Function HideProcess(oP As Object, ByVal hWnd As Long) As Long
      hWnd = GetWindow(hWnd, 0) 'first sibling
      Do While hWnd
        GetWindowThreadProcessId hWnd, HideProcess
        If HideProcess = oP.ProcessID Then ShowWindow hWnd, 0: Exit Do
        hWnd = GetWindow(hWnd, 2) 'next sibling
      Loop
    End Function
    HTH

    Olaf

  9. #9
    PowerPoster
    Join Date
    Feb 2006
    Posts
    20,625

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    That's an option but using the scripting library like that is a little clunky.

    First I'd avoid using it late-bound. There is no advantage in doing so and you sacrifice both Intellisense and the ability to look things up in the Object Browser. Those aren't major things but there isn't anything to be gained by sacrificing those programming aids. Early binding gets you performance but nothing that matters at all here. It's just a good habit to always use references and early binding except when you can't due to binary compatibility concerns.

    Second though all you have are blocking calls for the standard I/O streams. That works as long as the interaction is all lock-stepped "give then take" and you don't care about blocking your program's user interface thread.


    The ShellPipe control is far from perfect. It has to use Timer-driven polling and the dread DoEvents to accomplish async operation. Without a background thread it doesn't get much better than that. If I just used blocking calls the code could be radically simplified.

    I always wondered why we never got a Microsoft OCX for anonymous and Named pipes. I used to use a proprietary Named Pipes OCX from a 3rd party that was actually written for them by Microsoft, so I know it can be done. Sadly neither of them ever released it for general use.


    But at least now he has two options to choose from.

  10. #10
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Here is a 3-rd option: cExec.zip

    This class allows to start the console app hidden and w/ limit flags like JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. The latter one marks the child process to be terminated when the parent is terminated (if child is still running that is) in order not to leave orphaned (hidden) stockfish processes when your main app is getting closed or crashing. The class allows basic polling on output/error and writing to child process input too so it's not events based.

    I've been using this class to start (hidden) putty.exe in order to open SSH tunnels to some DB servers.

    cheers,
    </wqw>

  11. #11
    PowerPoster
    Join Date
    Jun 2013
    Posts
    4,403

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Quote Originally Posted by dilettante View Post
    That's an option but using the scripting library like that is a little clunky.
    Well, IMO it's a (very robust) working example, which uses LateBinding on the used "Process-encapsulating-Object" -
    for the sake of "easy-Copy&Paste-ness" (into an empty VB-Form-Project, which has the ChessEngine-executable in its App.Path)

    So, to make this example "non-clunky", one simply has to change the declaration of the sole Private Variable:
    Private oP As IWshRuntimeLibrary.WshExec '<- formerly As Object

    Quote Originally Posted by dilettante View Post
    Second though all you have are blocking calls for the standard I/O streams...
    Quite deliberately so (for this "uci-scenario")...

    Because "callig into the exe" is then exactly as one would expect when working against most StdDll-Binaries:
    Synchronous! ... (DoEvents - albeit contained in the little "LineCallback"-routine - not really needed).

    Olaf

  12. #12

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Schmidt,

    Thanks. Your code works a lot better for me. The earlier one, actually, wasn't even getting full output from the UCI engine. For instance, in positions like this: "1r2b3/2p5/3kB2r/K7/p3QP2/8/6P1/8 w - - 0 1", it was getting stuck at depth 9 to 12, never actually producing the best move. I'm not sure why. Your code seems to handle everything. I had to modify it as follows, however, to actually work within my own program.

    Code:
    Public Function Invoke_UCI_Engine(engine_path As String, the_fen As String, the_depth As Integer, move_time As Double) As String
    
    Engine_Analysis = vbNullString
    Set oP = CreateObject("WScript.Shell").Exec(engine_path)
    SendCommand "" 'initialize the engine
    HideProcess oP, Me.hWnd
    
    Call SendCommand("position fen " & the_fen)
    Call SendCommand("go depth " & the_depth & " movetime " & move_time, engine_path, "bestmove") '<- this can take a while, until the call returns...
    Call Delay_in_Sec((move_time + 2000) / 1000)
    Invoke_UCI_Engine = Engine_Analysis
    
    End Function
    I had to add a delay (a couple of seconds longer than the move time) otherwise the information won't register in the "Engine_Analysis" global string variable.

    Code:
    Private Function SendCommand(sCmd As String, Optional engine_path As String, Optional sWaitFor$ = "readyok") As String
      
    Dim sLine As String
      
    If oP.Status = 0 And Len(sCmd) > 0 Then oP.StdIn.WriteLine sCmd
    If oP.Status = 0 And sWaitFor = "readyok" Then oP.StdIn.WriteLine "isready"
      
    Do Until oP.Status Or InStr(sLine, sWaitFor) = 1
       sLine = oP.StdOut.ReadLine
       SendCommand = SendCommand & sLine & vbCrLf
       If Len(sCmd) Then ResponseLineCallback sCmd, sLine
    Loop
      
    If engine_path <> vbNullString Then
        Shell "taskkill.exe /s " & ComputerName & " /u " & Environ("USERDOMAIN") & "\" & Environ("USERNAME") & " /t /im " & FileNamefromPath(engine_path) & " /f", _
        vbHide
    End If
    If I don't "kill" the UCI engine after each invocation, I can't enter a new position to analyze (it won't work).

    Code:
    Private Sub ResponseLineCallback(sCmd As String, sLine As String)
      
    If InStr(sLine, "readyok") = 0 Then
      Engine_Analysis = Engine_Analysis & sLine & vbNewLine
    End If
    DoEvents
      
    End Sub
    I also had to ensure I only got the analysis lines in the Engine_Analysis string and not all the "readyok" stuff that comes out too.

    Code:
    Private Function HideProcess(oP As Object, ByVal hWnd As Long) As Long
    
    hWnd = GetWindow(hWnd, 0) 'first sibling
    Do While hWnd
      GetWindowThreadProcessId hWnd, HideProcess
      If HideProcess = oP.ProcessID Then ShowWindow hWnd, 0: Exit Do
      hWnd = GetWindow(hWnd, 2) 'next sibling
    Loop
    
    End Function
    I have a question for about this last segment, though. Is there any way to hide the UCI engine window completely? Now, it appears as a distracting pop-up and disappears almost instantly but it's still distracting (as I have to call it many times). Also, I found that, on occasion, it minimized my main program instead of the UCI engine command window. Being able to hide it completely would be fantastic. I've actually seen this done in Python. It makes a call to the engine and gets its output without any window appearing at all. Thanks again.

    wqweto,

    I imported your 'cExec.zip' class but have no idea how to use it. Could you provide some examples? Like starting the UCI engine, sending it a test position and getting the output into a string? Thanks.

  13. #13
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Quote Originally Posted by victor_knight View Post
    Could you provide some examples? Like starting the UCI engine, sending it a test position and getting the output into a string?
    IMO you had to provide these test samples in OP, not waiting for Olaf to research the UCI protocol commands w/ expected successful response that stockfish outputs. We are missing the failure responses documented and that is why currently SendCommand can hang on unexpected input which is something that has to be taken care of before completing this project, probably.

    cheers,
    </wqw>

  14. #14

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    What about with this part?

    Is there any way to hide the UCI engine window completely?
    Do you know how to modify the following code so there is no pop-up (even for a second)? I would like command window to be completely invisible since I call it many times in my program.

    Code:
    Private Function HideProcess(oP As Object, ByVal hWnd As Long) As Long
    
    hWnd = GetWindow(hWnd, 0) 'first sibling
    Do While hWnd
      GetWindowThreadProcessId hWnd, HideProcess
      If HideProcess = oP.ProcessID Then ShowWindow hWnd, 0: Exit Do
      hWnd = GetWindow(hWnd, 2) 'next sibling
    Loop
    
    End Function

  15. #15
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Quote Originally Posted by victor_knight View Post
    Do you know how to modify the following code so there is no pop-up (even for a second)?
    There is no way to pass SW_HIDE to WshExec from scripting runtime.

    This is already implemented sanely in cExec class w/ StartHidden parameter of Run method.

    cheers,
    </wqw>

  16. #16

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    There is no way to pass SW_HIDE to WshExec from scripting runtime.
    And there is apparently no way to send and receive information from the UCI engine otherwise. A catch-22.

  17. #17
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Quote Originally Posted by victor_knight View Post
    And there is apparently no way to send and receive information from the UCI engine otherwise. A catch-22.
    Or you can build WshExec alternative yourself by using cExec class to borrow ideas. . .

    JFYI, the pipes used throughout this topic are *anonymous* (not named) which get *redirected* (might help w/ google searches).

    cheers,
    </wqw>

  18. #18

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Couldn't find anything much online about how to do that, exactly. Perhaps I'll look into Python; write a simple piping EXE in that (which doesn't cause the UCI engine window to pop-up at all) and then simply call it from my VB6 program. Not very elegant, but what the hell. It should work.

  19. #19
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,504

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Quote Originally Posted by victor_knight View Post
    Okay, thanks. There appears to be some progress. I've adapted the material above to the following function and sub in my program.

    Code:
    Public Function Invoke_UCI_Engine(engine_path As String) As String
    
    Dim SPResult As SP_RESULTS
    Dim TextLine As String
    
    
    On Error Resume Next
    
    SPResult = SP.Run(engine_path)
    
    Do
    DoEvents
    Loop Until SPResult = SP_SUCCESS
    
    TextLine = "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    SP.SendLine TextLine
    TextLine = "go depth 30 movetime 15000"
    SP.SendLine TextLine
    
    End Function

    Code:
    Private Sub SP_DataArrival(ByVal CharsTotal As Long)
    
    With SP
        Do While .HasLine
            Engine_Analysis = Engine_Analysis & .GetLine() & vbNewLine
        Loop
    End With
    
    End Sub
    The trouble is, when I replace "Debug.Print .GetLine()" with "Engine_Analysis = Engine_Analysis & .GetLine() & vbNewLine", nothing is being stored in the Engine_Analysis global variable. The lines only appear in the Immediate window if I use "Debug.Print .GetLine()". Is there no way to capture what appears in the Immediate (debug) window so I use the information elsewhere in my program?
    Worked fine for me. Who knows at what point you clear the Engine_Analysis global variable, and if you've waited long enough. Do you really need all the analysis, or just the bestmove and ponder line?

    In any case, I just changed the code to add a variable, and to verify that it wasn't taking too long, looked for the "bestmove" line and timed how much time was spent processing. Here is the output I was getting with a few presses of command1. Of course, I waited until I got a response before pressing command1 again. Code to disable Command1 and reenable it again after receiving the bestmove could be added, but shouldn't be necessary for a quick test if the tester is disciplined.
    Code:
    bestmove e2e4 ponder e7e6   took  15.03739  seconds to compute
    bestmove e2e4 ponder e7e6   took  15.06038  seconds to compute
    bestmove d2d4 ponder g8f6   took  15.05502  seconds to compute
    The additional code modifications I made to my previous post.
    Code:
    'added a couple of Form scope variables
    Private Engine_Analysis As String
    Private TimeToProcess As Single
    
    'I also commented out the opening of file #1 in the cmdGo_Click Sub. 
    'Not sure why it wasn't commented out before, but didn't find the file when I ran it this time, so I commented it out as it isn't needed.
    
    'The Command1_Click() sub now looks like this.
    'I clear the Engine_Analysis string, and note the StartTime 
    '(realize this will not give a correct time if you compute through midnight, I didn't bother compensating for this simple test)
    
    Private Sub Command1_Click()
        Dim TextLine As String
        
        Engine_Analysis = ""
        TextLine = "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
        SP.SendLine TextLine
        TextLine = "go depth 30 movetime 15000"
        SP.SendLine TextLine
        TimeToProcess = Timer
    End Sub
    
    'The SP_DataArrival sub was modified to update the Engine_Analysis string with the full output of the run, 
    'and the Debug output changed to just reflect when the best move was returned, and how much wall time, in seconds, had passed.
    
    Private Sub SP_DataArrival(ByVal CharsTotal As Long)
      Dim LastLine As String
      
        With SP
            Do While .HasLine
     '           Print #2, .GetLine()
       '         Debug.Print .GetLine()
                LastLine = .GetLine()
                Engine_Analysis = Engine_Analysis & LastLine & vbNewLine
            Loop
        End With
    
        If InStr(LastLine, "bestmove") <> 0 Then
          TimeToProcess = Timer - TimeToProcess
          Debug.Print LastLine, "took "; TimeToProcess; " seconds to compute"
        End If
    End Sub
    I'm not saying that any one of the three methods is preferable to the other, I don't have any vested interest in the task, but I certainly didn't see any reason that you couldn't capture the text in the Engine_Analysis string (although I do worry about a lot of string concatenation, but the elapsed time shows that it is apparently not an issue here. You ask the engine to take 15 seconds, and it takes about 15 seconds).

    So, while I ignored it for a couple of days, I decided to test setting Engine_Analysis for myself to verify there should be no issue. Since we didn't have the discerning parts of your code (where all and when you touch Engine_Analysis), I won't try to guess how your code went wrong.

    p.s. After the bestmove line was printed to the Immediate window the third time, I did Pause the program and typed

    ? TimeToProcess

    in the Immediate window to print the contents of the TimeToProcess string to verify the string was indeed being filled with all the output of the process.
    Last edited by passel; Oct 15th, 2019 at 07:16 AM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  20. #20
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Btw, here is Olaf's sample translated to cExec class w/ StartHidden:=True does not show engine's console.

    thinBasic Code:
    1. Option Explicit
    2.  
    3. Private oP As cExec
    4.  
    5. Private Sub Form_Load()
    6.     Set oP = New cExec
    7.     oP.Run App.Path & "\stockfish_10_x64.exe", vbNullString, StartHidden:=True
    8.     SendCommand "" 'initialize the engine
    9. End Sub
    10.  
    11. Private Sub Form_Click() 'the command-call-examples here, all behave synchronous
    12.     Caption = "Command-Sequence started..."
    13.  
    14.     SendCommand "uci", "uciok"
    15.     SendCommand "ucinewgame"
    16.     SendCommand "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    17.     SendCommand "go depth 30 movetime 15000", "bestmove" '<- this can take a while, until the call returns...
    18.    
    19.     Caption = "Command-Sequence finished!"
    20. End Sub
    21.  
    22. Private Function SendCommand(sCmd As String, Optional sWaitFor$ = "readyok") As String
    23.     Dim sLine       As String
    24.    
    25.     If Not oP.AtEndOfOutput And Len(sCmd) > 0 Then
    26.         oP.WriteInput sCmd & vbCrLf
    27.     End If
    28.     If Not oP.AtEndOfOutput And sWaitFor = "readyok" Then
    29.         oP.WriteInput "isready" & vbCrLf
    30.     End If
    31.     Do While Not oP.AtEndOfOutput And InStr(sLine, sWaitFor) = 0
    32.         sLine = oP.ReadLineOutput
    33.         SendCommand = SendCommand & sLine
    34.         If LenB(sCmd) <> 0 Then
    35.             ResponseLineCallback sCmd, sLine
    36.         End If
    37.     Loop
    38. End Function
    39.  
    40. Private Sub ResponseLineCallback(sCmd As String, sLine As String)
    41.     Debug.Print Split(sCmd, " ")(0), Split(sLine, vbCrLf)(0)
    42.     DoEvents
    43. End Sub
    cheers,
    </wqw>

  21. #21

    Thread Starter
    New Member
    Join Date
    Oct 2019
    Posts
    12

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    wqweto,

    Thanks a lot. That works perfectly.

  22. #22
    Frenzied Member wqweto's Avatar
    Join Date
    May 2011
    Posts
    1,577

    Re: Details on How to Use Named Pipe for Communicating with Chess Engine

    Btw you can use LimitFlags to instruct OS always to terminate stockfish child processes when you app exits (even if it crashes) like this

    thinBasic Code:
    1. Private Sub Form_Load()
    2. '-- note: uses CreateJobObject and SetInformationJobObject so that closing the last
    3. '--       job object handle terminates all associated processes (all child's
    4. '--       spawned sub-processes become part of the job too by default)
    5.     Const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE As Long = &H2000
    6.     oP.Run App.Path & "\stockfish_10_x64.exe", vbNullString, StartHidden:=True, _
    7.         LimitFlags:=JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
    8. End Sub
    cheers,
    </wqw>

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width