使用io.WriteString时,可执行文件会提前退出。

huangapple go评论72阅读模式
英文:

executable exits early when using io.WriteString

问题

我正在使用io包来处理在我的PATH中定义的可执行文件。
这个可执行文件叫做"Stockfish"(国际象棋引擎),可以通过命令行工具使用。

为了让引擎搜索最佳着法,你可以使用"go depth n"命令,其中n是搜索的深度,深度越大,搜索时间越长。
使用我的命令行工具,它使用深度为20进行搜索,大约需要5秒钟,输出如下:

info string NNUE evaluation using nn-3475407dc199.nnue enabled
info depth 1 seldepth 1 multipv 1 score cp -161 nodes 26 nps 3714 tbhits 0 time 7 pv e7e6
info depth 2 seldepth 2 multipv 1 score cp -161 nodes 51 nps 6375 tbhits 0 time 8 pv e7e6 f1d3
info depth 3 seldepth 3 multipv 1 score cp -161 nodes 79 nps 7900 tbhits 0 time 10 pv e7e6 f1d3 g8f6
info depth 4 seldepth 4 multipv 1 score cp -161 nodes 113 nps 9416 tbhits 0 time 12 pv e7e6 f1d3 g8f6 b1c3
[...]
bestmove e7e6 ponder h2h4

现在,使用io.WriteString函数,它在毫秒级别完成,没有任何(可见的)计算结果:
(这也是下面代码的输出)

info string NNUE evaluation using nn-3475407dc199.nnue enabled
bestmove b6b5```

以下是我使用的代码:

```go
func useStockfish(commands []string) string {
	cmd := exec.Command("stockfish")
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}
	for _, cmd := range commands {
		writeString(cmd, stdin)
	}
	err = stdin.Close()
	if err != nil {
		log.Fatal(err)
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatal(err)
	}
	return string(out)
}

func writeString(cmd string, stdin io.WriteCloser) {
	_, err := io.WriteString(stdin, cmd)
	if err != nil {
		log.Fatal(err)
	}
}

这是我如何使用它的示例。第一个命令是设置棋局的初始位置,第二个命令是计算深度为20的下一步最佳着法。结果如上所示。

func FetchComputerMove(game *internal.Game) {
	useStockfish([]string{"position exmaplepos\n", "go depth 20"})
}
英文:

I'm using the io package to work with an executable defined in my PATH.
The executable is called "Stockfish" (Chess Engine) and obviously usable via command line tools.

In order to let the engine search for the best move, you use "go depth n" - the higher the depth - the longer it takes to search.
Using my command line tool it searches for about 5 seconds using a depth of 20, and it looks like this:

<code>go depth 20
info string NNUE evaluation using nn-3475407dc199.nnue enabled
info depth 1 seldepth 1 multipv 1 score cp -161 nodes 26 nps 3714 tbhits 0 time 7 pv e7e6
info depth 2 seldepth 2 multipv 1 score cp -161 nodes 51 nps 6375 tbhits 0 time 8 pv e7e6 f1d3
info depth 3 seldepth 3 multipv 1 score cp -161 nodes 79 nps 7900 tbhits 0 time 10 pv e7e6 f1d3 g8f6
info depth 4 seldepth 4 multipv 1 score cp -161 nodes 113 nps 9416 tbhits 0 time 12 pv e7e6 f1d3 g8f6 b1c3
[...]
bestmove e7e6 ponder h2h4
</code>

Now, using io.WriteString it finishes after milliseconds without any (visible) calculation:
(That's also the output of the code below)

<code>Stockfish 14 by the Stockfish developers (see AUTHORS file)
info string NNUE evaluation using nn-3475407dc199.nnue enabled
bestmove b6b5</code>

Here's the code I use:

func useStockfish(commands []string) string {
	cmd := exec.Command(&quot;stockfish&quot;)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}
	for _, cmd := range commands {
		writeString(cmd, stdin)
	}
	err = stdin.Close()
	if err != nil {
		log.Fatal(err)
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatal(err)
	}
	return string(out)
}

func writeString(cmd string, stdin io.WriteCloser) {
	_, err := io.WriteString(stdin, cmd)
	if err != nil {
		log.Fatal(err)
	}

And this is an example of how I use it. The first command is setting the position, the second one is calculation the next best move with a depth of 20. The result is showed above.

func FetchComputerMove(game *internal.Game) {
	useStockfish([]string{&quot;position exmaplepos\n&quot;, &quot;go depth 20&quot;})
}

答案1

得分: 2

要利用像stockfish这样的引擎,你需要启动进程并保持其运行。

你正在执行它,通过Stdin管道传递2个命令,然后关闭管道。关闭管道表示你对引擎的输出不再感兴趣。

要运行它并保持其运行,你需要像这样的代码:

func startEngine(enginePath string) (stdin io.WriteCloser, stdout io.ReadCloser, err error) {
    cmd := exec.Command(enginePath)

    stdin, err = cmd.StdinPipe()
    if err != nil {
        return
    }
    stdout, err = cmd.StdoutPipe()
    if err != nil {
        return
    }

    err = cmd.Start() // 启动命令,但不等待其完成
    return
}

返回的管道允许你发送命令并实时查看输出:

stdin, stdout, err := startEngine("/usr/local/bin/stockfish")

sendCmd := func(cmd string) error {
    _, err := stdin.Write([]byte(cmd + "\n"))
    return err
}

sendCmd("position examplepos")
sendCmd("go depth 20")

然后,粗略地读取异步响应:

b := make([]byte, 10240)

for {
    n, err := stdout.Read(b)
    if err != nil {
        log.Fatalf("read error: %v", err)
    }
    log.Println(string(b[:n]))
}

一旦出现类似bestmove d2d4 ponder g8f6的行,就表示当前的分析命令已完成。

然后,你可以关闭引擎(通过关闭stdin管道),如果只需要这样的话,或者保持它打开以进行进一步的命令提交。

英文:

To leverage engines like stockfish - you need to start the process and keep it running.

You are executing it, passing 2 commands via a Stdin pipe, then closing the pipe. Closing the pipe indicates to the program that you are no longer interested in what the engine has to say.

To run it - and keep it running - you need something like:

func startEngine(enginePath string) (stdin io.WriteCloser, stdout io.ReadCloser, err error) {
	cmd := exec.Command(enginePath )

	stdin, err = cmd.StdinPipe()
	if err != nil {
		return
	}
	stdout, err = cmd.StdoutPipe()
	if err != nil {
		return
	}

	err = cmd.Start() // start command - but don&#39;t wait for it to complete
	return
}

The returned pipes allow you to send commands & see the output live:

stdin, stdout, err := startEngine(&quot;/usr/local/bin/stockfish&quot;)


sendCmd := func(cmd string) error {
	_, err := stdin.Write([]byte(cmd + &quot;\n&quot;))
	return err
}

sendCmd(&quot;position examplepos&quot;)
sendCmd(&quot;go depth 20&quot;)

then to crudely read the asynchronous response:

b := make([]byte, 10240)

for {
	n, err := stdout.Read(b)
	if err != nil {
		log.Fatalf(&quot;read error: %v&quot;, err)
	}
	log.Println(string(b[:n]))
}

once a line like bestmove d2d4 ponder g8f6 appears, you know the current analysis command has completed.

You can then either close the engine (by closing the stdin pipe) if that's all you need, or keep it open for further command submissions.

huangapple
  • 本文由 发表于 2021年10月22日 23:41:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/69679652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定