掘金 人工智能 04月30日 08:53
用Trae,找初恋,代码写人生,Trae圆你初恋梦。
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

这篇文章分享了作者使用Trae平台,通过PyTorch和TensorFlow实现井字棋AI开发,并结合PyQt5优化用户体验的实践过程。作者将童年井字棋游戏与Minimax算法、Alpha-Beta剪枝等人工智能技术联系起来,探讨了算法背后的决策智慧,以及在青春记忆中的情感共鸣。文章不仅介绍了技术实现,更融入了对青春岁月的怀念,引发了对人工智能与人类情感关系的思考。

🕹️ **青涩的“如果体”思维**:文章指出,井字棋游戏中的预判思维,与Minimax算法的雏形相符,即推演两三步后的棋局,提前预判对方的应对。

🧠 **懵懂的最大最小原则**:作者解释了在井字棋中,玩家本能地遵循最大最小原则,即在自己回合最大化收益,对方回合最小化收益,最终在“最坏可能”里选择“相对最好”的策略。

⚖️ **Alpha-Beta剪枝的实现技巧**:文章详细介绍了Alpha-Beta剪枝的原理,通过维护alpha和beta两个值,当alpha >= beta时,终止当前分支的探索,从而提高算法效率。

🏆 **胜负判定函数**:文章展示了胜负判定函数,该函数作为赛场裁判,每走一步就检查是否有玩家连成直线。返回1表示玩家胜,-1表示AI胜,0代表继续比赛。

我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:www.trae.com.cn/?utm_source…

藏在井字棋里的青春密码

十五岁的午后,教室窗外的梧桐叶在初夏的风里沙沙作响。她用铅笔在草稿纸上画下歪歪扭扭的井字格,抬头时马尾辫扫过我的课本。"该你了",她指着第三行第二列的空格,眼睫在阳光下扑闪。那时我们总以为,这九宫格里的"X"与"O"不过是课间十分钟的游戏,却不知那些交错的三连棋里,藏着后来让我痴迷的算法奥秘。

二十年后的今天,当我用代码复现这个游戏时,屏幕上的AI总能精准落子。那些被橡皮擦抹去的棋局,那些故意输掉的对局,那些藏在胜负里的小心思,忽然在记忆里鲜活起来——原来当年我们稚嫩的博弈策略,早已预言了人工智能领域最经典的Minimax算法。

Trae 圆梦

首先,我们向Trae发出请求: “基于pytorch或者TensorFlow实现井字棋AI开发(Minimax算法+alpha-beta剪枝)”

随后系统会自动生成大量代码,其中部分内容你可能熟悉,也可能有些陌生。不过无需担心,我们可以根据提示信息,将这些代码逐一复制粘贴到对应的项目文件中。

完成代码的复制粘贴后,请运行 main.py 文件。若程序正常运行,控制台将进入交互模式,此时可以开始输入指令。

由于命令行交互体验不佳,我们进一步向 Trae 发送指令: “使用 PyQt5 实现 GUI 界面” ,以优化用户操作体验。

执行效果如下:

现在,这个小游戏已经可以发送给你的初恋对象啦!下面我们简单分析下游戏原理:为什么当年玩井字棋总是赢不了呢?

藏在青春悸动里的决策智慧

2.1 青涩的"如果体"思维

"如果我把棋子放在这里,她会怎么应对?"当年的课桌上,我们本能地推演着两三步后的棋局。这种朴素的预判思维,正是Minimax算法的雏形。

就像少年在纸条上写下又划去的告白词,算法也在虚拟的决策树上反复权衡:

2.2 懵懂的最大最小原则

那时的我们不懂算法,却在本能践行着核心逻辑:

这种思维模式,像极了初恋时小心翼翼的试探:既期待对方察觉心意,又害怕被直接拒绝后的尴尬。算法中的评分机制,恰似少年藏在课桌抽屉里的日记,给每个选择默默打分。

逐行解析核心算法

3.1 胜负判定函数

这个函数就像赛场裁判,每走一步就检查是否有玩家连成直线。返回1表示玩家胜,-1表示AI胜,0代表继续比赛。

def check_winner(board):    # 检查横向三连    for i in range(3):        if board[i,0] !=0 and (board[i] == board[i,0]).all():            return board[i,0]    # 检查纵向三连(类似逻辑)    # 检查两条对角线    return 0

3.2 Minimax递归框架

这个递归函数像一位深谋远虑的军师,不断推演后续可能的发展。参数说明:

def minimax(board, depth, is_maximizing, alpha, beta):    # 终止条件    winner = check_winner(board)    if winner !=0:        return 1 if winner==-1 else -1  # AI胜得1分        if 棋盘已满:        return 0  # 平局    if is_maximizing:  # AI回合        best = -∞        for 每个空位:            模拟落子            score = minimax(新棋盘, depth+1, False, alpha, beta)            best = max(best, score)            alpha = max(alpha, best)            if beta <= alpha:  # Alpha-Beta剪枝                break        return best    else:  # 玩家回合        # 类似逻辑,取最小值

Alpha-Beta剪枝原理

4.1 剪枝的必要性

假设AI在评估某个走法时,发现后续存在必输的局面,就可以立即放弃这个分支。就像下棋时说:"这步棋走下去肯定要输,不用再算了!"

4.2 剪枝的实现技巧

通过维护两个值:

alpha >= beta时,说明当前分支已经没有继续探索的价值。代码中对应的关键片段:

if beta <= alpha:    break  # 剪枝!

4.3 实际案例演示

假设当前棋盘:AI(O方)在计算时,发现某步棋可能导致玩家下一步形成双杀。这时Alpha-Beta机制会立即终止该分支的深入计算。

X |   | O---------  | X |  ---------O |   |  

当代码中的check_winner()函数扫描棋盘时,我总会想起她突然亮起来的眼睛——那年午后,她用手指划过三个连成一线的"O",欢呼声惊飞了窗台的白鸽。如今的AI虽能瞬间判断胜负,却永远读不懂获胜时人类眼底的星光。

minimax()函数的递归深处,算法在虚拟时空中推演着万千可能。这多像十七岁那个雨季,我在数学课上草稿本写满的"如果":"如果那天帮她捡起橡皮时多说一句话"、"如果校运会时报名双人项目"...那些未说出口的选择枝桠,最终都成了记忆里的alpha-beta剪枝。

致青春

当我们用算法复现青春的游戏,那些冰冷的代码竟也有了温度。屏幕上的AI永远理性,但当年故意输掉棋局时,藏在规则漏洞里的温柔,才是人类最珍贵的算法。或许人工智能永远学不会,为什么某个初夏的课间,有个少年明明看到必胜棋路,却悄悄把棋子落在了别处。

完整代码示例

import sysimport torchfrom PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QGridLayout, QMessageBoxPLAYER = 1AI = -1EMPTY = 0symbols = {PLAYER: 'X', AI: 'O', EMPTY: ' '}def check_winner(board):    for i in range(3):        if board[i, 0] != 0 and (board[i] == board[i, 0]).all():            return board[i, 0].item()        if board[0, i] != 0 and (board[:, i] == board[0, i]).all():            return board[0, i].item()    if board[1, 1] != 0:        if (board[0, 0] == board[1, 1] and board[2, 2] == board[1, 1]) or \           (board[0, 2] == board[1, 1] and board[2, 0] == board[1, 1]):            return board[1, 1].item()    return 0def minimax(board, depth, is_maximizing, alpha, beta):    winner = check_winner(board)    if winner != 0:        return 10 - depth if winner == AI else depth - 10    if torch.all(board != 0):        return 0    if is_maximizing:        best_score = -float('inf')        for move in torch.nonzero(board == 0):            row, col = move            new_board = board.clone()            new_board[row, col] = AI            score = minimax(new_board, depth + 1, False, alpha, beta)            best_score = max(best_score, score)            alpha = max(alpha, best_score)            if beta <= alpha:                break        return best_score    else:        best_score = float('inf')        for move in torch.nonzero(board == 0):            row, col = move            new_board = board.clone()            new_board[row, col] = PLAYER            score = minimax(new_board, depth + 1, True, alpha, beta)            best_score = min(best_score, score)            beta = min(beta, best_score)            if beta <= alpha:                break        return best_scoredef find_best_move(board):    best_score = -float('inf')    best_move = None    for move in torch.nonzero(board == 0):        row, col = move        new_board = board.clone()        new_board[row, col] = AI        score = minimax(new_board, 0, False, -float('inf'), float('inf'))        if score > best_score:            best_score = score            best_move = (row.item(), col.item())    return best_moveclass TicTacToe(QWidget):    def __init__(self):        super().__init__()        self.setWindowTitle("Tic Tac Toe - PyQt5")        self.board = torch.zeros((3, 3), dtype=torch.int)        self.buttons = [[None for _ in range(3)] for _ in range(3)]        self.init_ui()    def init_ui(self):        layout = QGridLayout()        for row in range(3):            for col in range(3):                btn = QPushButton('')                btn.setFixedSize(100, 100)                btn.setStyleSheet("font-size: 24px;")                btn.clicked.connect(lambda _, r=row, c=col: self.player_move(r, c))                self.buttons[row][col] = btn                layout.addWidget(btn, row, col)        self.setLayout(layout)    def player_move(self, row, col):        if self.board[row, col] != 0:            return        self.board[row, col] = PLAYER        self.update_ui()        if self.check_game_end():            return        self.ai_move()    def ai_move(self):        move = find_best_move(self.board)        if move:            row, col = move            self.board[row, col] = AI            self.update_ui()            self.check_game_end()    def update_ui(self):        for row in range(3):            for col in range(3):                self.buttons[row][col].setText(symbols[self.board[row, col].item()])    def check_game_end(self):        winner = check_winner(self.board)        if winner != 0:            QMessageBox.information(self, "Game Over", "You win!" if winner == PLAYER else "AI wins!")            self.reset_game()            return True        elif torch.all(self.board != 0):            QMessageBox.information(self, "Game Over", "It's a tie!")            self.reset_game()            return True        return False    def reset_game(self):        self.board[:] = 0        self.update_ui()if __name__ == '__main__':    app = QApplication(sys.argv)    window = TicTacToe()    window.show()    sys.exit(app.exec_())

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

井字棋 Minimax算法 Alpha-Beta剪枝 人工智能 PyQt5
相关文章