4

I am a beginner. I have tried to communicate to a chess engine with go's exec package, but it requires me to close the stdin. What I wish to do is to establish a dialogue with the engine.

How do I do that with go?

This is the python implementation of the communication which is pretty much straight forward, can be found at How to Communicate with a Chess engine in Python?

    import subprocess, time

    engine = subprocess.Popen(
    'stockfish-x64.exe',
    universal_newlines=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    )

    def put(command):
    print('\nyou:\n\t'+command)
    engine.stdin.write(command+'\n')

    def get():
    # using the 'isready' command (engine has to answer 'readyok')
    # to indicate current last line of stdout
    engine.stdin.write('isready\n')
    print('\nengine:')
    while True:
        text = engine.stdout.readline().strip()
        if text == 'readyok':
            break
        if text !='':
            print('\t'+text)

    get()
    put('uci')
    get()

put('setoption name Hash value 128')
get()
put('ucinewgame')
get()
put('position startpos moves e2e4 e7e5 f2f4')
get()
put('go infinite')
time.sleep(3)
get()
put('stop')
get()
put('quit')

For simplicity consider this in go:

package main

import ( 
    "bytes"
    "fmt"
    "io"
    "os/exec"
)

func main() {
    cmd := exec.Command("stockfish")
    stdin, _ := cmd.StdinPipe()
    io.Copy(stdin, bytes.NewBufferString("isready\n"))
    var out bytes.Buffer
    cmd.Stdout = &out
    cmd.Run()
    fmt.Printf(out.String())
}

The program waits without printing anything. But when I close the stdin the program prints the result, but closing the stdin hinders communication between the engine and go program.

The solution:

package main

    import ( 
        "bytes"
        "fmt"
        "io"
        "os/exec"
        "time"
    )

    func main() {
        cmd := exec.Command("stockfish")
        stdin, _ := cmd.StdinPipe()
        io.Copy(stdin, bytes.NewBufferString("isready\n"))
        var out bytes.Buffer
        cmd.Stdout = &out
        cmd.Start()
        time.Sleep(1000 * time.Millisecond)
        fmt.Printf(out.String())
    }
Community
  • 1
  • 1
addy
  • 387
  • 1
  • 18

1 Answers1

2

You should still be able to do this with exec.Command and then with the Cmd methods cmd.StdinPipe(), cmd.StdoutPipe(), and cmd.Start()

The example in the docs for exec.Cmd.StdoutPipe should be able to get you started: http://golang.org/pkg/os/exec/#Cmd.StdoutPipe

But in your case, you'd be doing reads and writes from the pipes in a loop. I'd imagine your architecture will look like this loop in a goroutine, passing commands to-and-fro the rest of your code via channels.

pauljz
  • 10,803
  • 4
  • 28
  • 33
  • yes I will be able to invoke an external app using exec but it requires closing the stdin which blocks the communication. – addy Feb 17 '14 at 18:08
  • Don't use `cmd.Run()`, use `cmd.Start()`. `Run` blocks and waits for the command to complete. `Start` lets you interact with the stdin/stdout while the program continues to run. – pauljz Feb 17 '14 at 20:12