我正在尝试使用 Go 文本协议(GTP - http://www.lysator.liu.se/~gunnar/gtp/)与程序(GoGui - https://sourceforge.net/projects/gogui/)交互),其中有它的文档在这里。(链接也可以在以前的网站上找到。)
所以我写了一些代码来至少让 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('')
现在这本身似乎并不不合理,但导致 GoGui 给我以下错误:
这当然是个问题。所以我认为我在编程中的某个地方犯了一个错误,但是当我简单地通过 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)
您没有刷新响应,因此不会将任何内容发送回调用方(因为命令不够大,无法触发自动缓冲区刷新)。此外,在浏览协议文档时,它清楚地表明您的响应应该采用= 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()
您可能想要创建一个简单的函数来解析命令/响应而不是重复代码,tho。此外,这可能不会(完全)工作,因为协议文档指出您需要实现相当多的命令 ( 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
此外,我们将在下面处理命令需要返回错误的情况 - 您需要从函数中执行的所有操作是 toraise 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
当然,正如我之前提到的,将您的命令打包在一个单独的模块中可能是一个更好的主意,但这至少应该让您知道如何进行“关注点分离”,这样您就可以担心响应您的命令而不是关于他们如何被呼叫以及他们如何回应呼叫者。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句