大爽Python入门教程 6-9 实例演示三 控制台——井字棋游戏(Tic-Tac-Toe)

大爽Python入门公开课教案 点击查看教程总目录

1 游戏介绍

实现一个控制台版本的井字棋小游戏, 英文名叫Tic-Tac-Toe。
代码量:100行左右。

面板展示效果

   |   |   
---+---+---
   |   |   
---+---+---
   |   |   

两种棋子,
一种用"X"来表示,
另一种用"O"表示。

流程说明

觉得流程描述罗嗦,可以直接看运行效果部分。

  1. 进入游戏,展示欢迎语,
  2. 展示井字棋面板,
    提醒当前玩家输入落子位置。
  3. 检查输入,是否符合位置格式,且对应位置是否为空,
    不满足则展示原因,并再次读取输入。
    满足则落子成功。
  4. 落子成功后,检查是否胜利(有三个字连成一条线),
    未胜利则切换到另一个玩家,
    然后重复上述步骤123
  5. 落子成功后,如果胜利,则直接展示面板,恭喜当前玩家,然后结束游戏。

运行效果

运行示例中的输入为

1, 1
1,0
0, 0
1,1
123
2,2
0,2
0,1
2,0

运行的效果为

Welcome to Tic-Tac-Toe!
   |   |   
---+---+---
   |   |   
---+---+---
   |   |   
X's turn. Enter row index and column index to place (ri, ci):
1, 1
   |   |   
---+---+---
   | X |   
---+---+---
   |   |   
O's turn. Enter row index and column index to place (ri, ci):
1,0
   |   |   
---+---+---
 O | X |   
---+---+---
   |   |   
X's turn. Enter row index and column index to place (ri, ci):
0, 0
 X |   |   
---+---+---
 O | X |   
---+---+---
   |   |   
O's turn. Enter row index and column index to place (ri, ci):
1,1
The place is already occupied.
 X |   |   
---+---+---
 O | X |   
---+---+---
   |   |   
O's turn. Enter row index and column index to place (ri, ci):
123
Invalid command.
 X |   |   
---+---+---
 O | X |   
---+---+---
   |   |   
O's turn. Enter row index and column index to place (ri, ci):
2,2
 X |   |   
---+---+---
 O | X |   
---+---+---
   |   | O
X's turn. Enter row index and column index to place (ri, ci):
0,2
 X |   | X
---+---+---
 O | X |   
---+---+---
   |   | O
O's turn. Enter row index and column index to place (ri, ci):
0,1
 X | O | X
---+---+---
 O | X |   
---+---+---
   |   | O
X's turn. Enter row index and column index to place (ri, ci):
2,0
 X | O | X
---+---+---
 O | X |   
---+---+---
 X |   | O
X win!

2 初步分析

大概步骤,在流程说明中,其实就分析的差不多了。
所以可以直接思考如何设计函数。

同时本章第四节部分已经实现了井字棋的胜利的检测。
这里可以直接拿来用(简单修改下更好)。

函数初步设计

需要微调的函数

  • check_bunch_match(bunch, symbol): 检查一串棋子bunch是否全都都是symbol对应的棋
  • get_ttt_winner(board):: 得出谁是赢家

对应的,需要以下几个函数

  • generate_board: 生成井字格棋盘(3x3二维列表)
  • show_board: 展示井字格棋盘(以可读性高的形式)
  • parse_rc: 从用户输入中解析出行列位置坐标。
  • main: 主函数或者说主逻辑

分析与调整

  • check_bunch_match(bunch, symbol) 不用调整

微调

  • get_ttt_winner(board) 调整成check_win(board, symbol), 检查symbol对应的棋子是否胜利。
    因为每次落子后只需要检查当前棋子是否能够练成一条线。

其他新的函数需要哪些参数

  • generate_board(): 无需参数
  • show_board(board): board为井字格棋盘(3x3二维列表)
  • parse_rc(cmd): cmd为用户输入的落子坐标(字符串)。
  • main(): 无需参数

3 逐步实现

字符串定义

先在代码开头定义要用的的字符串。

WELCOME = "Welcome to Tic-Tac-Toe!"
ENTER  = "%s's turn. Enter row index and column index to place (ri, ci):\n"
Invalid = "Invalid command."
Used = "The place is already occupied."

函数声明

把要用到的函数先声明

def generate_board():
    return

def show_board(board):
    pass

def parse_rc(cmd):
    return -1, -1

def check_bunch_match(bunch, symbol):
    return False

def check_win(board, symbol):
    return False

def main():
    pass

这里先实现generate_board,然后再去实先main

generate_board

代码如下

def generate_board():
    board = [
        [" " for ci in range(3)] for ri in range(3)
    ]

    return board

main

代码如下

def main():
    board = generate_board()
    print(WELCOME)

    turn = "X"
    while True:
        show_board(board)

        cmd = input(ENTER % turn)
        ri, ci = parse_rc(cmd)  # 解析出命令中的行列位置

        if ri >= 0:  # 检查输入是否有效
            if board[ri][ci] == " ":  # 检查是否是空格子
                board[ri][ci] = turn  # 直接放入棋子

                # 检查是否胜利,胜利则展示面板与结果并退出
                if check_win(board, turn):
                    show_board(board)
                    print("%s win!" % turn)
                    return

                # 切换玩家
                if turn == "X":
                    turn = "O"
                else:
                    turn = "X"
            else:
                print(Used)
        else:
            print(Invalid)

show_board

代码如下

def show_board(board):
    for ri, row in enumerate(board):

        row_str = ""
        for ci, cell in enumerate(row):
            row_str += " %s " % cell
            if ci != 2:
                row_str += "|"
        print(row_str)

        if ri != 2:
            print("---+---+---")

parse_rc

代码如下

def parse_rc(cmd):
    rc = cmd.split(",")
    if len(rc) == 2:
        ri, ci = rc
        ri = ri.strip()
        ci = ci.strip()
        if ci.isdigit() and ri.isdigit():
            ci = int(ci)
            ri = int(ri)
            if 0 <= ci < 3 and 0 <= ri < 3:
                return ri, ci

    return -1, -1

check_bunch_match

check_bunch_match直接复制过来就行
代码如下

def check_bunch_match(bunch, symbol):
    res = True
    for one in bunch:
        if one != symbol:
            res = False
            break

    return res

check_win

微调后代码如下

def check_win(board, symbol):
    r, c = len(board), len(board[0])
    for ri in range(r):
        row = board[ri]
        if check_bunch_match(row, symbol):
            return True

    for ci in range(c):
        column = [board[ri][ci] for ri in range(r)]
        if check_bunch_match(column, symbol):
            return True

    diagonal_1 = [board[ri][ri] for ri in range(r)]
    diagonal_2 = [board[ri][c-ri-1] for ri in range(r)]
    if check_bunch_match(diagonal_1, symbol):
        return True
    if check_bunch_match(diagonal_2, symbol):
        return True

    return False

调用代码

main()

4 最终代码

最终代码如下(运行效果第一部分已展示,不再额外展示)。

WELCOME = "Welcome to Tic-Tac-Toe!"
ENTER  = "%s's turn. Enter row index and column index to place (ri, ci):\n"
Invalid = "Invalid command."
Used = "The place is already occupied."


def generate_board():
    board = [
        [" " for ci in range(3)] for ri in range(3)
    ]

    return board


def show_board(board):
    for ri, row in enumerate(board):

        row_str = ""
        for ci, cell in enumerate(row):
            row_str += " %s " % cell
            if ci != 2:
                row_str += "|"
        print(row_str)

        if ri != 2:
            print("---+---+---")


def parse_rc(cmd):
    rc = cmd.split(",")
    if len(rc) == 2:
        ri, ci = rc
        ri = ri.strip()
        ci = ci.strip()
        if ci.isdigit() and ri.isdigit():
            ci = int(ci)
            ri = int(ri)
            if 0 <= ci < 3 and 0 <= ri < 3:
                return ri, ci

    return -1, -1


def check_bunch_match(bunch, symbol):
    res = True
    for one in bunch:
        if one != symbol:
            res = False
            break

    return res


def check_win(board, symbol):
    r, c = len(board), len(board[0])
    for ri in range(r):
        row = board[ri]
        if check_bunch_match(row, symbol):
            return True

    for ci in range(c):
        column = [board[ri][ci] for ri in range(r)]
        if check_bunch_match(column, symbol):
            return True

    diagonal_1 = [board[ri][ri] for ri in range(r)]
    diagonal_2 = [board[ri][c-ri-1] for ri in range(r)]
    if check_bunch_match(diagonal_1, symbol):
        return True
    if check_bunch_match(diagonal_2, symbol):
        return True

    return False


def main():
    board = generate_board()
    print(WELCOME)

    turn = "X"
    while True:
        show_board(board)
        cmd = input(ENTER % turn)
        ri, ci = parse_rc(cmd)
        if ri >= 0:
            if board[ri][ci] == " ":
                board[ri][ci] = turn

                if check_win(board, turn):
                    show_board(board)
                    print("%s win!" % turn)
                    return

                if turn == "X":
                    turn = "O"
                else:
                    turn = "X"
            else:
                print(Used)
        else:
            print(Invalid)


main()
posted @ 2021-12-05 22:41  大爽歌python编程辅导  阅读(584)  评论(0编辑  收藏  举报