作者:happyfly
2022.03.01
道棋棋盘可以视为一个16x16的矩阵,矩阵的元素有三种状态:EMPTY, BLACK, WHITE, 分别代表空点、黑子和白子。
先初始化一个矩阵:
import numpy as np EMPTY = 0 BLACK = 1 WHITE = -1 SIZE = 16 board = np.zeros((SIZE,SIZE)) print(board)
[[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. 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.]
[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. 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.]
[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. 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.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
由于道棋左右上下联通的特性,矩阵平移、旋转、翻转都不影响棋盘的逻辑。下面我们就用程序实现这些变换:
#为了实现平移,先定义一个函数boardshift,它有三个参数:要操作的矩阵、水平平移步数、垂直平移步数 def boardshift(matrix, v_shift, h_shift): h, w = matrix.shape matrix=np.vstack((matrix[(h - v_shift % h):,:],matrix[:(h - v_shift % h),:])) matrix=np.hstack((matrix[:,(w - h_shift % w):],matrix[:,:(w - h_shift % w)])) return matrix board = boardshift(board, 2, -10) #向下移动两步,向左移动10步 #矩阵旋转比较简单,使用numpy.rot90方法即可,其中参数k是逆时针旋转90度的次数 board = np.rot90(board, k = 2) #矩阵翻转: board = board.T #对角线为轴的翻转,相当于行列变换 board = np.flip(board, axis = 0) #上下翻转 board = np.flip(board, axis = 1) #左右翻转
着子可以通过改变矩阵元素的值来实现:
#黑棋在第十三行第四列着子 board[12,3] = BLACK print(board)
[[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. 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.]
[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. 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.]
[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. 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. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
通过矩阵的操作,可以让道棋棋盘在前两手棋实现棋盘标准化:
1、既然第一手黑棋走在任意点都是等价的,那么就定义第一手黑棋的坐标为0,0
2、第二手白棋理论上只有44种可能性,那么就把第二手白棋固定在某个特定的范围内(下面矩阵下划线的位置)
#黑棋在第十三行第四列着子 board[12, 3] = BLACK board = boardshift(board, -12, -3) #把第一个黑子移动到0,0位置。 print(board)
[[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. 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. 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. 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. 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. 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. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
现在白棋着子:
board[9, 13] = WHITE print(board)
[[ 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. 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. 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. 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. -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. 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. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
白棋横坐标和纵坐标都大于8,说明黑棋距离白棋近的方向是在另一边,所以需要先水平和垂直平移-1步,把第一手黑棋挪到右边和下边(右下角),使黑棋和白棋距离上最近,然后再逆时针旋转180度(等效于上下翻转加上左右翻转):
board = boardshift(board, -1, -1) board = np.rot90(board, k = 2) print(board)
[[ 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. 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. 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. -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. 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. 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. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
此时白棋恰好落在前面矩阵中下划线的范围内,如果白棋的横坐标大于纵坐标,则还需要用T方法做一次以对角线为轴的翻转。
棋盘的坐标按此规则固定后,道棋不同开局就能够进行比较了。这也可能有利于深度学习。
下面是完整的测试程序:
#-*-coding:utf-8-*- import numpy as np import random EMPTY = 0 BLACK = 1 WHITE = -1 SIZE = 16 board = np.zeros((SIZE,SIZE)) def boardshift(matrix, v_shift, h_shift): h, w = matrix.shape matrix=np.vstack((matrix[(h - v_shift % h):,:],matrix[:(h - v_shift % h),:])) matrix=np.hstack((matrix[:,(w - h_shift % w):],matrix[:,:(w - h_shift % w)])) return matrix # First Black Move x = int(input('Please input x for the first move (0-' + str(SIZE-1) + '): ')) y = int(input('Please input y for the first move (0-' + str(SIZE-1) + '): ')) board[y, x] = BLACK print('Black move: \n', board) board = boardshift(board, -y, -x) print('First Black stone shifted: \n', board) # Second White Move rand = input('Randomly generated coordinates for the second move (N/y): ') if rand == 'y': y = random.randint(1, SIZE/2) x = random.randint(1, y) else: x = int(input('Please input x for the second move (0-' + str(SIZE-1) + '): ')) y = int(input('Please input y for the second move (0-' + str(SIZE-1) + '): ')) board[y, x] = WHITE print('White move: \n', board) if x > SIZE/2: board = boardshift(board, 0, -1) board = np.flip(board, axis = 1) x = SIZE - x if y > SIZE/2: board = boardshift(board, -1, 0) board = np.flip(board, axis = 0) y = SIZE - y if x > y: board = board.T print('Standardized board coordinates: \n', board)
上面实现的这些变换只是计算机背后的逻辑,对弈者不需要知道程序中发生了什么。
大家也可以看到,道棋没有边角的特点,使编程更加容易。利用boardshift函数,计算机前端很容易实现窗口平移。在图形界面中画棋盘也变得更加灵活,不必象围棋一样先画一个有边缘的棋盘,道棋只需要在相应的坐标位置画上“┼”或“●”或“○”就可以了。
#字符界面下打印棋盘 for line in board: print_line = '' for point in line: if point == 1: print_line += '●' elif point == -1: print_line += '○' else: print_line += '┼' print(print_line)