【Gomoku Actual Combat】Chapter 3 Algorithm Packaged into a Third-Party Interface
In the previous chapter, we implemented the game tree negative maximum alpha-beta pruning algorithm, that is, ai()
the function, and got four return values: x, y coordinates, search times, and whether we won.
Now we need to wrap this ai()
function in two more shells:
1. The first layer is the preprocessed shell. For the parameters passed in by the interface, we need to preprocess ai()
the data structure required by the function; we also need to check whether the input is correct, if the input chessboard is not a square, or the input chessboard value has other values besides 1, 0, -1, etc. Wait, wait, we're going to limit it.
2. The second layer is the Python Flask library, which opens our functions as interfaces. Including the cross-domain configuration of some requests and the processing of request request data are all done here.
Use Flask to open the interface
## Define interface input
The principle of interface input is to have as few parameters as possible and be universal. For example, the input of the chessboard takes the following form:
[
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,1,-1,0,0,0,0],
[0,0,0,0,-1,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
]
Just convert the two-dimensional chessboard into a two-dimensional array directly, so that it is convenient for everyone to customize their own front-end pages.
So in summary, our interface has four parameters:
ratio
: Attack coefficient of ai. : The depth of algorithm traversal. : The length of the side length of the chessboard. : The variable obtained from the form, a two-dimensional array, representing the state of the chessboard, represented by a comma-separated string. Split a string into a list of integers by using a function.
depth
length
board
split
## Open interface, cross-domain configuration, data analysis
app = Flask(__name__)
CORS(app, supports_credentials=True)
@app.route('/api/next_step', methods=['POST'])
@cross_origin(supports_credentials=True)
def api():
try:
ratio = int(request.form.get('ratio'))
depth = int(request.form.get('depth'))
length = int(request.form.get('length'))
board = [int(_) for _ in request.form.get('board').split(',')]
board = np.array(board).reshape(-1, length)
return next_step(board, length, depth, ratio ) # 返回接口响应
except Exception as e:
return {
'code': 300,
'msg': str(e)
}
if __name__ == '__main__':
app.run()
/api/next_step
The above code is an API interface based on the Flask framework , which creates a route that accepts POST requests and processes form data including ratio, depth, length, and board parameters. In the request, it first gets the values of these parameters from the form data and converts the board into a two-dimensional array. It then calls the next_step function to process the request and returns the result as the interface's response. If an exception occurs during processing, it returns a dictionary containing the error message. Finally, start the service by running the application.
If cross-domain is not added, we need to add some configurations to the access interface on the front-end side, which is troublesome.
For the two-dimensional array passed from the front end, it is actually converted into a string form, so after receiving it, we need to manually convert it into a two-dimensional array. Then it can be handed over to preprocessing for execution.
data preprocessing
## Data inspection and exception capture
For the value passed from the interface, we care about the following issues:
1. Is the side length of the chessboard too small
? 2. Whether the chessboard is not square.
3. The depth of traversal should be controlled within 1, 2, 3, 4, otherwise it will be too big, and the process will be blocked at once. If it is deployed on the server 4. Will
the chessboard value exceed 1, 0, or -1
? 5. Will the chessboard be full?
# 统计白子数
white_count = np.count_nonzero(board == 1)
# 统计黑子数
black_count = np.count_nonzero(board == -1)
# 统计空子数
empty_count = np.count_nonzero(board == 0)
"""输入检查"""
# TODO 修改最低大小
if length < 12:
raise ValueError(f"棋盘边长最低为12.")
if board.shape[1] != length:
raise ValueError(f"输入棋盘不是正方形.")
if depth not in [1,2,3,4]:
raise ValueError(f"输入的遍历深度有误, 应该为1,2,3或4.")
assert (white_count + black_count + empty_count) == board.size,\
"输入的棋盘数据有误.只能为0,1,-1.其中1代表白棋,-1代表黑棋,0代表空棋."
if empty_count == 0:
raise ValueError(f"输入的棋盘已满, 无法下棋.")
Count the number of white pieces:
Use np.count_nonzero()
the function to count the number of elements with a value of 1 in the chessboard, and assign the result to a variable white_count
.
Count the number of sunspots:
Use np.count_nonzero()
the function to count the number of elements with a value of -1 in the chessboard, and assign the result to a variable black_count
.
Count the number of holes:
Use np.count_nonzero()
the function to count the number of elements with a value of 0 in the chessboard, and assign the result to a variable empty_count
.
Input check:
- Check whether the side length of the chessboard is less than 12, and if so, throw ValueError
an exception, prompting that the minimum side length of the chessboard is 12.
- Check if the input board is a square, if not, throw ValueError
an exception, indicating that the input board is not a square.
- Check whether the traversal depth is 1, 2, 3 or 4, if not, throw ValueError
an exception, prompting that the entered traversal depth is wrong and should be 1, 2, 3 or 4.
- Use an assertion statement to check whether the total number of elements in the chessboard is equal to the size of the chessboard. If not, an AssertionError
exception will be thrown, prompting that the entered data of the chessboard is incorrect, which can only be 0, 1, -1, where 1 represents white chess, - 1 represents black chess, 0 represents empty chess.
- Check whether the number of holes in the chessboard is 0, and if so, throw ValueError
an exception, prompting that the input chessboard is full and chess cannot be played.
## Preprocess data
# 当前玩家
player = 'black' if black_count == white_count else 'white'
# 当前步数
step = black_count + white_count + 1
# 开始计时
start_time = time.time()
# 电脑计算
x, y, search_count, flag = ai(player, ratio, length, board, depth)
# 结束计时
end_time = time.time()
Determine the current player based on the current number of black and white pieces. And calculate the backtracking algorithm execution time.
## Define interface output
The defined output is as follows:
return {
'code': 200,
'msg': '成功',
'data': {
'x': x,
'y': y,
'time': end_time - start_time,
'step': step,
'player': player,
'length': length,
'search_count': search_count,
'flag': flag,
'info': "【默认黑子先行】"
"【黑子存-1,白子存1,空子存0】"
"【x:横坐标,从0开始,length结束】"
"【y:纵坐标,从0开始,length结束】"
"【time:搜索时间】"
"【step:这是第几步棋】"
"【player:当前下棋的玩家'black'/'white'】"
"【length:棋盘边长,线的个数,即格子的个数+1】"
"【search_count:搜索次数】"
"【flag:是否赢了】"
"【info:参数介绍】"
}
}
The above code returns a dictionary of results, containing the following key-value pairs:
- 'code'
:表示返回的状态码,这里设置为 200 表示成功。
- 'msg'
:表示返回的消息,这里设置为 ‘成功’。
- 'data'
:表示返回的数据,是一个包含多个键值对的字典。
- 'x'
:表示横坐标。
- 'y'
:表示纵坐标。
- 'time'
:表示搜索时间,是结束时间减去开始时间的差值。
- 'step'
:表示当前步数。
- 'player'
:表示当前下棋的玩家,可以是 ‘black’ 或 ‘white’。
- 'length'
:表示棋盘边长,即格子的个数加1。
- 'search_count'
:表示搜索次数。
- 'flag'
:表示是否赢了。
- 'info'
:表示参数介绍的字符串,包含一些说明信息。
这段代码用于构建一个包含返回结果的字典,并将其作为函数的返回值。返回结果中包含了搜索结果的相关信息。
开启接口
此时去运行编写的接口的py文件:
如上图,表示开启成功,然后去写post请求调用就可以了。