├── Figure_1.png ├── README.md ├── .gitattributes ├── .gitignore ├── main.py ├── util.py └── solve.py /Figure_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesRaynor67/QQ_game_lianliankan/HEAD/Figure_1.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QQ_game_lianliankan 2 | 纯python实现QQ游戏连连看外挂 3 | 4 | 环境: 5 | windows 6 | 要求: 7 | python3 + 程序中使用到的相关库(使用pip直接安装即可) 8 | 9 | 如何使用: 10 | 进入连连看游戏大厅后,运行main.py文件即可实现自动排位,游戏,一局结束后重新排位的功能。 11 | 可在代码中任意调整自动消去方块的速度。 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import win32gui 2 | import win32api 3 | import time 4 | from util import RGB2Int 5 | from util import Int2RGB 6 | from util import click 7 | from util import display_room_rect 8 | from util import enter_room_from_lobby 9 | from util import click_start 10 | from util import exit_room 11 | from util import get_lobby_hWnd 12 | from util import get_status 13 | from util import GameState 14 | from solve import solve_game_by_brute_force 15 | from solve import solve_game_with_grace 16 | 17 | def debug(): 18 | print("In debug") 19 | print(get_status()) 20 | exit_room() 21 | 22 | 23 | 24 | def main(): 25 | # 这个状态机实际上不健壮,万一出现进入房间超时等意外状态,程序即陷入错误 26 | status = GameState.GS_INLOBBY 27 | waitting_time = 0 28 | while True: 29 | if status == GameState.GS_INLOBBY: 30 | enter_room_from_lobby() 31 | elif status == GameState.GS_INROOM: 32 | if waitting_time == 0: 33 | click_start() 34 | waitting_time = 1 35 | elif waitting_time < 20: 36 | time.sleep(1) 37 | waitting_time += 1 38 | print("等待{}秒...".format(waitting_time)) 39 | else: 40 | exit_room() 41 | elif status == GameState.GS_INGAME: 42 | print("准备开始游戏") 43 | time.sleep(2) # 一开始方块没有出现,等待两秒 44 | # solve_game_by_brute_force() 45 | solve_game_with_grace() 46 | time.sleep(2) 47 | exit_room() 48 | 49 | pre_status = status 50 | status = get_status() 51 | print("现在状态:{}".format(status)) 52 | # 退出room内等待状态后清零计时 53 | if pre_status == GameState.GS_INROOM and status != GameState.GS_INROOM: 54 | waitting_time = 0 55 | # 遇到未知情况时尝试重新恢复窗口焦点恢复运行,无法保证必然有效 56 | if status == GameState.GS_UNKNOWN: 57 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 58 | if hWnd == 0: 59 | hWnd = get_lobby_hWnd() 60 | assert hWnd != 0 61 | win32gui.SetForegroundWindow(hWnd) 62 | win32gui.SetActiveWindow(hWnd) 63 | status = GameState.GS_INLOBBY 64 | continue 65 | else: 66 | win32gui.SetForegroundWindow(hWnd) 67 | win32gui.SetActiveWindow(hWnd) 68 | status = GameState.GS_INROOM 69 | 70 | if __name__ == '__main__': 71 | main() -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32con 3 | import win32gui 4 | import time 5 | import random 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from enum import Enum 9 | 10 | class GameState(Enum): 11 | GS_INGAME = 1 #处在游戏中 12 | GS_OVER = 2 #游戏超时,等待其他玩家完成消除并结束一场游戏 13 | GS_INROOM = 3 #在房间里等待游戏开始 14 | GS_CLOSED = 4 #游戏窗口被关闭(主动关闭或者被踢出房间 15 | GS_MINIMIZED = 5 #游戏窗口被最小化(无法获取颜色值) 16 | GS_INLOBBY = 6 # 在游戏大厅中 17 | GS_UNKNOWN = 7 #未定义状态(这是错误) 18 | 19 | def RGB2Int(red, green, blue): 20 | return red + green * 256 + blue * 256 * 256 21 | 22 | def Int2RGB(rgb): 23 | red = (rgb >> 16) & 255 24 | green = (rgb >> 8) & 255 25 | blue = rgb & 255 26 | return (red, green, blue) 27 | 28 | BACK_COLOR = RGB2Int(0x30,0x4C,0x70) 29 | DEBUG = True 30 | g_lobby_hWnd = 0 31 | 32 | def enter_room_from_lobby(): 33 | # 因为有title为 连连看 的隐藏窗口存在导致之前 34 | # 无法getPixel即发送鼠标信息失败问题 35 | # 我们需要获取visible的title为 连连看 的窗口 36 | while True: 37 | hWnd = get_lobby_hWnd() 38 | win32gui.SetForegroundWindow(hWnd) 39 | win32gui.SetActiveWindow(hWnd) 40 | click(hWnd, "enterRoom") 41 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 42 | if hWnd == 0: 43 | print("进入游戏房间失败,重新尝试进入") 44 | else: 45 | break 46 | 47 | def exit_room(): 48 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 49 | win32gui.SetForegroundWindow(hWnd) 50 | win32gui.SetActiveWindow(hWnd) 51 | click(hWnd, "exitRoom") 52 | time.sleep(3) 53 | 54 | def click_start(): 55 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 56 | win32gui.SetForegroundWindow(hWnd) 57 | win32gui.SetActiveWindow(hWnd) 58 | click(hWnd, "clickStart") 59 | 60 | def click(hWnd, action): 61 | delay_time = 0.1 62 | # 注意这里的点都是考虑过IDP拉伸后的位置 63 | if action == "enterRoom": 64 | x, y = 270, 152 65 | delay_time = 5 66 | elif action == "clickStart": 67 | x, y = 650, 570 68 | elif action == "exitRoom": # 点右上角的乂,退出房间 69 | x, y = 780, 10 70 | elif action == "resort": # 重排 71 | x, y = 650, 200 72 | delay_time = 8 + random.random() * 3 #等待提示文字消失,否则会影响下一步判断 73 | elif action == "click_dialog_yes": 74 | x, y = 820, 280 75 | # x, y = 0, 0 76 | else: 77 | print("Wrong action string input, exiting...") 78 | exit() 79 | print("Clicked: {}".format(win32gui.ClientToScreen(hWnd, (x, y)))) 80 | win32api.SetCursorPos(win32gui.ClientToScreen(hWnd, (x, y))) # important to show where is acturally clicked 81 | lParam = win32api.MAKELONG(x, y) 82 | win32gui.SendMessage(hWnd, win32con.WM_LBUTTONDOWN, 0, lParam) 83 | win32gui.SendMessage(hWnd, win32con.WM_LBUTTONUP, 0, lParam) 84 | time.sleep(delay_time) 85 | 86 | def display_room_rect(left_top_x, left_top_y, right_bot_x, right_bot_y): 87 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 88 | win32gui.SetForegroundWindow(hWnd) 89 | win32gui.SetActiveWindow(hWnd) 90 | hDC = win32gui.GetWindowDC(hWnd) 91 | 92 | rect = win32gui.GetWindowRect(hWnd) 93 | x = rect[0] 94 | y = rect[1] 95 | w = rect[2] - x 96 | h = rect[3] - y 97 | print("Window name: " + win32gui.GetWindowText(hWnd)) 98 | print("Window hDC: " + str(hDC)) 99 | print("x, y, w, h == " + str(x) + " " + str(y) + " " + str(w) + " " + str(h) + " ") 100 | image = np.zeros((right_bot_y - left_top_y + 1, right_bot_x - left_top_x + 1, 3)) 101 | for y in range(right_bot_y - left_top_y + 1): #h 102 | for x in range(right_bot_x - left_top_x + 1): #w 103 | image[y,x] = np.asarray(Int2RGB(win32gui.GetPixel(hDC, x + left_top_x, y + left_top_y))) / 255 104 | win32gui.ReleaseDC(hWnd, hDC) 105 | plt.imshow(image[..., ::-1]) # GetPixel得到的是BGR而不是RGB,所以需要转换一下 106 | plt.show() 107 | 108 | def enumHandler(hWnd, lParam): 109 | if win32gui.IsWindowVisible(hWnd) and win32gui.GetWindowText(hWnd) == "连连看": 110 | global g_lobby_hWnd 111 | g_lobby_hWnd = hWnd 112 | 113 | def get_lobby_hWnd(): 114 | # 在enumHandler中设置了g_lobby_hWnd作为返回值 115 | win32gui.EnumWindows(enumHandler, None) 116 | return g_lobby_hWnd 117 | 118 | def scan_game_board(): 119 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 120 | win32gui.SetForegroundWindow(hWnd) 121 | win32gui.SetActiveWindow(hWnd) 122 | win32api.SetCursorPos((10, 10)) # 移开鼠标防止潜在干扰(也许鼠标根本不会干扰?) 123 | 124 | hDC = win32gui.GetWindowDC(hWnd) 125 | board_width, board_height = 19, 11 126 | board = [[0 for w in range(board_width)] for h in range(board_height)] 127 | ptns = [] 128 | 129 | idx = 0 130 | for i in range(11): 131 | for j in range(19): 132 | # 每个方块取 4*4 个像素点 133 | ptn = [0 for j in range(16)] 134 | is_ptn = False 135 | for k in range(4): 136 | for l in range(4): 137 | ptn[k * 4 + l] = win32gui.GetPixel(hDC, 9 + j * 31 + 13 + l, 180 + i * 35 + 13 + k) 138 | if ptn[k * 4 + l] != BACK_COLOR: 139 | is_ptn = True 140 | # 判断是否为背景色 141 | if is_ptn is True: 142 | try: 143 | idx = ptns.index(ptn) 144 | board[i][j] = idx + 1 145 | except ValueError: 146 | ptns.append(ptn) 147 | board[i][j] = len(ptns) 148 | if DEBUG is True: 149 | print("共计图案种类: " + str(len(ptns))) 150 | for i in range(len(board)): 151 | print(board[i]) 152 | return board, len(ptns) # 权宜之计,为准确判断room状态提供线索 153 | 154 | def get_status(): 155 | # 按照道理,这个函数中不应该调用 win32gui.SetForegroundWindow(hWnd) 156 | # 和 win32gui.SetActiveWindow(hWnd) 这两个函数。因为函数名中的get 157 | # 暗示这个函数是没有副作用的 158 | # 但是考虑到我们希望room窗口存在即在最前,若room窗口不存在则lobby应该 159 | # 在最前。从这个角度看,这个函数中的行为又是没有问题的。 160 | # 但总归这样的设计是不利于未来改变/拓展的,目前先这么用吧。 161 | if win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") != 0: 162 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 163 | win32gui.SetForegroundWindow(hWnd) 164 | win32gui.SetActiveWindow(hWnd) 165 | hDC = win32gui.GetWindowDC(hWnd) 166 | while True: 167 | try: 168 | if win32gui.GetPixel(hDC, 10, 10) != RGB2Int(0xFF, 0xFF, 0xFF): 169 | break 170 | except: 171 | print("In get_status wait for 1 second...") 172 | time.sleep(1) # 有时会抽风,此时等待图像恢复 173 | # 判断右上角星球图案像素是否存在来判断是否在房间中 174 | if win32gui.GetPixel(hDC, 560, 74) == RGB2Int(144, 152, 72): 175 | return GameState.GS_INROOM 176 | # 判断右下角方框边缘像素是否存在来判断是否在游戏中 177 | elif win32gui.GetPixel(hDC, 605, 588) == RGB2Int(24, 52, 80): 178 | return GameState.GS_INGAME 179 | elif win32gui.GetPixel(hDC, 200, 315) == RGB2Int(0xF0, 0xF4, 0x00): 180 | return GameState.GS_OVER 181 | else: 182 | return GameState.GS_UNKNOWN 183 | elif get_lobby_hWnd() != 0: 184 | hWnd = get_lobby_hWnd() 185 | win32gui.SetForegroundWindow(hWnd) 186 | win32gui.SetActiveWindow(hWnd) 187 | return GameState.GS_INLOBBY 188 | else: 189 | return GameState.GS_UNKNOWN 190 | 191 | def debug(): 192 | print("In debug util.py") 193 | for_debug() 194 | 195 | 196 | def for_debug(): 197 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 198 | win32gui.SetForegroundWindow(hWnd) 199 | win32gui.SetActiveWindow(hWnd) 200 | hDC = win32gui.GetWindowDC(hWnd) 201 | 202 | print(Int2RGB(win32gui.GetPixel(hDC, 605, 588))) 203 | print(get_status()) 204 | win32gui.ReleaseDC(hWnd, hDC) 205 | 206 | # def enumDialogHandler(hWnd, lParam): 207 | # if win32gui.GetWindowText(hWnd) == "提示信息": 208 | # print(win32gui.GetWindowText(hWnd)) 209 | # print(hWnd) 210 | 211 | # def show_dialog_hWnd(): 212 | # # 在enumHandler中设置了g_lobby_hWnd作为返回值 213 | # win32gui.EnumWindows(enumDialogHandler, None) 214 | 215 | 216 | if __name__ == '__main__': 217 | debug() -------------------------------------------------------------------------------- /solve.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32con 3 | import win32gui 4 | import time 5 | import random 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | import math 9 | from util import GameState 10 | from util import get_status 11 | from util import RGB2Int 12 | from util import click 13 | from util import scan_game_board 14 | 15 | BURTE_FORCE_GAME_SPEED = 0.1 16 | GRACE_GAME_SPEED_CORRECTOR = 1 17 | 18 | def click_piece(x, y, speed_corrector): 19 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 20 | win32gui.SetForegroundWindow(hWnd) 21 | win32gui.SetActiveWindow(hWnd) 22 | transferred_x, transferred_y = 9 + x * 31 + 13, 180 + y * 35 + 13 23 | lParam = win32api.MAKELONG(transferred_x, transferred_y) 24 | win32gui.SendMessage(hWnd, win32con.WM_LBUTTONDOWN, 0, lParam) 25 | win32gui.SendMessage(hWnd, win32con.WM_LBUTTONUP, 0, lParam) 26 | time.sleep(BURTE_FORCE_GAME_SPEED + random.random() * speed_corrector) 27 | 28 | def solve_at_least_one_pair(board): 29 | # 经过观察,即使积分很高的人消去也很慢,故不考虑效率,选择最简单的穷举法 30 | # 即点选所有相同的方块试图消去,这样至少可以消去一对(除非已经无解) 31 | # 这样可能导致消去的效率变化比较大 32 | # 若想得到稳定解可以考虑进行图的遍历 33 | # (规则:连接线不多于 3 根直线,就可以成功将对子消除。) 34 | # 每拐一次弯将深度加一,深度超过2则放弃搜索即可 35 | tried_ids = [] 36 | for y in range(len(board)): 37 | for x in range(len(board[0])): 38 | if board[y][x] != 0 and board[y][x] not in tried_ids: 39 | # 开始处理一种新图案 40 | target_id = board[y][x] 41 | tried_ids.append(target_id) 42 | pos_x, pos_y = [], [] 43 | # 找到所有此种图案的坐标 44 | for row_num in range(len(board)): 45 | indexes = [i for i, ptn_id in enumerate(board[row_num]) if ptn_id == target_id] 46 | pos_x = pos_x + indexes 47 | pos_y = pos_y + [row_num for i in range(len(indexes))] 48 | # 开始尝试组合点击坐标 49 | assert len(pos_x) == len(pos_y) 50 | for first in range(len(pos_x)): 51 | for second in range(first + 1, len(pos_x)): 52 | # 尝试组合消除 53 | # print(pos_x[first], pos_y[first], pos_x[second], pos_y[second]) 54 | assert board[pos_y[first]][pos_x[first]] == board[pos_y[second]][pos_x[second]] 55 | piece_num = 11*19 - sum(row.count(0) for row in board) 56 | # 当piece_num>100时,其实程序的效率可能很低,应该加快尝试速度 57 | speed_corrector = 0 58 | if piece_num > 100: 59 | speed_corrector = -0.8 * BURTE_FORCE_GAME_SPEED 60 | elif piece_num > 40: 61 | speed_corrector = ((piece_num - 40)*(piece_num - 100) / -2700) - (0.8 * BURTE_FORCE_GAME_SPEED) 62 | else: 63 | speed_corrector = 0.4 # 后期适当降速 64 | click_piece(pos_x[first], pos_y[first], speed_corrector) 65 | click_piece(pos_x[second], pos_y[second], speed_corrector) 66 | 67 | def resort(): 68 | hWnd = win32gui.FindWindow(None, "QQ游戏 - 连连看角色版") 69 | win32gui.SetForegroundWindow(hWnd) 70 | win32gui.SetActiveWindow(hWnd) 71 | click(hWnd, "resort") 72 | 73 | def if_all_zeros(board): 74 | for row in range(len(board)): 75 | if any(ele != 0 for ele in board[row]): 76 | return False 77 | return True 78 | 79 | def solve_game_by_brute_force(): 80 | while True: 81 | 82 | if get_status() != GameState.GS_INGAME: 83 | break 84 | board, _ = scan_game_board() 85 | solve_at_least_one_pair(board) 86 | # 设置sleep(1)可能会遇到重排等情况导致new_board出现暂时性错误 87 | # 但是这种情况下多重复while循环几次即可 88 | time.sleep(1) 89 | if get_status() != GameState.GS_INGAME: 90 | break 91 | new_board, _ = scan_game_board() 92 | if if_all_zeros(new_board): 93 | print("全部方块已被消除完毕") 94 | print(new_board) 95 | break 96 | if board == new_board: 97 | print("无解,需要进行重排") 98 | resort() 99 | time.sleep(8) 100 | 101 | def search(board, visited, i, j, pre_direction, conner_count, target_i, target_j): 102 | if i < 0 or i > 10 or j < 0 or j > 18: 103 | return False, -1, -1 104 | if visited[i][j] is True or conner_count > 2 or (board[i][j] != board[target_i][target_j] and board[i][j] != 0): 105 | return False, -1, -1 106 | # 先确认是否i,j,conner_count合法再确认是否配对。 107 | # 注意最开始进入函数时不能立刻返回方块与自身配对。 108 | if board[i][j] == board[target_i][target_j] and (i != target_i or j != target_j): 109 | return True, i, j 110 | 111 | visited[i][j] = True 112 | # -1 first point; 0 up; 1 down; 2 left; 3 right 113 | if pre_direction == -1: 114 | solved_one, pair_i, pair_j = search(board, visited, i-1, j, 0, conner_count, target_i, target_j) 115 | if solved_one is True: 116 | return solved_one, pair_i, pair_j 117 | solved_one, pair_i, pair_j = search(board, visited, i+1, j, 1, conner_count, target_i, target_j) 118 | if solved_one is True: 119 | return solved_one, pair_i, pair_j 120 | solved_one, pair_i, pair_j = search(board, visited, i, j-1, 2, conner_count, target_i, target_j) 121 | if solved_one is True: 122 | return solved_one, pair_i, pair_j 123 | solved_one, pair_i, pair_j = search(board, visited, i, j+1, 3, conner_count, target_i, target_j) 124 | if solved_one is True: 125 | return solved_one, pair_i, pair_j 126 | else: 127 | if pre_direction == 0: 128 | solved_one, pair_i, pair_j = search(board, visited, i-1, j, 0, conner_count, target_i, target_j) 129 | if solved_one is True: 130 | return solved_one, pair_i, pair_j 131 | else: 132 | solved_one, pair_i, pair_j = search(board, visited, i-1, j, 0, conner_count+1, target_i, target_j) 133 | if solved_one is True: 134 | return solved_one, pair_i, pair_j 135 | if pre_direction == 1: 136 | solved_one, pair_i, pair_j = search(board, visited, i+1, j, 1, conner_count, target_i, target_j) 137 | if solved_one is True: 138 | return solved_one, pair_i, pair_j 139 | else: 140 | solved_one, pair_i, pair_j = search(board, visited, i+1, j, 1, conner_count+1, target_i, target_j) 141 | if solved_one is True: 142 | return solved_one, pair_i, pair_j 143 | if pre_direction == 2: 144 | solved_one, pair_i, pair_j = search(board, visited, i, j-1, 2, conner_count, target_i, target_j) 145 | if solved_one is True: 146 | return solved_one, pair_i, pair_j 147 | else: 148 | solved_one, pair_i, pair_j = search(board, visited, i, j-1, 2, conner_count+1, target_i, target_j) 149 | if solved_one is True: 150 | return solved_one, pair_i, pair_j 151 | if pre_direction == 3: 152 | solved_one, pair_i, pair_j = search(board, visited, i, j+1, 3, conner_count, target_i, target_j) 153 | if solved_one is True: 154 | return solved_one, pair_i, pair_j 155 | else: 156 | solved_one, pair_i, pair_j = search(board, visited, i, j+1, 3, conner_count+1, target_i, target_j) 157 | if solved_one is True: 158 | return solved_one, pair_i, pair_j 159 | visited[i][j] = False 160 | return False, -1, -1 161 | 162 | def solve_one_pair_with_grace(board): 163 | for i in range(len(board)): 164 | for j in range(len(board[0])): 165 | if board[i][j] != 0: 166 | # 开始搜索棋盘进行尝试消除 167 | visited = [[False for j in range(19)] for i in range(11)] 168 | pre_direction = -1 # -1 first point, 0 up, 1 down, 2 left, 3 right 169 | conner_count = 0 170 | solved_one, pair_i, pair_j = search(board, visited, i, j, pre_direction, conner_count, i, j) 171 | if solved_one is True: 172 | piece_num = 11*19 - sum(row.count(0) for row in board) 173 | factor = max(1.2, 3.0*math.cos((piece_num-160)/(160/(math.pi/2)))) 174 | click_piece(j, i, GRACE_GAME_SPEED_CORRECTOR * factor * random.random()) 175 | click_piece(pair_j, pair_i, GRACE_GAME_SPEED_CORRECTOR * factor * random.random()) 176 | board[i][j], board[pair_i][pair_j] = 0, 0 # 消去的方块置零 177 | return True 178 | return False 179 | 180 | 181 | def solve_game_with_grace(): 182 | board, _ = scan_game_board() 183 | check_status_ticker = 1 184 | while True: 185 | solved_one = solve_one_pair_with_grace(board) 186 | if if_all_zeros(board): 187 | print("全部方块已被消除完毕") 188 | break 189 | elif solved_one is False: 190 | for row in board: 191 | print(row) 192 | print("无解,需要重排列") 193 | resort() 194 | time.sleep(8) #需要等待提示文字消失,或者干脆让这盘输掉比较好? 195 | board, _ = scan_game_board() 196 | elif check_status_ticker % 20 == 0: 197 | status = get_status() 198 | if status != GameState.GS_INGAME: 199 | print("游戏中出现未知问题,现状态为{}".format(status)) 200 | break 201 | else: 202 | check_status_ticker += 1 203 | 204 | def debug(): 205 | solve_game_with_grace() 206 | 207 | if __name__ == '__main__': 208 | debug() --------------------------------------------------------------------------------