- 개요
사과게임 시작화면의 숫자를 인식해서 numpy 배열로 저장했으니 이제 사과게임 매크로를 만듭시다.
- 숫자 합 10 확인 및 제거
사과게임에서는 마우스로 드래그한 영역의 사과들의 숫자합이 10일 때 그 사과들이 지워집니다.
우린 숫자를 numpy 배열에 저장했으니 numpy 배열에서 특정 범위의 합이 10인 경우를 찾으면 됩니다.
그 다음 이 특정 범위의 위치를 내 모니터의 좌표로 변환하여 마우스를 드래그하면 됩니다.
합이 10이되는 영역을 찾는 알고리즘은 여러가지가 있지만
저는 왼쪽 위부터 오른쪽으로 한 줄씩 합이 10인 영역을 찾겠습니다.
"""
합이 10인 위치를 제거하는 기능을 10번 시행하기 위해 _로 열번 돌립니다.
특정 영역이 0이 되면서 없어지면 합이 0이 될 수 있는 영역이 새로 생길 수 있으므로
여러 번 돌려야 합니다.
"""
for _ in range(10):
"""
num2d의 크기는 10x17로
yi, xi를 각각 for문으로 돌려 num2d의 모든 요소에 접근합니다.
"""
for yi in range(10):
for xi in range(17):
"""
w, h는 width, height를 의미합니다.
xi가 10이라면 최대값이 16이므로 마우스는 좌우폭으로 +6까지 움직이면 됩니다.
(이렇게 제한을 안하면 numpy 배열의 크기를 넘어버려서 오류가 남)
"""
w_max, h_max = 17 - xi, 10 - yi
for h in range(h_max):
for w in range(w_max):
"""
yi부터 yi+h까지, xi부터 xi+w까지의 범위의 합이 10이면
사과를 없앴다고 치고 해당 영역의 위치를 0으로 만듬
"""
if np.sum(num2d[yi: yi + h+1, xi: xi + w+1]) == 10:
num2d[yi: yi + h+1, xi: xi + w+1] = 0
numpy 배열의 합이 10인 영역을 0으로 만드는 코드를 짯고
이제 마우스를 드래그해서 사과게임 화면의 사과를 지워봅시다.
- 마우스로 숫자 합 10 확인 및 제거
numpy 배열에서 합이 10되는 위치를 찾았고, 실제 내 모니터에서 이 좌표값을 알아야 합니다.
그래야 파이썬으로 마우스를 제어해서 해당 영역 드래그를 할 수 있습니다.
import pyautogui
import keyboard
"""
xloc, yloc의 평균 간격으로 사과 간 좌표의 간격을 구합니다.
dx는 x방향의 사과좌표 간격
dy는 y방향의 사과좌표 간격
"""
xloc = df['left'].unique()
yloc = df['top'].unique()
dx = np.mean(np.diff(xloc))
dy = np.mean(np.diff(yloc))
"""
좌표를 가져오는 이미지는 사과 안의 숫자 이미지입니다.
그래서 이 좌표의 영역으로 마우스 드래그를 하면
드래그 영역에 사과가 완전히 속하지 않습니다.
그래서 사과가 드래그 영역에 들어올 수 있도록
-0.25 * dy, -0.25 * dy만큼 좌표 이동을 해야합니다.
"""
xloc_mouse = xloc - 0.25 * dx
yloc_mouse = yloc - 0.25 * dy
def mouse_move(xi, yi, w, h, duration):
# 드래그 영역의 왼쪽, 위 좌표로 마우스 이동
pyautogui.moveTo( xloc_mouse[xi], yloc_mouse[yi], duration=duration)
# 마우스 누르는 상태
pyautogui.mouseDown()
# 드래그 영역의 오른쪽, 아래 좌표로 마우스 이동
pyautogui.moveTo(xloc_mouse[xi] + (w+1)*dx, yloc_mouse[yi] + (h+1)*dy, duration=duration)
# 위의 좌표 이동과 똑같지만 이렇게 반복을 안하면 드래그가 제대로 안됩니다.
# 어떻게 되는지 궁금하면 지우고 테스트해보세요.
pyautogui.moveTo(xloc_mouse[xi] + (w+1)*dx, yloc_mouse[yi] + (h+1)*dy, duration=duration)
# 마우스 누르는 상태 해제
pyautogui.mouseUp()
duration = 0.01
for _ in range(10):
for yi in range(10):
for xi in range(17):
# q를 꾹 누르고 있으면 취소되게 만듬
if keyboard.is_pressed("q"):
break
w_max, h_max = 17 - xi, 10 - yi
for h in range(h_max):
for w in range(w_max):
if np.sum(num2d[yi: yi + h+1, xi: xi + w+1]) == 10:
num2d[yi: yi + h+1, xi: xi + w+1] = 0
mouse_move(xi, yi, w, h, duration)
코드의 로직은 간단합니다.
아까 numpy 배열에서 합이 10될 때 xi, yi, w, h를 mouse_move() 함수에 넣습니다.
duration은 단어 의미 그대로 마우스 행동의 지속시간을 설정합니다.
- 실행하기
저번 코드도 같이 첨부합니다.
사과게임을 시작하고 이 코드를 실행하면 매크로가 동작합니다.
q를 꾹 누르고 있으면 꺼집니다.
#전체코드
import pyautogui
import pandas as pd
df = pd.DataFrame(columns=['num', 'top', 'left', 'width', 'height'])
img_path = './imgs/'
ll = 0
for i in range(1, 9+1):
positions = pyautogui.locateAllOnScreen(f'{img_path}{i}.png', confidence=0.90)
for pos in positions:
new_row = {'num':i, 'top':pos.top, 'left':pos.left,
'width':pos.width, 'height':pos.height}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
ll += 1
import os
os.environ["LOKY_MAX_CPU_COUNT"] = "4"
os.environ["OMP_NUM_THREADS"] = "2"
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=170)
kmeans.fit(df[['top', 'left']])
df['cluster'] = kmeans.labels_
df = df.groupby(by='cluster').mean()
kmeans = KMeans(n_clusters=17)
kmeans.fit(df[['left']])
df['x_cls'] = kmeans.labels_
kmeans = KMeans(n_clusters=10)
kmeans.fit(df[['top']])
df['y_cls'] = kmeans.labels_
for xi in range(17):
mean_value = df.loc[df['x_cls'] == xi, 'left'].mean() # 먼저 평균값 계산
df.loc[df['x_cls'] == xi, 'left'] = mean_value # loc를 사용하여 값 업데이트
for xi in range(10):
mean_value = df.loc[df['y_cls'] == xi, 'top'].mean() # 먼저 평균값 계산
df.loc[df['y_cls'] == xi, 'top'] = mean_value # loc를 사용하여 값 업데이트
import numpy as np
df = df.sort_values(by=['top', 'left']).reset_index(drop=True)
num1d = np.array(df['num'].values.tolist(), dtype=int)
num2d = num1d.reshape(10, 17)
import pyautogui
import keyboard
xloc = df['left'].unique()
yloc = df['top'].unique()
dx = np.mean(np.diff(xloc))
dy = np.mean(np.diff(yloc))
xloc_mouse = xloc - 0.25 * dx
yloc_mouse = yloc - 0.25 * dy
def mouse_move(xi, yi, w, h, duration):
pyautogui.moveTo( xloc_mouse[xi], yloc_mouse[yi], duration=duration)
pyautogui.mouseDown()
pyautogui.moveTo(xloc_mouse[xi] + (w+1)*dx, yloc_mouse[yi] + (h+1)*dy, duration=duration)
pyautogui.moveTo(xloc_mouse[xi] + (w+1)*dx, yloc_mouse[yi] + (h+1)*dy, duration=duration)
pyautogui.mouseUp()
duration = 0.01
for _ in range(10):
for yi in range(10):
for xi in range(17):
if keyboard.is_pressed("q"):
break
w_max, h_max = 17 - xi, 10 - yi
for h in range(h_max):
for w in range(w_max):
if np.sum(num2d[yi: yi + h+1, xi: xi + w+1]) == 10:
num2d[yi: yi + h+1, xi: xi + w+1] = 0
mouse_move(xi, yi, w, h, duration)
'프로젝트 > 사과게임 매크로 만들기' 카테고리의 다른 글
[python][사과게임 매크로 만들기] 3. CLI 기반 사과게임 매크로 코드 작성 (0) | 2025.04.11 |
---|---|
[python][사과게임 매크로 만들기] 1. 이미지 인식, numpy 배열로 변환 (0) | 2025.03.31 |