Python实现黑白棋游戏策略与胜负判定详解:电脑落子与游戏结束条件分析
Python 实现黑白棋

该问题需要计算机策略
对于每个可能的放置位置,尝试计算该位置的“分数”(可以翻转的对手棋子的数量)。分数越高,将该棋子放置在该位置越有利。计算每个可能位置的分数并选择得分最高的位置。可能有 2 个或更多方格得分相同。在这种情况下,选择行字母最小的棋盘。如果两个棋子得分相同并且位于同一行,则选择列字母较小的棋子。
游戏结束条件
1)整个棋盘已满;
2)一方的棋子已被另一方吃掉;
3) 双方棋手都没有可以放置棋子的棋盘方格;
4)一方处于非法职务。
前三种情况按棋子数量计算胜负,棋子多的一方获胜;第四种情况,判定对方获胜。
解决问题思路的关键逻辑
1)无论是电脑下棋还是人下棋,首先要判断是否有可以掉的字。
2)人类在下棋之前,必须确定这一步棋是否合法。
3)判断走棋是否合法时,要注意棋盘的边界
因为python列表的下标为-1,所以必须注意下标的下界。另一方面,因为黑白棋需要考虑最后的棋子是否同色,所以必须注意当前搜索的棋子和下一个方向相同的棋子。该棋子(可能具有相同颜色)是否出界。
例如:4*4的棋盘上的“黑与白”,如果不考虑下一个棋子的出界情况,就会出现从黑开始,回去找黑的错误,从而翻转三个白色棋子。
4)其实翻转、检查走法是否合法、计算走法分数都用的是同一套循环逻辑,所以写一个就行了,基本是复制粘贴。
5)使用多重排序来比较不同权重
6)比赛结束时的几种情况要具体情况具体讨论。
关键函数Init_board:
读入棋盘大小n(n为偶数,且4≤n≤26),并按要求初始化棋盘。程序使用以下字符来表示每个棋盘的状态:
。 – 未被占据 X – 被黑色占据 O – 被白色占据
def Init_board():
global board
board = [[0] * l for row in range(l)] #用列表生成式生成棋盘
board[int(l / 2 - 1)][int(l / 2 - 1)] = -1
board[int(l / 2 - 1)][int(l / 2)] = 1
board[int(l / 2)][int(l / 2 - 1)] = 1
board[int(l / 2)][int(l / 2)] = -1 #四个初始棋子的摆放
return board
#在这个处理中我们用0代表未落子,1代表黑棋,-1代表白棋
打印板:
输出棋盘。例如:4×4棋盘的初始状态如下:
abcd
一个。 。 。 。
盒子。
c.XO。
d. 。 。 。
def printBoard():
l = len(board)
print(' ', end = ' ')
for i in range(l):
print(chr(ord('a') + i), end = ' ')
print()
for i in range(l):
print(chr(ord('a') + i), end = ' ')
for j in range(l):
if (board[i][j] == 0):
print('.', end = ' ')
elif (board[i][j] == 1):
print('X', end = ' ')
else:
print('O', end = ' ')
print()
计算机_移动:
计算机下棋。棋子的位置以“行行字母”的格式表示。例如:“ba”表示棋子落在b行a列。
def computer_move(row, col, color): #电脑行棋和棋子翻转
global board
if (check != 0): #check中存储的是该棋子的价值(翻转的棋子数),不为0则合法
board[row][col] = color #向规定地点放上棋子
direction = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
for i in range(8):
z = 1 #对8个方向进行搜索
while (0<= row + (z+1) * direction[i][0] < l) and (0<= col + (z+1) * direction[i][1] < l) and (
board[row + z * direction[i][0]][col + z * direction[i][1]] == -color):#搜索进行的条件,搜索的下一个位置没有超过系统边界(因为尽头是一个相同的棋子所以要多查找一步),当前位置的棋子是一个异色的棋子。
z += 1 #这里涉及到一个问题就是python列表值是存在-1值的,所以要规定列表搜索的下界只能到0,不然本质上会查找到列表最后一个(下标为-1)的值
if board[row + z * direction[i][0]][col + z * direction[i][1]] == color: #如果查找到了尽头的那个同色棋子
for j in range(z):
board[row + j * direction[i][0]][col + j * direction[i][1]] = color #那就把中间的棋子全部置为同色并退出这次搜索
break
人类移动:
用户下棋
def human_move(row, col, color): #人类行棋和棋子翻转,与电脑行棋类似
global board
if (check != 0):
board[row][col] = color
direction = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
for i in range(8):
z = 1
while (0<=row + (z+1) * direction[i][0] < l) and (0 <= col + (z+1) * direction[i][1] < l) and (
board[row + z * direction[i][0]][col + z * direction[i][1]] == -color):
z += 1
if board[row + z * direction[i][0]][col + z * direction[i][1]] == color:
for j in range(z):
board[row + j * direction[i][0]][col + j * direction[i][1]] = color
break
检查板:
检查游戏是否结束
def check_board(): #检测本局游戏是否结束
a1 = 1 #用来标记棋盘是否下满
a21 = 1 #检测棋盘中是否还有黑棋
a22 = 1 #检测棋盘中是否还有白棋
a4 = 0 #检测当前落子位置是否合法
for i in range(l):
for j in range(l): #遍历整个棋盘
if (board[i][j] == 0): #如果存在空位
a1 = 0
elif (board[i][j] == 1): #如果存在黑棋
a21 = 0
else: #如果存在白棋
a22 = 0
if not (check): # check为0则不存在翻转,落子合法
a4 = 1
if (a1 or (a21 and a22) or (not (xp or op)) or a4):
if (a1 or (a21 and a22) or (not (xp or op))): # xp和op代表了双方是否存在落子位置
if (not (xp or op)): #如双双置0,则证明双方都无落子位置
print("Both player have no vaild move!")
gameover('score') #这些都是积分计算胜负关系
else: #如果a4出现了,则落子非法,计算胜负关系
gameover('illegal')
检查合法移动:
检查有颜色的棋子落在棋盘上是否合法(行,列)
def check_legal_move(row, col, color): #检测落子位置是否合法
direction = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
score = 0
for i in range(8): #思路与落子的思路类似,但不同的是这里当搜索到最后的同色棋子时,进行加和操作而非翻面操作
zscore = 1
while (0 <= (row + (zscore+1) * direction[i][0]) < l) and (0 <= (col + (zscore+1) * direction[i][1]) < l) and (
(board[row + zscore * direction[i][0]][col + zscore * direction[i][1]]) == -color):
if (board[row + (zscore + 1) * direction[i][0]][col + (zscore + 1) * direction[i][1]] == color):
score += zscore
break
zscore += 1
return score
游戏结束:
游戏结束,统计分数,输出结果。
def gameover(s): #游戏结束判定
global s1
global condition
global black
global white
condition = 0 #将游戏标记置为 0,游戏停止
if s == 'score': #如果是分数过程,则计算黑白棋的个数
print('Game Over!')
black = 0
white = 0
for i in range(l):
for j in range(l):
if board[i][j] == 1:
black += 1
if board[i][j] == -1:
white += 1
if (black > white):
print('X : O =', black, ':', white)
print('X player wins!')
elif (black < white):
print('O player wins!')
print('X : O =', black, ':', white)
else:
print('Draw!')
s1 = str(black) + 'to' + str(white) # s1 中存储的是要输进文件去的文本
return s1
if s == 'illegal':
print('Invaild move!')
s1 = 'Invaild move!'
if s == 'gg':
print('Human gave up!')
s1 = 'Human gave up!'
print('Game Over!')
if (color == 1): #这里是非法输入和gg投子的输出设置,color在主程序中更改
print('O player wins.')
else:
print('X player wins.')
return s1
保存信息:
将每个人机游戏的信息一行写入文件 reversi.csv 中。这些信息包括:游戏开始时间、单局所用时间、棋盘大小、黑方、白方、游戏得分、信息等。使用逗号字符分隔它们。
def saveinfo(): #文件存储
time3 = time.time() #游戏结束时间
long = time3-time2 #游戏持续时长
f = open('Reversi.csv', 'a+')
s = str(time1) + ',' + str(long) + ',' + str(l) + '*' + str(l) + ',' + Xplayer + ',' + Oplayer + ',' + s1
f.write(s) #向文件中输入
f.write('\n')
f.close()
主要的
import datetime
import time
time1 = datetime.datetime.now().strftime('%Y-%m-%d %T') #保存游戏时间
time2 = time.time() #游戏开始时间
l = int(input('Enter the board dimension:')) # l 中保存了棋盘的长度
board = Init_board() #初始化棋盘
x1 = input('Computer plays (X/O):') # x1中保存电脑的棋子颜色
if (x1 == 'X'): # 电脑执黑
hcolor = -1
Xplayer = 'computer'
Oplayer = 'human' #这个是要输进文件里的东西
condition = 1 #游戏标记置1,游戏进行
printBoard() #打印 、
while condition: #如果游戏标志为1(没进gameover),则可以继续运行
legal = []
for i in range(l):
for j in range(l):
if (board[i][j] == 0):
if (check_legal_move(i, j, 1) != 0):
legal.append([i, j, check_legal_move(i, j, 1)]) #将所有可下位置存进legal列表中
legal.sort(key=lambda x: x[1], reverse=False)
legal.sort(key=lambda x: x[0], reverse=False)
legal.sort(key=lambda x: x[2], reverse=True) #对列表进行先值,再行,再列的排序
if (legal != []): #如果存在合法输入
row = legal[0][0]
col = legal[0][1]
color = 1
check = check_legal_move(row, col, color)
xp = 1 #证明X仍可下
op = 1 #证明O仍可下
computer_move(row, col, 1) #将分值存进check里,进行落子
print('Computer places X at', chr(ord('a') + row), chr(ord('a') + col), '.')
else:
xp = 0 #如果不存在落子位置
print('X player has no vaild move.') #不进行落子
printBoard() #进行棋盘打印
check_board() #进行检测棋局是否结束
if (condition != 0): #如果棋局没有结束的话进行人类行棋
legal = []
for i in range(l):
for j in range(l):
if board[i][j] == 0:
if (check_legal_move(i, j, -1) != 0) and (board[i][j] == 0):
legal.append([i, j, check_legal_move(i, j, -1)]) #检测是否有行棋位置
if (legal != []):
inter = input('Enter move for O (Rowcol):')
if inter == 'gg': #贴心的投降设置
gameover('gg')
break
row = ord(inter[0]) - 97 #把输入的字母通过ASCII码变成输出
col = ord(inter[1]) - 97
color = -1
if (row>=l) or (col>=l) or (row<0) or (col <0): #如果输入超过边界,则为非法输入
gameover('illegal')
break
check = check_legal_move(row, col, color) #检测落子是否存在翻转
xp = 1
op = 1
human_move(ord(inter[0]) - 97, ord(inter[1]) - 97, -1) #人类落子
check_board() #检测当前棋盘是否满足结束条件
if check:
printBoard() #如果通过,则进行打印
else:
op = 0 #白棋无棋可下
print("O player has no vaild move.")
saveinfo() #游戏结束之后进行游戏记录的保存
else: #之后为电脑执白棋的情况,与上文黑棋的情况类似
hcolor = 1
Oplayer = 'computer'
Xplayer = 'human'
condition = 1
printBoard()
while condition:
legal = []
for i in range(l):
for j in range(l):
if (board[i][j] == 0):
if (check_legal_move(i, j, 1) != 0):
legal.append([i, j, check_legal_move(i, j, 1)])
if (legal != []):
inter = input('Enter move for X (Rowcol):')
if inter == 'gg':
gameover('gg')
break
row = ord(inter[0]) - 97
col = ord(inter[1]) - 97
color = 1
if (row>=l) or (col>=l) or (row<0) or (col <0):
gameover('illegal')
break
check = check_legal_move(row, col, color)
xp = 1
op = 1
human_move(ord(inter[0]) - 97, ord(inter[1]) - 97, 1)
check_board()
if check:
printBoard()
else:
op = 0
print("O player has no vaild move.")
if condition:
legal = []
for i in range(l):
for j in range(l):
if (board[i][j] == 0):
if (check_legal_move(i, j, -1) != 0):
legal.append([i, j, check_legal_move(i, j, -1)])
legal.sort(key=lambda x: x[1], reverse=False)
legal.sort(key=lambda x: x[0], reverse=False)
legal.sort(key=lambda x: x[2], reverse=True)
if (legal != []):
xp = 1
op = 1
row = legal[0][0]
col = legal[0][1]
color = -1
check = check_legal_move(row, col, color)
computer_move(row, col, -1)
else:
xp = 0
print("X player has no vaild move.")
check_board()
printBoard()
print('Computer places O at', chr(ord('a') + row), chr(ord('a') + col), '.')
saveinfo()
postscript程序可能的优化方向
可能是程序错误!有一种情况,一方原本无棋可下,但对方下了棋后,突然有棋可下!因此,我们需要重新设置“是否有棋可以下”的条件码。我们将等到重大任务到期后再进行任何更改。
1)单独讨论计算机是黑是白太麻烦(相当于代码量*2)。可以用函数解决吗?
2)实际上,判断走棋的位置、得分以及是否合法都是用同一套函数。你可以使用函数来复用代码,所以不需要写那么多。
3)因为我们后面基本都是用pycharm进行一步步调试,所以有些判断和列表语句可能写得不是那么漂亮。
4)程序的稳定性有待测试(我实在是懒得用电脑玩那么多游戏了)
5)GameOver的多次进入功能可以优化吗?