案例介绍

本案例采用 python 实现了一个简单的井字棋游戏。该游戏在控制台界面中进行,有游戏提示。游戏棋盘为 3 X 3 的九宫格样式,每个棋格与数字键盘上的 1 - 9 数字键一一对应,可通过输入数字来选择落棋位置和落子。游戏的规则是两个玩家轮流下棋,首先实现横线、竖线、斜线连续三个格棋子一样的获胜。

学习目标

本案例主要是对 python 基础知识的运用,包括语法、列表型数据结构、元组、类、函数、循环、条件判断、控制台输入与输出、引入模块等基础知识。通过本案例的学习,将强化对这些知识的理解和运用。

需要的引入的模块

本案例以 python 3.5.3 版本为基础,需要引入三个模块 random 、 sys 、 copy .

模块 random :主要用于生成随机数;
模块 sys :该模块包含了与 Python 解释器和它的环境有关的函数。
模块 copy :主要用于拷贝对象。
案例结构和主要算法
该案例是个游戏,其主要算法为:

初始化棋盘,确定玩家执什么棋子,并随机确定谁执先手;
玩家和计算机轮换下棋。
计算机依据玩家落棋的位置,先依次做五个检测:自己下一步棋是否会赢、玩家下一步棋是否会赢、检测四个角是否为空、检测中心是否为空、检测边是否有空位,然后根据检测结果将棋子下在相应的位置,直到游戏结束。
该案例包含的功能模块
该案例只有一个程序文件: tictactoe.py ,其包含的功能模块为:

starGame( ) 函数:定义了本游戏的主要流程和逻辑——初始化棋盘、输出帮助信息、取得玩家姓名和希望执的棋子、随机选择谁执先手、进入游戏循环。
drawBoard( ) 函数:绘制或更新棋盘。如果没有棋盘,就绘制棋盘;如果棋盘已经存在,就更新棋盘。
help( ) 函数:输出帮助信息。
inputPlayerLetter( ) 函数:确定游戏双方选择的代表棋子的字符。
startGameLoop( ) 函数:定义了游戏的主循环。玩家和计算机轮换下棋,直到某一方获胜或达成平局。
getPlayerMove( ) 函数:玩家下棋,返回输入的位置数字。
makeMove( ) 函数:下棋,将字符配给相应的棋盘网格。
isWin( ) 函数:判断获胜的标准。
isBoardFull( ) 函数:检测棋盘是否下满棋。
isSpaceFree( ) 函数:检测哪个位置为空的,可以下棋。
getComputerMove( ) 函数:依次做五个检测,确定计算机要下棋的位置。
chooseRandomMove( ) 函数:随机下棋。
endGame( ) 函数:游戏结束时给出选择,再来一次,还是退出。
exitGame( ) 函数:退出游戏。
‘’’

代码实现

# -*- coding: utf-8 -*-

import random
import sys
import copy

class TicTacToe:
    # 初始化信息
    def __init__(self):
        self.board = [' '] * 10
        self.playerName = ''
        self.playerLetter = ''
        self.computerName = 'Leo'
        self.computerLetter = ''
        self.corners = [1,3,7,9]
        self.sides = [2,4,6,8]
        self.middle = 5

        self.form = '''
            \t        |   |
            \t      %s | %s | %s
            \t        |   |
            \t    -------------
            \t        |   |
            \t      %s | %s | %s
            \t        |   |
            \t    -------------
            \t        |   |
            \t      %s | %s | %s
            \t        |   |
           '''

    # 开始游戏
    def startGame(self):
        # 欢迎信息
        print('''\n\t------------------------------------
                \n\t             TIC-TAC-TOE
                \n\t------------------------------------
             ''')

        # 画棋盘,调用 drawBoard()
        self.drawBoard(board = None)

        # 显示帮助信息,调用 help()
        self.help()

        # 获取玩家姓名
        self.playerName = input("您好,我是 %s" % self.computerName + ". 请问您的名字是? ")

        # 获取代表棋子的字符,调用 inputPlayerLetter()
        self.playerLetter, self.computerLetter = self.inputPlayerLetter()
        print("您的选择是 " + self.playerLetter)

        # 随机选择谁执先手,并开始游戏。调用 startGameLoop()
        if random.randint(0,1) == 0:
            print(self.computerName + "为执先手方!")
            self.startGameLoop(self.computerName)
        else:
            print(self.playerName + "为执先手方!")
            self.startGameLoop(self.playerName)

    # 画棋盘,如果游戏新开始,没有棋盘,就初始化棋盘,棋盘格都为空。如果在游戏中,就在网格中显示字符。
    def drawBoard(self,board = None):
        if board is None:
            print(self.form % tuple(self.board[7:10] + self.board[4:7] + self.board[1:4]))
        else:
            print(self.form % tuple(board[7:10] + board[4:7] + board[1:4]))

    # 帮助信息
    def help(self):
        print('''
        \n\t 游戏说明:
        \n\t 1、游戏棋盘是以 3 X 3 的九宫格形式。每格坐标与键盘上的小键盘 1 - 9 数字键一一对应。
        \n\t 2、游戏中,两个玩家轮流下棋,输入数字,将棋下在九宫格的相应格里。
        \n\t 3、首先实现横线、竖线、斜线连续三个格棋子一样的获胜。
        ''')

    # 玩家输入选择的代表棋子的字符
    def inputPlayerLetter(self):
        letter = ''
        while not (letter == 'X' or letter == 'O'):
            print('您希望选择 X 还是 O ?')
            letter = input().upper()
        # 设置第一个字符为玩家,第二个为计算机
        if letter == 'X':
            return ['X', 'O']
        else:
            return ['O', 'X']

    # 游戏主循环
    def startGameLoop(self,turn):
        gameIsRunning = True
        player = turn
        while gameIsRunning:
            # 如果是玩家下棋
            if player == self.playerName:
                playerInput = self.getPlayerMove()
                self.makeMove(self.board, self.playerLetter, playerInput)
                if(self.isWin(self.board, self.playerLetter)):
                    self.drawBoard()
                    print("\n\t恭喜您获胜,%s!!! \t\n" % self.playerName)
                    gameIsRunning = False
                else:
                    if self.isBoardFull():
                        self.drawBoard()
                        print("\n\t 您和%s战成平局!!! \n\t" % self.computerName)
                        gameIsRunning = False
                    else:
                        self.drawBoard()
                        player = self.computerName
            # 转为计算机下棋
            else:
                computerMove =  self.getComputerMove()
                self.makeMove(self.board, self.computerLetter, computerMove)
                if (self.isWin(self.board, self.computerLetter)):
                    self.drawBoard()
                    print("\n\t很遗憾,计算机%s获得了胜利 \t\n" % self.computerName)
                    gameIsRunning = False
                    break
                else:
                    if self.isBoardFull():
                        self.drawBoard()
                        print("\n\t 您和%s战成平局!!! \n\t" % self.computerName)
                        gameIsRunning = False
                    else:
                        self.drawBoard()
                        player = self.playerName

        # 当跳出游戏循环,及获胜、平局、失败后结束游戏。
        self.endGame()

    # 玩家下棋,返回输入的位置数字
    def getPlayerMove(self):
        move = int(input("请选择棋子位置: (1-9) "))
        while move not in range(1,10) or not self.isSpaceFree(self.board, move):
            move = int(input("无效操作,请重新选择: (1-9) "))
        return move

    # 下棋,将字符配给相应的棋盘网格
    def makeMove(self,board, letter, move):
        board[move] = letter

    # 判断获胜的标准
    def isWin(self, board, letter):
        if ((board[1] == letter and board[2] == letter and board[3] == letter) or      # 下横线
                (board[4] == letter and board[5] == letter and board[6] == letter) or  # 中横线
                (board[7] == letter and board[8] == letter and board[9] == letter) or  # 上横线
                (board[1] == letter and board[4] == letter and board[7] == letter) or  # 左竖线
                (board[2] == letter and board[5] == letter and board[8] == letter) or  # 中竖线
                (board[3] == letter and board[6] == letter and board[9] == letter) or  # 右竖线
                (board[1] == letter and board[5] == letter and board[9] == letter) or  # 一条对角线
                (board[3] == letter and board[5] == letter and board[7] == letter)):   # 又一条对角线
            return True
        else:
            return False

    # 检测棋盘是否下满
    def isBoardFull(self):
        for i in range(1,10):
            if self.isSpaceFree(self.board, i):
                return False
        return True

    # 检测哪个位置为空的,可以下棋
    def isSpaceFree(self, board, move):
        return board[move] == ' '

    # 计算机下棋,得到要下的位置
    # 依次做五个检测: 1、自己下一步棋是否会赢; 2、玩家下一步棋是否会赢; 3、检测四个角是否为空; 4、检测中心是否为空; 5、检测边是否有空位。
    def getComputerMove(self):
        for i in range(1, 10):
            boardCopy = copy.deepcopy(self.board)
            if self.isSpaceFree(boardCopy, i):
                self.makeMove(boardCopy, self.computerLetter, i)
                if self.isWin(boardCopy, self.computerLetter):
                    return i

        for i in range(1, 10):
            boardCopy = copy.deepcopy(self.board)
            if self.isSpaceFree(boardCopy, i):
                self.makeMove(boardCopy, self.playerLetter, i)
                if self.isWin(boardCopy, self.playerLetter):
                    return i

        move = self.chooseRandomMove(self.corners)
        if move != None:
            return move

        if self.isSpaceFree(self.board, self.middle):
            return self.middle

        return self.chooseRandomMove(self.sides)

    # 随机下棋
    def chooseRandomMove(self, moveList):
        possibleWinningMoves = []
        for move in moveList:
            if self.isSpaceFree(self.board, move):
                possibleWinningMoves.append(move)
                if len(possibleWinningMoves) != 0:
                    return random.choice(possibleWinningMoves)
                else:
                    return None

    # 游戏结束
    def endGame(self):
        playAgain = input("您希望再战一局吗? (y/n): ").lower()
        if playAgain == 'y':
            self.__init__()
            self.startGame()
        else:
            print("\n\t-- 游戏结束!!!--\n\t")
            self.exitGame()

    # 退出游戏
    def exitGame(self):
        print("\n\t 感谢您的参与 ? \n\t 欢迎下次再玩!\n")
        sys.exit()

if __name__ == "__main__":
     TTT = TicTacToe()
     TTT.startGame()