Python3 實現QQ遊戲連連看遊戲輔助
- 連連看(零)—— 前記
- 連連看(一)—— 你看,這是截圖啊
- 連連看(二)—— 哦吼,PIL + CV2 + Numpy 假圖像識別構建矩陣
- 連連看(三)—— 拐了拐了啊,賣柺啦(連線規則)
- 連連看(四)—— 你看我這鼠標比用戶還真(Pymouse 的替身)
- 連連看(五)—— 後記 + 成果展示
目錄
0、本片前言
從這裏開始,我們可以按照上一篇文章中介紹的方法,手動賦值一個矩陣和 total ,不用每次調試運行都打開遊戲、截圖、保存、構建矩陣了。
本文使用的是這個矩陣,total = 32
[ [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 看結果
# 使用保存的矩陣和 total 初始化
linkup = game()
# 隨心所欲的想要測試的兩點座標帶入即可
print(linkup.vertical(3, 1, 4, 1))
print(linkup.two_corner(r1, c1, r2, c2))
1、水平方向
首先判斷是否在同一行,然後根據進行掃描兩點之間是否存在非空(非 -1)的點。
第一次寫的時候發現測試 (0, 5) 和 (0, 6) 這兩點的時候,發現明明是可以連接的,但是返回的卻是 False。
因爲有兩種特殊情況:
1、如果兩點相連接,導致在 for 循環一次都不運行。(使用 Pycharm 的 Debug 模式發現)
解決方案就是判斷同一行的兩點是否縱座標相鄰。如果兩個座標點所對應的矩陣中的值相同,可以連線。但這就引起了第二個特殊情況:其中一個點是 -1,另一個點爲大於零的數字。
2、其中一個點是 -1,另一個點爲大於零的數字。(主要是爲了後面的拐點連線判斷做鋪墊)
解決方案是用或操作對兩個點進行判斷,若其中一個爲 -1,可以連線。
# 判斷水平方向
def horizon(self, row1: int, col1: int, row2: int, col2: int) -> bool:
if row1 == row2:
for i in range(min(col1, col2) + 1, max(col1, col2)):
# print(self.game_map[row1, i])
if self.game_map[row1, i] != -1:
return False
# 解決 conjunction
if min(col1, col2) + 1 == max(col1, col2):
if (self.game_map[row1, col1] == -1) or (self.game_map[row2, col2] == -1):
return True
if self.game_map[row1, col1] == self.game_map[row2, col2]:
return True
return True
return False
2、垂直方向
與水平方向相同,舉一反三,不再贅述。
# 判斷垂直方向
def vertical(self, row1: int, col1: int, row2: int, col2: int) -> bool:
if col1 == col2:
for i in range(min(row1, row2) + 1, max(row1, row2)):
# print(self.game_map[i, col1])
if self.game_map[i, col1] != -1:
return False
# 解決 conjunction
if min(row1, row2) + 1 == max(row1, row2):
if (self.game_map[row1, col1] == -1) or (self.game_map[row2, col2] == -1):
return True
if self.game_map[row1, col1] == self.game_map[row2, col2]:
return True
return True
return False
3、一個拐角
增加兩個拐點 (cor_one_row, cor_one_col) = (row1, col2) 和 (cor_two_row, cor_two_col) = (row2, col1) 作爲輔助,不用擔心兩個點的位置關係,因爲在水平和垂直的判斷中,我們都使用了 max 和 min 函數進行修正。
那麼思路就是判斷:
1、point1 與 cor_one 之間是否可以水平連接,point2 與 cor_one 之間是否可以垂直連接。
2、point2 與 cor_two 之間是否可以水平連接,point1 與 cor_two 之間是否可以垂直連接。
其中一個可行,那麼 point1 與 point2 之間可以連線。
# 判斷一個拐點
def one_corner(self, row1: int, col1: int, row2: int, col2: int) -> bool:
cor_one_row, cor_one_col = row1, col2
cor_two_row, cor_two_col = row2, col1
if self.game_map[cor_one_row, cor_one_col] == -1:
if self.horizon(cor_one_row, cor_one_col, row1, col1) & self.vertical(cor_one_row, cor_one_col, row2, col2):
return True
if self.game_map[cor_two_row, cor_two_col] == -1:
if self.horizon(cor_two_row, cor_two_col, row2, col2) & self.vertical(cor_two_row, cor_two_col, row1, col1):
return True
return False
4、兩個拐角
詳細分析:
1、在 point1 的水平或垂直連線上任意一點可以與 point2 經過一個拐點的連線連接,即兩點間可以經過兩個拐點鏈接
2、在 point2 的水平或垂直連線上任意一點可以與 point1 經過一個拐點的連線連接,即兩點間可以經過兩個拐點鏈接
輔助函數:用於獲取兩目標點所在水平和垂直方向上的空白點(-1)
ps:此函數可以優化,大家可以自行修改優化,歡迎評論私信。
# 收集水平、垂直方向可行點
def collect_points(self, row: int, col: int) -> list:
points = []
# 向右方搜索
for i in range(col + 1, 19):
try:
if self.game_map[row, i] == -1:
points.append((row, i))
else:
break
except IndexError as err:
# print("Ignore col error.")
continue
# 向左方搜索
for i in range(col - 1, -1, -1):
try:
if self.game_map[row, i] == -1:
points.append((row, i))
else:
break
except IndexError as err:
# print("Ignore col error.")
continue
# 向上方搜索
for i in range(row - 1, -1, -1):
try:
if self.game_map[i, col] == -1:
points.append((i, col))
else:
break
except IndexError as err:
# print("Ignore row error.")
continue
# 向下方搜索
for i in range(row + 1, 19):
try:
if self.game_map[i, col] == -1:
points.append((i, col))
else:
break
except IndexError as err:
# print("Ignore row error.")
continue
return points
然後拿着這些點進行遍歷,看看是否存在可以經過一個拐點和另一個目標點連接的。存在,則可以兩拐點鏈接。
最終判斷兩個拐點的完整代碼:
# 判斷兩個拐點
def two_corner(self, row1: int, col1: int, row2: int, col2: int) -> bool:
for item in self.collect_points(row1, col1):
if self.one_corner(item[0], item[1], row2, col2):
return True
for item in self.collect_points(row2, col2):
if self.one_corner(item[0], item[1], row1, col1):
return True
return False
5、判斷是否可以連接
符合任意一種連接方式即可認定爲可以連接。
# 判斷是否可以連接
def erasable(self, row1: int, col1: int, row2: int, col2: int) -> bool:
flag_no_corner = (self.horizon(row1, col1, row2, col2)) or (self.vertical(row1, col1, row2, col2))
flag_corner = (self.one_corner(row1, col1, row2, col2)) or (self.two_corner(row1, col1, row2, col2))
return flag_no_corner or flag_corner
【本片完】
【下一篇:連連看(四)—— 你看我這鼠標比用戶還真(Pymouse 的替身)】