作者:daoqiclub@126.com
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)