70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Screenshot the game window, find a cafe affection sparkle, click it.
|
|
|
|
Runs entirely on nik-gpu (screenshot -> detect -> click all local) so the
|
|
whole cycle finishes in well under a second - roaming students move fast
|
|
enough that a multi-hop SSH/scp pipeline misses the click.
|
|
|
|
Prints "MATCH x y score" and exits 0 if a sparkle was found and clicked,
|
|
or prints "NO_MATCH" and exits 1 otherwise.
|
|
"""
|
|
import subprocess
|
|
import sys
|
|
|
|
import cv2
|
|
import numpy as np
|
|
|
|
TEMPLATE_PATH = "/home/nik/ba_assets/cafe_sparkle.png"
|
|
SHOT_PATH = "/tmp/ba_live.png"
|
|
OFFSET_X = 75
|
|
OFFSET_Y = 47
|
|
THRESHOLD = 0.97
|
|
|
|
ENV = {"DISPLAY": ":0", "XAUTHORITY": "/run/user/1000/gdm/Xauthority"}
|
|
|
|
|
|
def screenshot():
|
|
subprocess.run(
|
|
["scrot", "-a", "0,0,1920,1200", "-o", SHOT_PATH],
|
|
env=ENV, check=True,
|
|
)
|
|
|
|
|
|
def find_sparkles():
|
|
template = cv2.imread(TEMPLATE_PATH)
|
|
b, g, r = cv2.split(template.astype(np.int16))
|
|
yellow_white = ((r > 180) & (g > 140) & (r - b > 60)) | ((r > 200) & (g > 200) & (b > 200))
|
|
mask_plane = (yellow_white.astype(np.uint8)) * 255
|
|
mask = cv2.merge([mask_plane, mask_plane, mask_plane])
|
|
th, tw = template.shape[:2]
|
|
|
|
img = cv2.imread(SHOT_PATH)
|
|
result = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED, mask=mask)
|
|
locs = np.where(result >= THRESHOLD)
|
|
points = sorted(zip(*locs[::-1]), key=lambda p: -result[p[1], p[0]])
|
|
|
|
merged = []
|
|
for x, y in points:
|
|
if all(abs(x - mx) > tw // 2 or abs(y - my) > th // 2 for mx, my, _ in merged):
|
|
merged.append((x, y, result[y, x]))
|
|
return [(x + tw // 2, y + th // 2, score) for x, y, score in merged]
|
|
|
|
|
|
def click(x, y):
|
|
subprocess.run(
|
|
["xdotool", "mousemove", str(x), str(y), "click", "1"],
|
|
env=ENV, check=True,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
screenshot()
|
|
sparkles = find_sparkles()
|
|
if not sparkles:
|
|
print("NO_MATCH")
|
|
sys.exit(1)
|
|
x, y, score = sparkles[0]
|
|
click(x + OFFSET_X, y + OFFSET_Y)
|
|
print(f"MATCH {x} {y} {score:.4f}")
|
|
sys.exit(0)
|