本帖最后由 PJlay 于 2026-4-27 11:24 编辑
0. 一切从“被小学生羞辱”开始
朋友给我推荐了一个叫「舒尔特方格」的专注力训练小游戏。规则很简单:屏幕上出现一堆乱序数字(比如 1 到 25),你要按顺序从 1 点到 25,谁用时短谁就赢了。
我第一次玩,花了 38 秒。
不服,再来,32 秒。
再来,29 秒……这他喵的不就是考验眼疾手快么?可我偏偏是个手残——鼠标经常点歪,眼睛找到了 8,手却戳到了 9,然后心态炸裂。就在我准备卸载软件时,一个邪恶的念头冒了出来:我写代码的,为什么要跟一般人拼手速?于是,我打开了 Python 的大门,准备给这个游戏安排一台“物理外挂”。
1.如何让电脑自己“看见”和“点击”
思路很直白:
- 找到游戏窗口;
- 找出所有数字格子;
- 读每个格子的数字和它在屏幕上的位置;
- 按 1,2,3… 的顺序,依次模拟鼠标点上去。
第一个问题:程序怎么知道格子在哪?数字是多少?
这时候就要请出老朋友——uiautomation(Windows UI 自动化库)。它能像“上帝之眼”一样,扫描窗口里的每一个控件。只要写个递归,把所有 AutomationId == "item" 的控件揪出来,再找到它里面的 TextControl,数字就到手了。
第二个问题:怎么模拟真正的鼠标点击?
selenium 那种 element.click() 对桌面程序没用。更稳妥的方式是调用 Windows API:先 SetCursorPos 挪鼠标,再 mouse_event 发送左键按下和抬起信号。我用 ctypes 调了 user32.dll,两行代码搞定。
2. 完整代码(已脱敏,很简单,可直接对着游戏用)
[Python] 纯文本查看 复制代码 import uiautomation as auto
import ctypes
import time
user32 = ctypes.windll.user32
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
def click_at(x: int, y: int):
"""在屏幕坐标 (x, y) 处模拟一次鼠标左键点击"""
user32.SetCursorPos(x, y)
user32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
user32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
def main():
# 1. 定位窗口
window = auto.PaneControl(Name="舒尔特方格练习")
if not window.Exists(maxSearchSeconds=5):
print("未找到窗口,请确认游戏已打开并进入格子界面")
return
try:
window.SetFocus()
time.sleep(0.3)
except Exception:
pass
# 2. 递归收集所有 item 控件(AutomationId == "item")
items = []
def find_items(ctrl):
if ctrl.AutomationId == "item" and ctrl.ControlType == auto.ControlType.GroupControl:
items.append(ctrl)
for child in ctrl.GetChildren():
find_items(child)
find_items(window)
if not items:
print("未找到任何数字格子(item)")
return
# 3. 提取每个 item 的数字和屏幕中心坐标
num_to_coord = {}
for item in items:
# 获取 item 内部的 TextControl,其 Name 就是数字
text_ctrl = item.TextControl(foundIndex=1)
if not text_ctrl.Exists():
continue
num_str = text_ctrl.Name.strip()
if not num_str.isdigit():
continue
num = int(num_str)
# 获取 item 的矩形并计算中心点
rect = item.BoundingRectangle
if rect.width() <= 0 or rect.height() <= 0:
continue
cx = rect.left + rect.width() // 2
cy = rect.top + rect.height() // 2
num_to_coord[num] = (cx, cy)
if not num_to_coord:
print("未能提取到任何有效数字")
return
# 自动检测方格尺寸并排序
total = len(num_to_coord)
print(f"当前舒尔特方格尺寸:{int(total ** 0.5)}×{int(total ** 0.5)} (共 {total} 格)")
sorted_nums = sorted(num_to_coord.keys())
# 4. 按数字升序点击
for num in sorted_nums:
x, y = num_to_coord[num]
print(f"点击 {num} ({x}, {y})")
click_at(x, y)
# 点击延迟,自行调整
time.sleep(0.3)
print("全部点击完成!")
if __name__ == "__main__":
main()
3. 请看成果:
本文及所附代码仅供技术交流与娱乐参考,切勿用于任何破坏游戏公平性、违反游戏规则或进行恶意竞争的场景!!!
请尊重原游戏的设计初衷,舒尔特方格本身是锻炼专注力和反应能力的优秀工具。代码的存在意义在于学习自动化技术和解决问题的思路,而不是替代人类应有的挑战精神。
如果你真用这个脚本去跟朋友比成绩……那我只能说:你开心就好,但别说是从我这儿学的。 |