Skip to content

Tic-Tac-Toe

一个简单的井字棋项目,写给那些学了很多语法但不知道下一步该做什么的人。

Board

首先我们需要一个 3 ✖️ 3 的棋盘,可以用一个简单的列表来表示,每个元素就代表一个棋盘格。

py
[
    None, None, None,
    None, None, None,
    None, None, None
]

任务一:实现 new_board() 函数,返回一个空棋盘,也就是一个全为 None 的列表。

py
def new_board():
    # 补全它
参考
py
def new_board():
    return [None] * 9

Render

为了简化,我们会将棋盘直接 print 到终端内,就像这样:

    1 2 3
   -------
1 | X O X |
2 | O X O |
3 |       |
   -------

任务二:实现 print_board(board) 函数,接受棋盘作为参数,然后打印出棋盘。

py
def print_board(board):
    # 补全它

提示:print() 函数可以使用 end 参数来指定换行符。

参考
py
def print_board(board):
    print("    1 2 3")
    print("   -------")
    for row in range(3):
        print(f"{row + 1} | ", end="")
        for col in range(3):
            print(board[row * 3 + col] or " ", end=" ")
        print("|")
    print("   -------")

Input

任务三:获取玩家输入,范围是 [1, 9],如果输入不符合要求或已经被占用,则提示重新输入。

py
def get_move(board):
    # 补全它
参考
py
def get_move(board):
    while True:
        try:
            move = int(input("Enter move (1-9): ")) - 1
            if move < 0 or move >= 9:
                raise ValueError
            if board[move] is not None:
                print("This square is already taken, try again.")
                continue
            return move
        except ValueError:
            print("Invalid input, try again.")

Move

任务四:实现 make_move(board, move, player) 函数,在对应位置放置玩家的棋子。

py
def make_move(board, move, player):
    # 补全它
参考
py
def make_move(board, move, player):
    board[move] = player

Winner

任务五:检测有无获胜者,如果有,则返回获胜者,否则返回 None

py
def check_winner(board):
    lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ]

    # 补全它
参考
py
def check_winner(board):
    lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ]

    for line in lines:
        a, b, c = line
        if board[a] and board[a] == board[b] == board[c]:
            return board[a]
    return None

Draw

任务六:如果没有获胜者,则继续检测棋盘是否已经被占满,如果是,则判定为平局。

py
def check_draw(board):
    # 补全它
参考
py
def check_draw(board):
    return None not in board

Game Loop

任务七:组合以上函数,实现一个完整的游戏循环。

py
def play_game():
    # 初始化棋盘
    # 初始化玩家
    # 显示棋盘
    while True:
        # 玩家输入
        # 显示输入后的棋盘
        # 判定输赢或平局
        # 切换玩家

if __name__ == "__main__":
    play_game()
参考
py
def play_game():
    board = new_board()
    player = "X"
    print_board(board)
    while True:
        move = get_move(board)
        make_move(board, move, player)
        print_board(board)
        winner = check_winner(board)
        if winner:
            print(f"{winner} wins!")
            break
        if check_draw(board):
            print("Draw!")
            break
        player = "O" if player == "X" else "X"

Random AI

任务八:实现 AI 随机落子的函数,返回一个随机的棋子位置。

py
def get_random_ai_move(board):
    # 补全它
参考
py
def get_random_ai_move(board):
    available_moves = [i for i in range(9) if board[i] is None]
    return random.choice(available_moves)

def play_game():
    ...
    while True:
        if player == "X":
            move = get_move(board)
        else:
            move = get_random_ai_move(board)
        make_move(board, move, player)
        ...

Minimax AI

任务十:学习 Minimax 算法来实现 AI 的最佳落子。

py
def get_ai_move(board):
    # 补全它
参考
py
def get_ai_move(board):
    best_score = float("-inf")
    best_move = None
    for i in range(len(board)):
        if board[i] is None:
            board[i] = "O"
            score = minimax(board, False)
            board[i] = None
            if score > best_score:
                best_score = score
                best_move = i
    return best_move


def minimax(board, is_maximizing):
    winner = check_winner(board)
    if winner == "X":
        return -10
    elif winner == "O":
        return 10
    elif check_draw(board):
        return 0

    if is_maximizing:
        best_score = float("-inf")
        for i in range(len(board)):
            if board[i] is None:
                board[i] = "O"
                score = minimax(board, False)
                board[i] = None
                best_score = max(score, best_score)
        return best_score
    else:
        best_score = float("inf")
        for i in range(len(board)):
            if board[i] is None:
                board[i] = "X"
                score = minimax(board, True)
                board[i] = None
                best_score = min(score, best_score)
        return best_score

布局切换

调整 VitePress 的布局样式,以适配不同的阅读习惯和屏幕环境。

全部展开
使侧边栏和内容区域占据整个屏幕的全部宽度。
全部展开,但侧边栏宽度可调
侧边栏宽度可调,但内容区域宽度不变,调整后的侧边栏将可以占据整个屏幕的最大宽度。
全部展开,且侧边栏和内容区域宽度均可调
侧边栏宽度可调,但内容区域宽度不变,调整后的侧边栏将可以占据整个屏幕的最大宽度。
原始宽度
原始的 VitePress 默认布局宽度

页面最大宽度

调整 VitePress 布局中页面的宽度,以适配不同的阅读习惯和屏幕环境。

调整页面最大宽度
一个可调整的滑块,用于选择和自定义页面最大宽度。

内容最大宽度

调整 VitePress 布局中内容区域的宽度,以适配不同的阅读习惯和屏幕环境。

调整内容最大宽度
一个可调整的滑块,用于选择和自定义内容最大宽度。

聚光灯

支持在正文中高亮当前鼠标悬停的行和元素,以优化阅读和专注困难的用户的阅读体验。

ON开启
开启聚光灯。
OFF关闭
关闭聚光灯。