我有以下情况。我有一个游戏和两个机器人,他们互相玩这个游戏。所有的通信都是通过stdin和stdout完成的。现在我想写一个引擎,让机器人互相对抗并决定哪个机器人赢了比赛。要做到这一点,我需要从我的引擎运行机器人并通过stdin和stdout与它们进行通信。
因此,为了启动机器人,我已经完成了以下工作:
from subprocess import Popen, PIPE, STDOUT
bot = Popen(['/path/to/bot1'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
现在我想将游戏状态传输到机器人并请求从我的机器人移动。首先,我想在此过程中编写一些文本行,然后我的引擎应该等待一段时间并从过程中读取结果。请注意,僵尸程序始终在运行,并且不会自行终止。
如何实现这一目标?
答案 0 :(得分:3)
不幸的是,它可能不如从bot.stdout
阅读并写入bot.stdin
那么容易。原因是,如果程序执行完毕,某些程序不会刷新到STDOUT
,如果它是子进程。解决这个问题的方法是在伪终端下生成它。以下是使用pty
模块的示例:
<强> bot1.py 强>
#!/usr/bin/env python
name = raw_input("What is your name?\n")
print "Hello {}, my name is Bot1".format(name)
raw_input() # Bot2: Hello Bot1
<强> bot2.py 强>
#!/usr/bin/env python
raw_input() # Bot1: What is your name?
print "Bot2"
greeting = raw_input() # Bot1: Hello Bot2, my name is Bot1
print "Hello {}".format(greeting.split()[-1])
<强> run.py 强>
#!/usr/bin/env python
import os
import pty
import termios
class Bot:
def __init__(self, name, program):
self.name = name
# Fork a process in a pseudo-terminal
self._pid, self._fd = pty.fork()
if self._pid == 0:
# Child process, replace with program
os.execvp(program, [program])
# Turn off echoing for child file descriptor, otherwise it will spew the
# child's output to the parent's STDOUT.
attribs = termios.tcgetattr(self._fd)
attribs[3] = attribs[3] & ~termios.ECHO
termios.tcsetattr(self._fd, termios.TCSANOW, attribs)
def speak(self, bot):
output = self.read()
bot.write(output)
print '{} -> {}: {}'.format(self.name, bot.name, output)
def read(self):
return os.read(self._fd, 1024).strip()
def write(self, line):
os.write(self._fd, line + "\n")
if __name__ == '__main__':
bot1 = Bot('Bot1', './bot1.py')
bot2 = Bot('Bot2', './bot2.py')
# Bot1 -> Bot2: What is your name?
bot1.speak(bot2)
# Bot2 -> Bot1: Bot2
bot2.speak(bot1)
# Bot1 -> Bot2: Hello Bot2, my name is Bot1
bot1.speak(bot2)
# Bot2 -> Bot1: Hello Bot1
bot2.speak(bot1)
<强>输出强>
$ ./run.py
Bot1 -> Bot2: What is your name?
Bot2 -> Bot1: Bot2
Bot1 -> Bot2: Hello Bot2, my name is Bot1
Bot2 -> Bot1: Hello Bot1
这是一个简单的例子,但希望它足以让你入门。
答案 1 :(得分:2)
由于您使用PIPE
,因此Popen
对象bot
包含{stdin
,stdout
和stderr
}三个属性{{ 3}}
因此,您可以使用bot.stdout.read()
和bot.stdin.write()
。
正如Aldehir所提到的,你可能必须flush
你写的流,以便bot
实际收到一些东西。
文档建议使用communicate
(在这种情况下完全不适合)的原因是,如果子进程或者您没有快速/经常读取数据,OS管道缓冲区可能会填满,导致陷入僵局。
除非你高速发送大量数据,否则这不会是一个问题。