addy
addy

Reputation: 385

Communicating with console apps

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())
    }

Upvotes: 4

Views: 303

Answers (1)

pauljz
pauljz

Reputation: 10901

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.

Upvotes: 2

Related Questions