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

文章目录

一、创作背景

二、效果图

三、准备工作

[1. 安装Chrome和Chrome Driver](#1. 安装Chrome和Chrome Driver)

[2. 安装Python库](#2. 安装Python库)

四、代码说明

[‌1. init_driver 函数‌](#‌1. init_driver 函数‌)

[2. play_2048 函数‌](#2. play_2048 函数‌)

五、完整代码

六、改进版本

七、主要模块

八、核心算法分析

[1. 棋盘状态获取](#1. 棋盘状态获取)

[2. 位置权重系统](#2. 位置权重系统)

[3. 连续性评估](#3. 连续性评估)

[4. 单调性评估](#4. 单调性评估)

[5. 移动模拟系统](#5. 移动模拟系统)

九、评估系统

[1. 评估标准](#1. 评估标准)

[2. 决策机制](#2. 决策机制)

十、性能优化

[1. 延迟控制](#1. 延迟控制)

[2. 错误处理](#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 driver

def 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:
                # 如果找不到"重玩"按钮,则继续游戏循环
                continue
    except KeyboardInterrupt:
        print("用户手动停止游戏")
    finally:
        driver.quit()

if __name__ == "__main__":
    play_2048()

六、改进版本

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

主要特点包括:

  • 采用启发式评估系统进行决策

  • 实现了基于位置的权重系统

  • 考虑了数字的连续性和单调性

  • 具有完善的错误处理机制

七、主要模块

  1. 浏览器控制模块
  • 初始化WebDriver

  • 页面元素定位

  • 键盘事件模拟

  1. 游戏状态获取模块
  • 棋盘状态解析

  • 数据转换和存储

  1. 决策系统模块
  • 移动模拟

  • 状态评估

  • 最优移动选择

  1. 评估系统模块
  • 位置权重计算

  • 连续性评估

  • 单调性评估

八、核心算法分析

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]) - 1
                y = int(position[3]) - 1
                try:
                    value = int(tile.text) if tile.text else 0
                except ValueError:
                    value = 0
                board[y][x] = value
    return 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 = 0
    for 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] * 2
    elif 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 = 0
    for i in range(4):
    for j in range(3):
    if board[i][j] >= board[i][j+1]:
    score += 1
    return 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
  • 保持棋盘有足够空间

  • 避免过早填满

  1. 最大数字:100
  • 鼓励产生更大的数字

  • 提高游戏得分

  1. 位置权重:50
  • 优化数字分布

  • 保持大数字在角落

  1. 连续性:30
  • 提高合并效率

  • 保持数字相邻

  1. 单调性:20
  • 保持数字有序

  • 便于后续合并

2. 决策机制

实现细节:

  1. 遍历所有可能的移动方向

  2. 评估每个移动的得分

  3. 选择得分最高的移动

  4. 默认返回向下移动

    def 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 = score
    best_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 driver


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]) - 1
                y = int(position[3]) - 1
                try:
                    value = int(tile.text) if tile.text else 0
                except ValueError:
                    value = 0
                board[y][x] = value
    return board


def print_board(board):
    for row in board:
        print('\t'.join(map(str, row)))
    print()


def calculate_position_score(board):
    """计算基于位置的得分"""
    score = 0
    for i in range(4):
        for j in range(4):
            if board[i][j] != 0:
                score += board[i][j] * POSITION_WEIGHTS[i][j]
    return score


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] * 2
                elif 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] * 2
                elif abs(board[i][j] - board[i + 1][j]) == 1:
                    score += min(board[i][j], board[i + 1][j])

    return score


def 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 += 1

    return score


def evaluate_move(board, move):
    """评估移动后的棋盘状态"""
    new_board = simulate_move(board, move)
    if new_board == board:  # 如果移动没有改变棋盘,返回负无穷
        return float('-inf')

    score = 0
    empty_cells = 0
    max_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 score


def 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 = i
                    while row < 3 and (new_board[row + 1][j] == 0 or
                                       (new_board[row + 1][j] == new_board[row][j] and
                                        not merged[row + 1][j])):
                        if new_board[row + 1][j] == 0:
                            new_board[row + 1][j] = new_board[row][j]
                            new_board[row][j] = 0
                        else:
                            new_board[row + 1][j] *= 2
                            new_board[row][j] = 0
                            merged[row + 1][j] = True
                        row += 1

    elif move == Keys.ARROW_LEFT:
        # 向左移动
        for i in range(4):
            for j in range(1, 4):
                if new_board[i][j] != 0:
                    col = j
                    while col > 0 and (new_board[i][col - 1] == 0 or
                                       (new_board[i][col - 1] == new_board[i][col] and
                                        not merged[i][col - 1])):
                        if new_board[i][col - 1] == 0:
                            new_board[i][col - 1] = new_board[i][col]
                            new_board[i][col] = 0
                        else:
                            new_board[i][col - 1] *= 2
                            new_board[i][col] = 0
                            merged[i][col - 1] = True
                        col -= 1

    elif move == Keys.ARROW_RIGHT:
        # 向右移动
        for i in range(4):
            for j in range(2, -1, -1):
                if new_board[i][j] != 0:
                    col = j
                    while col < 3 and (new_board[i][col + 1] == 0 or
                                       (new_board[i][col + 1] == new_board[i][col] and
                                        not merged[i][col + 1])):
                        if new_board[i][col + 1] == 0:
                            new_board[i][col + 1] = new_board[i][col]
                            new_board[i][col] = 0
                        else:
                            new_board[i][col + 1] *= 2
                            new_board[i][col] = 0
                            merged[i][col + 1] = True
                        col += 1

    elif move == Keys.ARROW_UP:
        # 向上移动
        for j in range(4):
            for i in range(1, 4):
                if new_board[i][j] != 0:
                    row = i
                    while row > 0 and (new_board[row - 1][j] == 0 or
                                       (new_board[row - 1][j] == new_board[row][j] and
                                        not merged[row - 1][j])):
                        if new_board[row - 1][j] == 0:
                            new_board[row - 1][j] = new_board[row][j]
                            new_board[row][j] = 0
                        else:
                            new_board[row - 1][j] *= 2
                            new_board[row][j] = 0
                            merged[row - 1][j] = True
                        row -= 1

    return new_board


def 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 = score
            best_move = move

    # 如果没有找到有效的移动,返回默认移动
    if best_move is None:
        return Keys.ARROW_DOWN

    return best_move


def 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:
                continue

    except KeyboardInterrupt:
        print("用户手动停止游戏")
    finally:
        driver.quit()


if __name__ == "__main__":
    play_2048()

十二、总结

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

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

相关推荐
that's boy22 分钟前
谷歌Gemini 2.0 Flash重磅更新:图文融合,初现AGI曙光
人工智能·chatgpt·openai·midjourney·agi·gemini·deepseek
終不似少年遊*25 分钟前
实践-给图片右下角加opencv-logo
人工智能·python·opencv·机器学习·计算机视觉
今天吃什么了29 分钟前
矩阵幂(矩阵k次幂)
人工智能·算法·矩阵
四口鲸鱼爱吃盐1 小时前
CVPR2024 | TT3D | 物理世界中可迁移目标性 3D 对抗攻击
人工智能·深度学习·机器学习·3d·对抗样本
琑952 小时前
Next.js项目MindAI教程 - 第四章:用户认证系统
开发语言·javascript·数据库·人工智能·mysql·typescript·node.js
pen-ai2 小时前
【NLP】 8. 处理常见词(Stopwords)的不同策略
人工智能·自然语言处理
程序趣谈4 小时前
算法随笔_74: 不同路径_1
数据结构·python·算法
乱世刀疤5 小时前
AI绘画软件Stable Diffusion详解教程(11):图生图进阶篇(局部用上传蒙版重绘)
人工智能·ai作画·stable diffusion
Cheng_08295 小时前
LLM自动化评测
python