本文来源吾爱破解论坛
此篇帖子需要参考:
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 上传
版权声明:
本站所有资源均为站长或网友整理自互联网或站长购买自互联网,站长无法分辨资源版权出自何处,所以不承担任何版权以及其他问题带来的法律责任,如有侵权或者其他问题请联系站长删除!站长QQ754403226 谢谢。
- 上一篇: python生成器学习
- 下一篇: 迷站论坛图片一键批量下载PY源码