首页 编程教程正文

[原创]控制雷电模拟器玩2048游戏算法优化[附成果和源代码]

piaodoo 编程教程 2020-02-22 22:06:11 901 0 python教程

本文来源吾爱破解论坛

此篇帖子需要参考:
1 雷电模拟器说明
2 在雷电模拟器玩2048
这两篇帖子实现了在雷电模拟器上面玩2048,但是这是一个没有灵魂的程序,没有什么智能
所以本篇主要讨论算法,给这个程序以灵魂
按照之前两篇帖子的方法,思考深度即使加到5,也基本就是1000~3000分左右,基本就是小朋友的水平
观察算法,我们可以看到代码总体的思路是递归
每次考虑四个方向移动后的情况,然后计算空格的数量
努力在四个方向的推演矩阵中,找到那个空位最多的方向

但是这种方式有一个缺点:算法会优先合成两对2,而不会理会即将合成的64
实际在游戏的时候,如果人来决策,则会合成64,而不是2.因为2很容易合成,但是64却无法随时找到机会合成
从这个角度思考,应该优先合成出大的那个,而不是合成2

所以这个算法的关键在于评分函数,也就是get_null_count函数
如果让这个评分函数偏向于合成大数,那么就可以让脚本像人一样去移动
经过反复测试,目前我找到了一个得分最高的评分函数[见图片附件]
现在将源码展示如下:
[Python] 纯文本查看 复制代码

import time

from Dnconsole import Dnconsole
import cv2 as cv


def compare_status(st0: list, st1: list) -> bool:  # True相同 False不同
    if len(st0) != len(st1):
        return False
    for i in range(len(st0)):
        if len(st0[i]) != len(st1[i]):
            return False
        for j in range(len(st0[i])):
            if st0[i][j] != st1[i][j]:
                return False
    return True


def copy_statu(status: list) -> list:
    result = list()
    for line in status:
        result.append([line[0], line[1], line[2], line[3]])
    return result


def read_num(area: tuple, screen: str, template: list) -> int:
    scr = cv.imread(screen)
    cur_area = scr[area[1]:area[3], area[0]:area[2]]  # 前面是y轴 后面是x轴
    for i, tmp in enumerate(template):
        tp = cv.imread(tmp)
        try:
            result = cv.matchTemplate(cur_area, tp, cv.TM_SQDIFF_NORMED)
            min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
            if min_val < 0.001:
                return int(template[i].replace('2048/', '').replace('.png', ''))
        except cv.error:
            continue
    return -1


def get_cur_status() -> list:
    result = list()
    result.append([0, 0, 0, 0])
    result.append([0, 0, 0, 0])
    result.append([0, 0, 0, 0])
    result.append([0, 0, 0, 0])
    pos = [30, 260, 240, 470]  # , [300, 260, 510, 470], [570, 260, 780, 470], [840, 260, 1050, 470]]
    template = ['2048/0.png', '2048/2.png', '2048/4.png', '2048/8.png', '2048/16.png', '2048/32.png',
                '2048/64.png', '2048/128.png', '2048/256.png', '2048/512.png', '2048/1024.png']
    screen = 'E:/Project/PyCharmProj/Script/2048/share/2048.png'
    for i in range(4):
        for j in range(4):
            area = (pos[0] + 270 * j, pos[1] + 270 * i, pos[2] + 270 * j, pos[3] + 270 * i)
            result[i][j] = read_num(area, screen, template)
    return result


def swipe_up():
    Dnconsole.swipe(0, (540, 1280), (540, 260))
    print('上移')


def swipe_down():
    Dnconsole.swipe(0, (540, 260), (540, 1280))
    print('下移')


def swipe_left():
    Dnconsole.swipe(0, (1000, 770), (100, 770))
    print('左移')


def swipe_right():
    Dnconsole.swipe(0, (100, 770), (1000, 770))
    print('右移')


def get_score(status: list) -> int:
    score = 0
    if len(status) == 0:
        return score
    for row in status:
        for node in row:
            if node == 0:
                continue
            score += int(node * node)
    return score


def get_null_count(status: list) -> tuple:
    if len(status) == 0:
        # print('此方向移动无效')
        return -1, -1
    result = 0
    max_num = 0
    for row in status:
        for node in row:
            if node == 0:
                result += 1
            if node > max_num:
                max_num = node
    return result, max_num


def get_sum(line: list) -> int:
    total = 0
    for i in range(len(line)):
        total += line[i]
    return total


def compress_line(line: list) -> list:
    # 压实一条数据
    length = len(line)
    result = line[:]
    if get_sum(line) == 0:  # 全零则直接返回
        return line
    for i in range(length):
        if get_sum(result[i:]) == 0:
            break
        while result[i] == 0:
            for j in range(i, length - 1):
                result[j] = result[j + 1]
                result[j + 1] = 0
    return result


def combine_line(line: list) -> list:
    # 合并一条数据
    length = len(line)
    result = line[:]
    for i in range(length - 1):
        if result[i] == result[i + 1]:
            result[i] *= 2
            result[i + 1] = 0
    return result


def deduction_up(status: list) -> list:
    result = copy_statu(status)
    # 先把数据压实,再合并
    for i in range(4):
        line = [result[0][i], result[1][i], result[2][i], result[3][i]]
        line = compress_line(line)  # 压实(去掉为0的格子)
        line = combine_line(line)  # 合并
        line = compress_line(line)  # 压实
        result[0][i], result[1][i], result[2][i], result[3][i] = line[0], line[1], line[2], line[3]
    if compare_status(result, status):
        return []
    return result


def deduction_down(status: list) -> list:
    result = copy_statu(status)
    # 先把数据压实,再合并
    for i in range(4):
        line = [result[3][i], result[2][i], result[1][i], result[0][i]]
        line = compress_line(line)  # 压实(去掉为0的格子)
        line = combine_line(line)  # 合并
        line = compress_line(line)  # 压实
        result[3][i], result[2][i], result[1][i], result[0][i] = line[0], line[1], line[2], line[3]
    if compare_status(result, status):
        return []
    return result


def deduction_left(status: list) -> list:
    result = copy_statu(status)
    # 先把数据压实,再合并
    for i in range(4):
        line = [result[i][0], result[i][1], result[i][2], result[i][3]]
        line = compress_line(line)  # 压实(去掉为0的格子)
        line = combine_line(line)  # 合并
        line = compress_line(line)  # 压实
        result[i][0], result[i][1], result[i][2], result[i][3] = line[0], line[1], line[2], line[3]
    if compare_status(result, status):
        return []
    return result


def deduction_right(status: list) -> list:
    result = copy_statu(status)
    # 先把数据压实,再合并
    for i in range(4):
        line = [result[i][3], result[i][2], result[i][1], result[i][0]]
        line = compress_line(line)  # 压实(去掉为0的格子)
        line = combine_line(line)  # 合并
        line = compress_line(line)  # 压实
        result[i][3], result[i][2], result[i][1], result[i][0] = line[0], line[1], line[2], line[3]
    if compare_status(result, status):
        return []
    return result


def get_direction(status: list, deep: int) -> tuple:
    func = [deduction_up, deduction_down, deduction_left, deduction_right]
    score = 0
    direction = -1
    for i in range(4):
        result = func[i](status)
        if compare_status(result, status):
            result = []
        s = get_score(result)  # 获取本次操作的评价
        if deep > 0 and len(result) > 0:
            d, s0 = get_direction(result, deep - 1)  # 基于本次操作,获取下次移动的评价
            s += s0
        if s > score:
            direction = i
            score = s
    return direction, score


def run():
    if Dnconsole.is_running(0) is False:
        Dnconsole.launch(0)
    for i in range(120):
        if Dnconsole.is_running(0):
            time.sleep(10)
            break
        time.sleep(1)
    Dnconsole.invokeapp(0, 'com.digiplex.game')
    end_status = [[-1, -1, -1, -1], [-1, -1, -1, -1], [-1, -1, -1, -1], [-1, -1, -1, -1]]
    if Dnconsole.wait_activity(0, 'com.digiplex.game/.MainActivity', 20) is True:
        while True:
            time.sleep(1)
            Dnconsole.dnld(0, 'screencap -p /sdcard/Pictures/2048.png')
            status = get_cur_status()
            if compare_status(end_status, status):
                print('结束')
                break
            print('status=', status)
            for line in status:
                print(line)
            direction, score = get_direction(status, 5)
            if direction == -1:
                print('结束')
                return
            swipe = [swipe_up, swipe_down, swipe_left, swipe_right]
            swipe[direction]()

后续的改进方向:
一个是调整得分函数get_score
目前仅是一个逻辑,单纯追求合成更大的数
还可以在这个逻辑下,追加一个空格数更大的逻辑
另外一个是采用机器学习来替代这个得分函数
但是这个需要大量的标注数据,或者一个受控制的游戏(一个能够每秒中运行上百次的游戏)
如果有人知道2048游戏产生零散方块的算法,可以联系我,我后面可以考虑引入TensorFlow来进行机器学习解答,如果能够达到更高的分数,我会分享出代码

Screenshot_2018-12-21-21-53-39.png (127.8 KB, 下载次数: 3)

下载附件  保存到相册

2018-12-21 22:10 上传

Screenshot_2018-12-21-21-53-39.png

版权声明:

本站所有资源均为站长或网友整理自互联网或站长购买自互联网,站长无法分辨资源版权出自何处,所以不承担任何版权以及其他问题带来的法律责任,如有侵权或者其他问题请联系站长删除!站长QQ754403226 谢谢。

有关影视版权:本站只供百度云网盘资源,版权均属于影片公司所有,请在下载后24小时删除,切勿用于商业用途。本站所有资源信息均从互联网搜索而来,本站不对显示的内容承担责任,如您认为本站页面信息侵犯了您的权益,请附上版权证明邮件告知【754403226@qq.com】,在收到邮件后72小时内删除。本文链接:https://www.piaodoo.com/7531.html

搜索