好友
阅读权限10
听众
最后登录1970-1-1
|
此篇帖子需要参考:
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来进行机器学习解答,如果能够达到更高的分数,我会分享出代码
|
-
免费评分
-
参与人数 1 | 吾爱币 +5 |
热心值 +1 |
收起
理由
|
苏紫方璇
| + 5 |
+ 1 |
欢迎分析讨论交流,吾爱破解论坛有你更精彩! |
查看全部评分
|