Python AI游戏编程入门:基于Pygame和PyTorch
上QQ阅读APP看书,第一时间看更新

2.2 编写第一个小游戏

本书以游戏为编程主题,下面就让我们尝试编写一个井字棋游戏来热身。编写该游戏一方面是为了复习我们以前学过的Python知识,另一方面也可以让我们更熟悉VS Code。

井字棋游戏很简单,它的棋盘是一个3×3的网格,玩家率先实现3颗棋子在一条直线上即可胜利。本游戏程序包含的核心数据就是棋盘信息,另外还需要若干函数来承载游戏功能,例如显示棋盘信息的函数、处理玩家输入的函数,以及判断棋局胜负的函数等。所有代码都在一个代码文件中。

首先定义几个变量,包括棋盘大小变量boardSize和棋盘信息变量board。我们会使用两种符号分别表示两个玩家的落子,其中一个玩家的落子使用字母符号“X”来表示,而另一个玩家的落子使用字母符号“O”来表示,当棋盘上还没有玩家落子时,所有位置都以符号“.”来表示。先手玩家使用字母符号“X”来落子,将落子信息保存在变量currentPlayer中。随后输出一些游戏提示信息。

boardSize = 3
board = ['.'] * boardSize * boardSize
currentPlayer = 'X'
 
print("井字棋游戏开始")
print("规则:三子连成直线即胜利")
print("X先手,O后手")

随后我们定义print_board函数,它负责在终端上显示棋盘信息。

def print_board(board):
    print("\n")
    print("%s|%s|%s"%(board[0],board[1],board[2]))
    print("-+-+-")
    print("%s|%s|%s"%(board[3],board[4],board[5]))
    print("-+-+-")
print("%s|%s|%s"%(board[6],board[7],board[8]))

我们还需要一个函数来负责判断胜负。函数hasWon看起来很复杂,但其内在逻辑很简单,就是列举各种可能获胜的棋局状态,只要满足这种状态就返回True。如果觉得某个地方不容易理解,可以通过VS Code设置断点,并按键盘上的F5键进入调试模式,在调试模式下可以一步步地运行代码,以观察每个变量的值并理解代码的运行逻辑。

def hasWon(currentBoard,player):
    winningSet=[player in range(boardSize)]
 
    row1=currentBoard[:3]
    row2=currentBoard[3:6]
    row3=currentBoard[6:]
    if winningSet in [row1,row2,row3]:
        return True
 
    col1=[currentBoard[0],currentBoard[3],currentBoard[6]]
    col2=[currentBoard[1],currentBoard[4],currentBoard[7]]
    col3=[currentBoard[2],currentBoard[5],currentBoard[8]]
    if winningSet in[col1,col2,col3]:
        return True
 
    diag1=[currentBoard[0],currentBoard[4],currentBoard[8]]
    diag2=[currentBoard[6],currentBoard[4],currentBoard[2]]
    if winningSet in [diag1,diag2]:
        return True
 
return False

因为两个玩家交替落子,所以需要定义getNextPlayer函数以交换当前玩家。

def getNextPlayer(currentPlayer):
    if currentPlayer == 'X':
        return 'O'
return 'X'

getPlayerMove函数用来处理玩家的输入。玩家输入的是坐标的形式,每次需要输入两个数字,数字之间以英文逗号间隔,数字取值为0~2。“X,Y”表示落子位置在棋盘上的第X行、第Y列。需要注意的是,该函数获取玩家输入后,会将这些输入信息通过字符串操作拆分成两部分,再转换成整数类型保存为坐标,最后将二维的坐标信息转换成一维的索引。判断当前棋盘的落子位置为空白位置时,就用当前玩家的表示落子的字母符号进行赋值。

def getPlayerMove(board,currentPlayer):
    isMoveValid = False
    while isMoveValid == False:
        print('')
        userMove = input(f'玩家{currentPlayer}输入棋盘坐标(坐标取值0,1,2):    X,Y?    ')
        userX, userY = [int(char) for char in userMove.split(',')]
        userIndex = userX * boardSize + userY
        if board[userIndex] == '.':
            isMoveValid = True
 
    board[userIndex] = currentPlayer
return board

当棋盘上摆满棋子时,需要结束游戏,因此要定义一个hasMovesLeft函数来判断还有没有空白位置供玩家落子。

def hasMovesLeft(board):
return '.' in board

最后将这些函数组装在一起。

if __name__ == '__main__':
    print_board(board)
    while hasMovesLeft(board):
        board = getPlayerMove(board,currentPlayer)
        print_board(board)
        if hasWon(board, currentPlayer):
            print('Player'+currentPlayer+'haswon!')
            break
currentPlayer = getNextPlayer(currentPlayer)

首先显示棋盘,只要棋盘上还有空白位置可以下棋,就保持运行游戏主循环中的代码。代码先处理玩家输入,显示新的棋盘;如果出现胜负结果,就显示相关信息。否则轮换玩家。游戏画面如图2-3所示。

图2-3

这个游戏是不是很简单。读者可以先试着玩一下。如果不能很好地理解这些代码,可以进入调试模式慢慢理解每行代码完成的任务。如果你对某些语法不熟悉,可以先复习、巩固对应的知识。