我尝试使用Go文本协议(GTP - https://sourceforge.net/projects/gogui/)与程序(GoGui - http://www.lysator.liu.se/~gunnar/gtp/)进行交互,该文档具有文档here。 (链接也可以在以前的网站上找到。)
所以我写了一些代码,至少让GoGui承认我的程序的存在:
import sys
import engine
import input_processing
for line in sys.stdin:
if line == 'name\n':
sys.stdout.write(' Muizz-Bot\n\n')
if line == 'protocol_version\n':
sys.stdout.write('2\n\n')
if line == 'version\n':
sys.stdout.write('')
这当然是一个问题。所以我认为我在编程中的某个地方犯了一个错误,但是当我只是通过visual studio运行程序时,一切都按预期工作:
这让我觉得问题在于连接两个应用程序,也许我应该看看除stdin和stdout之外的其他功能。有谁知道这里可能出了什么问题?
编辑评论:目前正在进行命令解析的代码(完整版)如下所示:
import sys
commands = ['protocol_version', 'name', 'version', 'list_commands', 'known_command', 'quit', 'boardsize',
'clear_board', 'komi', 'play', 'genmove']
pre_game_out = ['2','Muizz-Bot','']
# Define all output functions
def list_commands():
out = '\n'.join(commands)
return(out)
def known():
return(True)
def quit():
return(None)
def boardsize():
return(None)
def clear_board():
return(None)
def komi():
return(None)
def play():
return(None)
def genmove():
return("A1")
# Create dictionary to point to all functions.
output = {'list_commands':list_commands, 'known_command':known, 'quit':quit, 'boardsize':boardsize,
'clear_board':clear_board, 'komi':komi, 'play':play, 'genmove':genmove}
# Define the function that will pass the commands and write outputs.
def parse(line):
if line.strip() in commands:
i = commands.index(line.strip())
if i<3:
sys.stdout.write('= '+ pre_game_out[i]+'\n\n')
sys.stdout.flush()
else:
sys.stdout.write('= ' + output[line.strip()]() + '\n\n')
sys.stdout.flush()
对于预处理:
def input(inp):
# Remove control characters
inp = inp.replace('\r', '')
inp = inp.replace(' ', '')
inp = inp.split('#', 1)[0]
inp = inp.replace('\t', ' ')
# Check if empty
if inp.isspace() or inp==None or inp=='':
return
else:
return(inp)
答案 0 :(得分:1)
你没有刷新你的响应,所以没有任何东西被发送回调用者(因为命令不足以触发自动缓冲区刷新)。此外,漫步协议文件清楚地表明你的回复应该是= response\n\n
的形式,所以即使你正在冲洗它可能仍然无法正常工作。
尝试使用以下内容:
import sys
for line in sys.stdin:
if line.strip() == 'name':
sys.stdout.write('= Muizz-Bot\n\n')
sys.stdout.flush()
elif line.strip() == 'protocol_version':
sys.stdout.write('= 2\n\n')
sys.stdout.flush()
elif line.strip() == 'version':
sys.stdout.write('=\n\n')
sys.stdout.flush()
你可能想要创建一个简单的函数来解析命令/响应而不是重复代码。此外,这可能不会(完全)工作,因为协议文档声明您需要实现相当多的命令(6.1 Required Commands
),但它应该让您开始。
更新 - 这是使其更易于管理且符合规范的一种方法 - 您可以为每个命令创建一个功能,以便您可以根据需要轻松添加/删除它们,例如:
def cmd_name(*args):
return "Muizz-Bot"
def cmd_protocol_version(*args):
return 2
def cmd_version(*args):
return ""
def cmd_list_commands(*args):
return " ".join(x[4:] for x in globals() if x[:4] == "cmd_")
def cmd_known_command(*args):
commands = {x[4:] for x in globals() if x[:4] == "cmd_"}
return "true" if args and args[0] in commands else "false"
# etc.
此处所有命令函数都以“cmd_”为前缀(cmd_list_commands()
和cmd_known_command()
使用该事实检查全局命名空间中的命令函数)但您也可以将它们移动到另一个模块,然后“扫描”模块。使用这样的结构,添加新命令非常容易,例如添加所需的quit
命令,只需要定义它:
def cmd_quit(*args):
raise EOFError() # we'll use EOFError to denote an exit state bellow
此外,当命令需要返回错误时,我们将处理以下情况 - 您需要执行的所有功能都是raise ValueError("error response")
,并且它将作为错误发回。
一旦你将一组命令添加为函数,你只需要解析输入命令,用正确的参数调用正确的函数并打印回复:
def call_command(command):
command = "".join(x for x in command if 31 < ord(x) < 127 or x == "\t") # 3.1.1
command = command.strip() # 3.1.4
if not command: # ... return if there's nothing to do
return
command = command.split() # split to get the [id], cmd, [arg1, arg2, ...] structure
try: # try to convert to int the first slice to check for command ID
command_id = int(command[0])
command_args = command[2:] if len(command) > 2 else [] # args or an empty list
command = command[1] # command name
except ValueError: # failed, no command ID present
command_id = "" # set it to blank
command_args = command[1:] if len(command) > 1 else [] # args or an empty list
command = command[0] # command name
# now, lets try to call our command as cmd_<command name> function and get its response
try:
response = globals()["cmd_" + command](*command_args)
if response != "": # response not empty, prepend it with space as per 3.4
response = " {}".format(response)
sys.stdout.write("={}{}\n\n".format(command_id, response))
except KeyError: # unknown command, return standard error as per 3.6
sys.stdout.write("?{} unknown command\n\n".format(command_id))
except ValueError as e: # the called function raised a ValueError
sys.stdout.write("?{} {}\n\n".format(command_id, e))
except EOFError: # a special case when we need to quit
sys.stdout.write("={}\n\n".format(command_id))
sys.stdout.flush()
sys.exit(0)
sys.stdout.flush() # flush the STDOUT
最后,您只需要收听STDIN并将命令行转发到此功能即可完成繁重的工作。在这方面,我实际上明确地从你的STDIN逐行读取,而不是试图迭代它,因为它是一种更安全的方法,所以:
if __name__ == "__main__": # make sure we're executing instead of importing this script
while True: # main loop
try:
line = sys.stdin.readline() # read a line from STDIN
if not line: # reached the end of STDIN
break # exit the main loop
call_command(line) # call our command
except Exception: # too broad, but we don't care at this point as we're exiting
break # exit the main loop
当然,正如我之前提到的,将命令打包在一个单独的模块中可能更好一点,但这至少应该让您知道如何“分离关注点”,这样您就会担心回复命令而不是它们如何被调用以及它们如何响应调用者。