Python3 实现QQ游戏连连看游戏辅助
- 连连看(零)—— 前记
- 连连看(一)—— 你看,这是截图啊
- 连连看(二)—— 哦吼,PIL + CV2 + Numpy 假图像识别构建矩阵
- 连连看(三)—— 拐了拐了啊,卖拐啦(连线规则)
- 连连看(四)—— 你看我这鼠标比用户还真(Pymouse 的替身)
- 连连看(五)—— 后记 + 成果展示
目录
0、本篇前言
不难发现,截得的图片中有很多都是蓝色的空白,这些也就是两图片连线之间的可行性路径,这是十分重要的,那么在将图片转化为矩阵时,区分空白和不同的图片是很重要的。
本文使用使用 opencv-python 进行图像处理——读取像素点,使用 numpy 辅助。为了方便起见,使用一维长矩阵,最后使用 reshape 将其变为 11*19 和连连看界面相对应的矩阵。
1、判断是否为空&构造矩阵
首先我们先读取一张空白的图片(其实是纯蓝色的)看一下它的中间的像素点的颜色:
# 先找一个空白的图片,将他的序号对应进去进行读取
im = cv.imread("pic0.png")
print(im[15, 16])
这样我们得到了空白蓝的值:
# 三元组值
[112 76 48]
接下来就是遍历了:
# 将图片转为矩阵阵列
def draw_map(self):
# 判断是否为空
def is_empty(arr: np.array) -> bool:
return (arr == np.array([112, 76, 48])).all()
for i in range(1, 210):
im = cv.imread("pic%s.png" % i)
if is_empty(im[15, 16]):
self.game_map[i - 1] = -1
哈哈,是不是又看不懂的地方了,没错,我们使用的数据结构如下:
class game:
def __init__(self):
self.game_map = np.zeros(209, dtype=int) # 一阶矩阵,后面使用reshape将其置为11*19的矩阵
self.total = 1 # 图片种类总计
self.win = False # 游戏胜利
self.bros = [] # 一个相同图片的兄弟们 [后面再讲解]
self.fam = {} # 每一个相同图片的大家庭 [后面再讲解]
self.steps = [] # 消除步骤 [后面再讲解]
self.draw_map() # 绘制矩阵
self.family() # [后面再讲解]
2、判断是否相同&构造矩阵
当我们读取二百多张图片之后再重新进行相同图片识别的话我们就需要读取四百多张图片了,这样很不划算,那么我们不如就一起,添加 else 语句把空白图片和图片相同与否一起进行判别。
思路:我们取五个像素点 im[5, 16], im[15, 16], im[20, 16], im[15, 6], im[15, 26] 组成的一个矩阵作为一张图片的抽象,若这连长图片的抽象矩阵完全相同,则认为是同一张图片,否则认为不同。
PS:曾经尝试过取四个像素点(不含 im[20, 16]),导致检查的时候发现有误,【布】和【黑桃】被识别为同一张图片:
这是由于像素点选取的位置巧合地都为纯黑色,因此增加一个中间的像素点加以区别。(或者可以换所取得像素点,但是,害,懒嘛,就直接肉眼观察法找到一个不一样的地方做区分了。)
def draw_map(self):
# 判断是否为空
def is_empty(arr: np.array) -> bool:
return (arr == np.array([112, 76, 48])).all()
print("[step2]:Drawing game map...")
# pics 用于存放已经识别的图片抽象数组
pics = [np.array([0])]
# 无图像位置中心点 [15, 16] 三元组值 [112 76 48]
for i in range(1, 210):
im = cv.imread("pic%s.png" % i)
if is_empty(im[15, 16]):
self.game_map[i - 1] = -1
else:
# im[5, 16], im[15, 16], im[20, 16], im[15, 6], im[15, 26]
temp0 = np.append(np.append(im[5, 16], im[15, 16]), np.append(im[15, 16], im[15, 26]))
temp = np.append(im[20, 16], temp0)
# print(temp)
for num in range(0, len(pics)):
if (pics[num] == temp).all():
# 该图片是 pics 里的图片,将序号赋给此图片
self.game_map[i - 1] = num
break
else:
# 该图片是新图片,赋予新值,总数 total 加一
self.game_map[i - 1] = self.total
pics.append(temp)
self.total += 1
这样我们就得到了完整的一维矩阵,最后 reshape ,print 出来刺激一下
self.game_map = self.game_map.reshape(11, 19)
[[-1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 2 -1 -1 -1 -1 -1]
[-1 -1 -1 3 4 5 -1 -1 -1 -1 -1 -1 6 3 7 -1 -1 -1 -1]
[-1 -1 8 9 4 8 10 -1 -1 -1 -1 2 11 12 6 13 -1 -1 -1]
[-1 5 6 4 12 11 10 2 -1 -1 14 5 15 13 13 7 3 -1 -1]
[-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]
[-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]
[-1 16 16 17 13 15 18 18 -1 -1 4 8 17 5 18 7 9 -1 -1]
[-1 -1 17 16 12 10 3 -1 -1 -1 -1 7 10 1 14 16 -1 -1 -1]
[-1 -1 -1 18 6 17 -1 -1 -1 -1 -1 -1 12 8 15 -1 -1 -1 -1]
[-1 -1 -1 -1 15 -1 -1 -1 -1 -1 -1 -1 -1 2 -1 -1 -1 -1 -1]
[-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]]
PS:后面的连线规则可以直接使用该矩阵,不需要打开游戏、截图等操作了。 将 self.draw_map() 换为下方的矩阵赋值,将总数 total 直接赋值为矩阵中的最大值+1(32 = 31 + 1)
【附:完整代码】
class game:
def __init__(self):
self.game_map = np.zeros(209, dtype=int)
self.total = 1
# self.total = 32 # 图片种类总计
self.win = False # 游戏胜利
self.bros = [] # 一个相同图片的兄弟们
self.fam = {} # 每一个相同图片的大家庭
self.steps = [] # 消除步骤
self.draw_map()
# self.game_map = np.array(
# [[1, 2, 3, 4, 5, 1, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 9, 7],
# [16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3],
# [17, -1, 18, 19, 20, 21, 22, 2, 9, 12, 4, 7, 16, 12, 23, 17, 6, -1, 5],
# [18, -1, 24, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, 5],
# [25, -1, 2, -1, 22, 20, 16, 19, 26, 23, 4, 1, 27, 8, -1, -1, 14, -1, 6],
# [16, -1, 28, -1, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, 17],
# [3, -1, 19, -1, 15, 3, 21, 24, 11, 5, 18, 28, 27, 30, 25, 22, 10, -1, 27],
# [17, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19],
# [11, -1, 31, 29, 20, 13, 4, 13, 14, 11, 10, 24, 7, 18, 14, 12, 30, 8, 28],
# [31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
# [9, 6, 10, 24, 27, 26, 2, 25, 28, 20, 23, 29, 22, 29, 31, 31, 13, 21, 25]])
print(self.game_map)
print("[Time]3....")
time.sleep(1)
print("[Time]2....")
time.sleep(1)
print("[Time]1....")
time.sleep(1)
print("[Time]Begin!")
self.family()
# 将图片转为矩阵阵列
def draw_map(self):
# 判断是否为空
def is_empty(arr: np.array) -> bool:
return (arr == np.array([112, 76, 48])).all()
print("[step2]:Drawing game map...")
pics = [np.array([0])]
# 无图像位置中心点 [15, 16] 三元组值 [112 76 48]
for i in range(1, 210):
im = cv.imread("pic%s.png" % i)
if is_empty(im[15, 16]):
self.game_map[i - 1] = -1
else:
# im[5, 16], im[15, 16], im[20, 16], im[15, 6], im[15, 26]
temp0 = np.append(np.append(im[5, 16], im[15, 16]), np.append(im[15, 16], im[15, 26]))
temp = np.append(im[20, 16], temp0)
# print(temp)
for num in range(0, len(pics)):
if (pics[num] == temp).all():
self.game_map[i - 1] = num
break
else:
self.game_map[i - 1] = self.total
pics.append(temp)
self.total += 1
self.game_map = self.game_map.reshape(11, 19)
print("[step2]:Drawing game map finished")
3、其他函数功能
# 消除图片更新 game_map
def boom(self, row1: int, col1: int, row2: int, col2: int):
self.game_map[row1, col1] = -1
self.game_map[row2, col2] = -1
# 游戏胜利判断
def is_win(self):
res = np.where(self.game_map == -1)
return len(res[1]) == 209
【本篇完】
【下一篇:连连看(三)—— 拐了拐了啊,卖拐啦(连线规则)】