用Python打造AI玩家:挑战2048,谁与争锋

ops/2025/3/16 14:00:46/

文章目录

一、创作背景

二、效果图

三、准备工作

1. 安装Chrome和Chrome Driver

2. 安装Python库

四、代码说明

‌1. init_driver 函数‌

2. play_2048 函数‌

五、完整代码

六、改进版本

七、主要模块

八、核心算法分析

1. 棋盘状态获取

2. 位置权重系统

3. 连续性评估

4. 单调性评估

5. 移动模拟系统

九、评估系统

1. 评估标准

2. 决策机制

十、性能优化

1. 延迟控制

2. 错误处理

十一、完整代码

​编辑

十二、总结


一、创作背景

厌倦了手动滑动方块,在2048的海洋中挣扎?是时候让AI接管了!这是一个基于Python和Selenium的2048游戏AI程序,通过模拟浏览器操作实现自动玩游戏。下面我们一起构建游戏环境、设计AI算法,并最终见证AI如何以惊人的策略和速度,突破2048!

二、效果图

说明:模拟Chrome浏览器登入2048小游戏网站,AI将自动开始游戏,Pycharm终端输出实时棋盘

三、准备工作

1. 安装Chrome和Chrome Driver

‌下载与 Chrome 浏览器和与之版本匹配的 ChromeDriver,并将其路径添加到系统 PATH 中,或在代码中指定路径。

具体细节可参考这两篇文章:彻底解决 Selenium ChromeDriver 不匹配问题:Selenium ChromeDriver 最新版本下载安装教程_driver = webdriver.chrome-CSDN博客

ChromeDriver下载安装 - chenhongl - 博客园

2. 安装Python库

pip install selenium

四、代码说明

‌1. init_driver 函数‌

初始化 Chrome WebDriver

2. play_2048 函数‌

使用 driver.get(GAME_URL) 打开 2048 游戏网页。
等待游戏加载完成(这里使用简单的 time.sleep(2),可根据实际情况调整)。
通过 driver.find_element_by_tag_name('body') 获取游戏主体元素,用于发送键盘指令。
在无限循环中,随机选择一个移动方向,并发送键盘指令。
使用 time.sleep(random.uniform(*DELAY_RANGE)) 实现随机延迟,模拟人类操作。
检查游戏是否结束,这里通过尝试点击“重玩”按钮来判断。如果找到“重玩”按钮,则打印最终得分并点击按钮开始新游戏;如果找不到,则继续游戏循环。
捕获 KeyboardInterrupt 异常,允许用户通过 Ctrl+C 手动停止游戏。
在 finally 块中确保 WebDriver 正常关闭。

五、完整代码

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
import random
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options# 游戏网址
GAME_URL = 'https://2048game.com/'# 指定 Chrome以及Chrome Driver可执行文件路径
chrome_path = {"Windows": r"你自己电脑上chrome可执行文件的绝对路径",}
options = Options()
options.binary_location = chrome_path["Windows"]
driver_path = r'你自己电脑上chrome Driver所在目录'
service = Service(executable_path=driver_path)# 定义移动方向
MOVES = [Keys.ARROW_UP, Keys.ARROW_RIGHT, Keys.ARROW_DOWN, Keys.ARROW_LEFT]# 随机延迟时间范围(秒)
DELAY_RANGE = (0.5, 1.5)def init_driver():driver = webdriver.Chrome(service=service, options=options)return driverdef play_2048():driver = init_driver()try:driver.get(GAME_URL)# 等待游戏加载完成(可根据实际情况调整等待时间)time.sleep(2)game_board = driver.find_element(By.TAG_NAME, 'body')while True:# 随机选择一个移动方向move = random.choice(MOVES)game_board.send_keys(move)# 随机延迟time.sleep(random.uniform(*DELAY_RANGE))# 检查游戏是否结束(可根据页面元素调整检查逻辑)# 这里简单通过尝试点击“重玩”按钮来判断游戏是否结束,如果不存在则继续游戏try:replay_button = driver.find_element(By.CLASS_NAME, 'replay-button')print("游戏结束!最终得分:", driver.find_element(By.CLASS_NAME, 'score-board').text.split()[-1])replay_button.click()# 等待新游戏开始time.sleep(2)except Exception:# 如果找不到“重玩”按钮,则继续游戏循环continueexcept KeyboardInterrupt:print("用户手动停止游戏")finally:driver.quit()if __name__ == "__main__":play_2048()

六、改进版本

由于采用的策略是随机移动,因此游戏表现性能不稳定,得分不高。下面将介绍一种更智能的移动策略来提高游戏成绩。

主要特点包括:
- 采用启发式评估系统进行决策
- 实现了基于位置的权重系统
- 考虑了数字的连续性和单调性
- 具有完善的错误处理机制

七、主要模块

1. 浏览器控制模块
   - 初始化WebDriver
   - 页面元素定位
   - 键盘事件模拟

2. 游戏状态获取模块
   - 棋盘状态解析
   - 数据转换和存储

3. 决策系统模块
   - 移动模拟
   - 状态评估
   - 最优移动选择

4. 评估系统模块
   - 位置权重计算
   - 连续性评估
   - 单调性评估

八、核心算法分析

1. 棋盘状态获取

实现细节:
1)使用Selenium的find_elements方法获取所有tile元素
2)通过class属性解析每个tile的位置信息
3)使用try-except处理空值情况
4)返回4x4的二维数组表示棋盘状态

def get_board_state(driver):tiles = driver.find_elements(By.CLASS_NAME, 'tile')board = [[0] * 4 for _ in range(4)]for tile in tiles:classes = tile.get_attribute('class').split()for cls in classes:if cls.startswith('tile-position-'):position = cls.split('-')x = int(position[2]) - 1y = int(position[3]) - 1try:value = int(tile.text) if tile.text else 0except ValueError:value = 0board[y][x] = valuereturn board

2. 位置权重系统

POSITION_WEIGHTS = [
    [4, 3, 2, 1],
    [3, 2, 1, 1],
    [2, 1, 1, 1],
    [1, 1, 1, 1]
]

实现细节:
1. 定义4x4的权重矩阵,角落权重最高
2. 遍历棋盘计算加权得分
3. 大数字在角落获得更高分数

def calculate_position_score(board):score = 0for i in range(4):for j in range(4):if board[i][j] != 0:score += board[i][j] * POSITION_WEIGHTS[i][j]return score

3. 连续性评估

实现细节:
1. 分别评估水平和垂直方向的连续性
2. 相同数字相邻获得双倍分数
3. 数字相差1获得额外奖励

def calculate_continuity(board):score = 0# 水平连续性for i in range(4):for j in range(3):if board[i][j] != 0 and board[i][j+1] != 0:if board[i][j] == board[i][j+1]:score += board[i][j] * 2elif abs(board[i][j] - board[i][j+1]) == 1:score += min(board[i][j], board[i][j+1])return score

4. 单调性评估

实现细节:
1. 评估数字是否按顺序排列
2. 计算符合单调性的相邻数字对
3. 鼓励数字的有序排列

def calculate_monotonicity(board):score = 0for i in range(4):for j in range(3):if board[i][j] >= board[i][j+1]:score += 1return score

5. 移动模拟系统

实现细节:
1. 深拷贝当前棋盘状态
2. 使用merged数组防止重复合并
3. 分别处理四个方向的移动
4. 实现数字的移动和合并逻辑

def simulate_move(board, move):new_board = [row[:] for row in board]merged = [[False] * 4 for _ in range(4)]# ... 移动逻辑实现

九、评估系统

1. 评估标准

def evaluate_move(board, move):score = (empty_cells * 200 +           # 空格权重max_tile * 100 +              # 最大数字权重position_score * 50 +         # 位置权重continuity_score * 30 +       # 连续性权重monotonicity_score * 20)      # 单调性权重

权重分配说明:
1. 空格数量:200
   - 保持棋盘有足够空间
   - 避免过早填满

2. 最大数字:100
   - 鼓励产生更大的数字
   - 提高游戏得分

3. 位置权重:50
   - 优化数字分布
   - 保持大数字在角落

4. 连续性:30
   - 提高合并效率
   - 保持数字相邻

5. 单调性:20
   - 保持数字有序
   - 便于后续合并

2. 决策机制

实现细节:
1. 遍历所有可能的移动方向
2. 评估每个移动的得分
3. 选择得分最高的移动
4. 默认返回向下移动

def get_best_move(board):best_score = float('-inf')best_move = Nonefor move in MOVES:score = evaluate_move(board, move)if score > best_score:best_score = scorebest_move = move

十、性能优化

1. 延迟控制

说明:
1)使用随机延迟避免固定模式
2)延迟范围0.5-1.5秒
3)模拟人类操作特征

DELAY_RANGE = (0.5, 1.5)
time.sleep(random.uniform(*DELAY_RANGE))

2. 错误处理

实现细节:
1. 处理空值情况
2. 处理数值转换异常
3. 确保程序稳定运行

try:value = int(tile.text) if tile.text else 0
except ValueError:value = 0

十一、完整代码

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
import random
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
import numpy as np# 游戏网址
GAME_URL = 'https://2048game.com/'# 指定 Chrome以及Chrome Driver可执行文件路径
chrome_path = {"Windows": r"你自己电脑上chrome可执行文件的绝对路径",}
options = Options()
options.binary_location = chrome_path["Windows"]
driver_path = r'你自己电脑上chrome Driver所在目录'
service = Service(executable_path=driver_path)# 定义移动方向
MOVES = [Keys.ARROW_UP, Keys.ARROW_RIGHT, Keys.ARROW_DOWN, Keys.ARROW_LEFT]# 随机延迟时间范围(秒)
DELAY_RANGE = (0.5, 1.5)# 位置权重矩阵(角落和边缘权重更高)
POSITION_WEIGHTS = [[4, 3, 2, 1],[3, 2, 1, 1],[2, 1, 1, 1],[1, 1, 1, 1]
]def init_driver():driver = webdriver.Chrome(service=service, options=options)return driverdef get_board_state(driver):tiles = driver.find_elements(By.CLASS_NAME, 'tile')board = [[0] * 4 for _ in range(4)]for tile in tiles:classes = tile.get_attribute('class').split()for cls in classes:if cls.startswith('tile-position-'):position = cls.split('-')x = int(position[2]) - 1y = int(position[3]) - 1try:value = int(tile.text) if tile.text else 0except ValueError:value = 0board[y][x] = valuereturn boarddef print_board(board):for row in board:print('\t'.join(map(str, row)))print()def calculate_position_score(board):"""计算基于位置的得分"""score = 0for i in range(4):for j in range(4):if board[i][j] != 0:score += board[i][j] * POSITION_WEIGHTS[i][j]return scoredef calculate_continuity(board):"""计算数字的连续性"""score = 0# 水平连续性for i in range(4):for j in range(3):if board[i][j] != 0 and board[i][j + 1] != 0:if board[i][j] == board[i][j + 1]:score += board[i][j] * 2elif abs(board[i][j] - board[i][j + 1]) == 1:score += min(board[i][j], board[i][j + 1])# 垂直连续性for i in range(3):for j in range(4):if board[i][j] != 0 and board[i + 1][j] != 0:if board[i][j] == board[i + 1][j]:score += board[i][j] * 2elif abs(board[i][j] - board[i + 1][j]) == 1:score += min(board[i][j], board[i + 1][j])return scoredef calculate_monotonicity(board):"""计算单调性(数字是否按顺序排列)"""score = 0# 水平单调性for i in range(4):for j in range(3):if board[i][j] >= board[i][j + 1]:score += 1# 垂直单调性for i in range(3):for j in range(4):if board[i][j] >= board[i + 1][j]:score += 1return scoredef evaluate_move(board, move):"""评估移动后的棋盘状态"""new_board = simulate_move(board, move)if new_board == board:  # 如果移动没有改变棋盘,返回负无穷return float('-inf')score = 0empty_cells = 0max_tile = 0# 计算空格数量for row in new_board:empty_cells += row.count(0)# 找出最大数字for row in new_board:max_tile = max(max_tile, max(row))# 计算位置得分position_score = calculate_position_score(new_board)# 计算连续性得分continuity_score = calculate_continuity(new_board)# 计算单调性得分monotonicity_score = calculate_monotonicity(new_board)# 计算总分(调整权重)score = (empty_cells * 200 +  # 空格权重增加max_tile * 100 +  # 最大数字权重增加position_score * 50 +  # 位置权重continuity_score * 30 +  # 连续性权重monotonicity_score * 20)  # 单调性权重return scoredef simulate_move(board, move):"""模拟移动并返回新的棋盘状态"""new_board = [row[:] for row in board]merged = [[False] * 4 for _ in range(4)]if move == Keys.ARROW_DOWN:# 向下移动for j in range(4):for i in range(2, -1, -1):if new_board[i][j] != 0:row = iwhile row < 3 and (new_board[row + 1][j] == 0 or(new_board[row + 1][j] == new_board[row][j] andnot merged[row + 1][j])):if new_board[row + 1][j] == 0:new_board[row + 1][j] = new_board[row][j]new_board[row][j] = 0else:new_board[row + 1][j] *= 2new_board[row][j] = 0merged[row + 1][j] = Truerow += 1elif move == Keys.ARROW_LEFT:# 向左移动for i in range(4):for j in range(1, 4):if new_board[i][j] != 0:col = jwhile col > 0 and (new_board[i][col - 1] == 0 or(new_board[i][col - 1] == new_board[i][col] andnot merged[i][col - 1])):if new_board[i][col - 1] == 0:new_board[i][col - 1] = new_board[i][col]new_board[i][col] = 0else:new_board[i][col - 1] *= 2new_board[i][col] = 0merged[i][col - 1] = Truecol -= 1elif move == Keys.ARROW_RIGHT:# 向右移动for i in range(4):for j in range(2, -1, -1):if new_board[i][j] != 0:col = jwhile col < 3 and (new_board[i][col + 1] == 0 or(new_board[i][col + 1] == new_board[i][col] andnot merged[i][col + 1])):if new_board[i][col + 1] == 0:new_board[i][col + 1] = new_board[i][col]new_board[i][col] = 0else:new_board[i][col + 1] *= 2new_board[i][col] = 0merged[i][col + 1] = Truecol += 1elif move == Keys.ARROW_UP:# 向上移动for j in range(4):for i in range(1, 4):if new_board[i][j] != 0:row = iwhile row > 0 and (new_board[row - 1][j] == 0 or(new_board[row - 1][j] == new_board[row][j] andnot merged[row - 1][j])):if new_board[row - 1][j] == 0:new_board[row - 1][j] = new_board[row][j]new_board[row][j] = 0else:new_board[row - 1][j] *= 2new_board[row][j] = 0merged[row - 1][j] = Truerow -= 1return new_boarddef get_best_move(board):"""获取最佳移动方向"""best_score = float('-inf')best_move = None# 评估每个可能的移动for move in MOVES:score = evaluate_move(board, move)if score > best_score:best_score = scorebest_move = move# 如果没有找到有效的移动,返回默认移动if best_move is None:return Keys.ARROW_DOWNreturn best_movedef play_2048():driver = init_driver()try:driver.get(GAME_URL)time.sleep(2)game_board = driver.find_element(By.TAG_NAME, 'body')while True:board = get_board_state(driver)print_board(board)# 获取最佳移动方向best_move = get_best_move(board)game_board.send_keys(best_move)time.sleep(random.uniform(*DELAY_RANGE))try:replay_button = driver.find_element(By.CLASS_NAME, 'replay-button')print("游戏结束!最终得分:", driver.find_element(By.CLASS_NAME, 'score-board').text.split()[-1])replay_button.click()time.sleep(2)except Exception:continueexcept KeyboardInterrupt:print("用户手动停止游戏")finally:driver.quit()if __name__ == "__main__":play_2048()

十二、总结

程序能够实现基本的游戏功能,在复杂局面下的表现还有提升空间,只玩到1024,离2048还差一些,读者可以通过进一步优化算法和评估系统,提高游戏表现,自己试试吧。

喜欢这篇文章的话记得点赞收藏加关注哦!!


http://www.ppmy.cn/ops/166218.html

相关文章

【Python办公】Excel通用匹配工具(双表互匹)

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 👍 该系列文章专栏:请点击——>Python办公自动化专…

【CSS】一、基础选择器

文章目录 1、CSS2、CSS的引入方式3、选择器3.1 标签选择器3.2 类选择器3.3 id选择器3.4 通配符选择器 4、练习&#xff1a;画盒子 1、CSS CSS&#xff0c;Cascading Style Sheets&#xff0c;层叠样式表&#xff0c;是一种样式表语言&#xff0c;用于表述HTML的呈现&#xff0…

使用Qt创建悬浮窗口

在Qt中创建悬浮窗口&#xff08;如无边框、可拖动的浮动面板或提示框&#xff09;可以通过以下方法实现。以下是几种常见场景的解决方案&#xff1a; 方法1&#xff1a;使用无边框窗口 鼠标事件拖动 适用于自定义浮动工具窗口&#xff08;如Photoshop的工具栏&#xff09;。 …

[目标检测] 训练之前要做什么

背景&#xff1a;训练一个Yolo8模型&#xff0c;在训练之前&#xff0c;数据集的处理是影响效果的关键因素。 Step1 定义规则 什么是人/车&#xff0c;比如人的话可能是站着的人&#xff0c;如果是骑电动车/自行车就不算是人。 Step2 收集数据集 1. 自己标注。如果是自己标…

每日一题——63. 不同路径 II

题目链接&#xff1a;63. 不同路径 II - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m obstacleGrid.size();int n obstacleGrid[0].size();…

探索Trae:Cursor的完美替代,Claude-3.5-Sonnet与GPT-4o免费体验

2025年1月 —— 字节跳动&#xff08;TikTok 的母公司&#xff09;推出了 Trae&#xff0c;这款创新的 AI 驱动代码编辑器&#xff0c;旨在大幅提升开发者的工作效率。Trae 将强大的人工智能与简洁直观的界面结合&#xff0c;帮助开发者更快速、轻松地编写、调试和优化代码。 …

【新品解读】直采+异构,看 RFSoC FPGA 开发板 AXW49 如何应对射频信号处理高要求

在追求更快、更稳的无线通信路上&#xff0c;传统射频架构深陷带宽-功耗-成本的“不可能三角”&#xff1a;带宽每翻倍&#xff0c;系统复杂度与功耗增幅远超线性增长。传统方案通过“分立式功放多级变频链路JESD204B 接口”的组合试图平衡性能与成本&#xff0c;却难以满足实时…

Redis7——进阶篇(六)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…