Reversegam是一款在格子上玩的游戏版游戏,所以我们将使用带有XY坐标的一个笛卡尔坐标系。
主要内容
- 如何玩Reversegam游戏
- bool函数
- 模拟在Reversegam游戏版上移动
- 编写一个Reversegam AI程序
目录
(三)def isValidMove(board, tile, xstart, ystart):
(五)getBoardWithValidMoves(board, tile):
(六)getValidMoves(board, tile):
(十)makeMove(board, tile, xstart, ystart):
(十三)getPlayerMove(board, playerTile):
(十四)getComputerMove(board, computerTile):
(十五)playGame(playerTile, computerTile):
如何玩Reversegam
Reversegam有一个8*8的游戏版,一方的棋子是黑色,另一方的棋子是白的(我们使用O和X来代替这两种颜色),开始的时候游戏版如图所示:
假设白子先走,那么可能的结果
轮流落子,一直到任意一位玩家不能落子或游戏板填满了的时候,游戏就结束了。剩下的棋子多的获胜。
导入模块和设置常量
# Reversegam: a clone of Othello/Reversi
# 导入random模块以便使用randint()函数和chice()函数。
import random
# 导入sys模块,以便使用exit()函数
import sys
# 定义两个常量用于设置游戏版
WIDTH = 8 # Board is 8 spaces wide.
HEIGHT = 8 # Board is 8 spaces tall.
游戏板的数据结构
这个数据结构是列表的列表,就像之前Sonar游戏中的游戏版一样。创建列表的列表,以便使用board[x][y]来表示坐落于X坐标轴(向左/向右)上的x位置和坐落于Y坐标轴(向上/向下)上的y位置的格子上的字符。
尽管游戏版的X坐标和Y坐标的范围是1到8,但列表数据结构的范围将会是从0到7,注意到这一点很重要。我们的代码将需要根据这一点来做一些调整。
自定义函数分析
Reversegam游戏通过自定义了16个函数来实现相关功能和计算机AI,下面将详细分析每一个自定义的函数中所涉及的Python语法以及在Reversegam游戏中所扮演的角色。
(一)drawBoard(board):
# 在屏幕上绘制游戏版数据结构
def drawBoard(board):
# Print the board passed to this function. Return None.
print(' 12345678')
print(' +--------+')
for y in range(HEIGHT):
print('%s|' % (y+1), end='')
for x in range(WIDTH):
print(board[x][y], end='')
print('|%s' % (y+1))
print(' +--------+')
print(' 12345678')
涉及的主要Python语法:
- def定义函数
- 函数形参
- print()函数
- for循环迭代
- range()函数
- 格式化字符串
以上所涉及到的语法都是我们之前所使用过的。
drawBoard()函数接受一个列表值,并且将其显示到屏幕上,以便玩家知道在哪里下棋子。通过for循环迭代将board列表的列表打印到屏幕上,注意print()函数中(y + 1)实现游戏版左边和右边的标签是从1到8,end=' '实现打印完一个字符之后不换行。
(二)getNewBoard():
# 创建一个新的游戏版数据结构
def getNewBoard():
# Create a brand-new, blank board data structure.
board = []
for i in range(WIDTH):
board.append([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '])
return board
涉及的主要Python语法:
- 无形参函数
- 列表append()方法
- return关键字
该函数返回一个列表的列表。
(三)def isValidMove(board, tile, xstart, ystart):
这个函数有点儿长
通过流程图来理解函数作用:
涉及的主要Python语法:
- 比较操作符
- 布尔运算
- 复合赋值操作符
- while循环
- len()函数
- if语句
给定了一个游戏版数据结构(board)、玩家的棋子(tile)以及玩家落子地XY坐标(xstart, ystart),如果Reversegam游戏规则允许在该坐标上落子,isValidMove()函数应该返回True;否则,它返回False。
对于一次有效地移动,必须满足:
- 它必须位于游戏版之上;
- 至少能够反转对手的一个棋子。
确定玩家(人类玩家和计算机玩家)棋子的类型
最后,如果给定的X坐标和Y坐标最终是一个有效位置,那么isValidMove()函数返回这一步落子可能导致反转的对手的所有棋子的一个列表。我们创建了一个新的、空的列表tilesToFlip,并且用它来存储所有这些棋子的坐标。
# 判断一次落子是否有效
def isValidMove(board, tile, xstart, ystart):
# Return False if the player's move on space xstart, ystart is invalid.
# If it is a valid move, return a list of spaces that would
# become the player's if they made a move here.
# 检查坐标是否在游戏版上、是否为空
if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):
return False
# 确定玩家棋子的类型
if tile == 'X':
otherTile = 'O'
else:
otherTile = 'X'
tilesToFlip = []
# 遍历两元素列表中的每一个,以便对每个方向都进行检查。
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1],
[0, -1], [-1, -1], [-1, 0], [-1, 1]]:
x, y = xstart, ystart # 多变量赋值
x += xdirection # First step in the x direction
y += ydirection # First step in the y direction
while isOnBoard(x, y) and board[x][y] == otherTile:
# Keep moving in this x & y direction.
x += xdirection
y += ydirection
if isOnBoard(x, y) and board[x][y] == tile:
# There are pieces to flip over. Go in the reverse direction
# until we reach the original space, noting all the tiles along the way.
while True:
x -= xdirection
y -= ydirection
if x == xstart and y == ystart:
break
tilesToFlip.append([x, y])
if len(tilesToFlip) == 0: # If no tiles were flipped, this is not a valid move.
return False
return tilesToFlip
为了检查一次落子是否有效,它使用当前玩家的新的棋子,将对手的棋子夹在这个新的棋子和玩家的一个旧的棋子之间,从而至少能够反转对手的一个棋子。这意味着,新的棋子必须紧挨着对手的一个棋子。
for循环遍历了列表的一个列表,表示了程序将要检查对手的一个棋子的方向:
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1],
[0, -1], [-1, -1], [-1, 0], [-1, 1]]:
游戏板使用X坐标和Y坐标表示的笛卡尔坐标系。这里有8个可以移动的方向:上下左右和四个对角线方向。通过Y坐标不变、X坐标加减1实现向右向左移动;通过X坐标不变、Y坐标加减1实现向下上移动;对于四个对角线方向,需要XY坐标同时加减实现。下图表示了元素列表中的哪一项表示哪一个方向:
for循环遍历了两元素列表中的每一个,以便对每个方向都进行检查。在for循环中使用了多变量赋值。