- 개요
사과게임 매크로를 만들어봅시다.
아래 유투브 쇼츠는 제가 가장 처음에 만든 사과게임 매크로입니다.
지금은 코드 리팩토링을 해서 코드 로직이 좀 다르긴 합니다만 아무튼 사과게임 매크로를 만들어 봅시다.
가장 먼저 할 일은 사과게임을 시작한 뒤 모니터 화면을 캡쳐해서 어느 위치에 어떤 숫자가 있는지에 대한 정보를 저장하는 것입니다.
- 사과 안의 숫자 인식
1. 사과게임 화면 저장
사과게임을 실행한 뒤 화면을 스크린 캡쳐해서 png파일로 저장합시다.
제가 저장한 사과게임 화면은 아래와 같습니다.
2. 숫자 인식
파이썬 코드의 숫자 인식 과정에서는 첫째로 모니터 화면을 스크린샷으로 저장한 다음 1부터 9까지의 그림(imgs.zip 다운로드, reset.png, start.png은 나중에 쓰임)이 스크린샷의 어디에 위치하는지 x, y 좌표로 저장합니다.
참고로 모니터의 가장 왼쪽 위의 좌표가 (0, 0)이고 오른쪽으로 갈수록 x가 증가, 아래로 갈수록 y가 증가합니다.
수학시간에 y축의 방향이 위쪽이라고 배우지만 모니터에서 y축 방향은 아래쪽입니다.
pyautogui 라이브러리의 locateAllOnscreen() 메서드를 사용하면 화면 스크린샷에서 특정 png 파일과 유사한 그림이 어디에 위치하는지 알아낼 수 있습니다.
import pyautogui
import pandas as pd
# 여러 계산시 pd.DataFrame 형식을 사용할 예정
df = pd.DataFrame(columns=['num', 'top', 'left', 'width', 'height'])
img_path = './imgs/'
ll = 0
for i in range(1, 9+1):
"""
1.png ~ 9.png까지 for문을 돌리며
숫자 1부터 9까지의 위치를 df에 저장합니다.
"""
positions = pyautogui.locateAllOnScreen(f'{img_path}{i}.png', confidence=0.90)
for pos in positions:
new_row = {'num':i, 'left':pos.left, 'right':pos.top,
'width':pos.width, 'height':pos.height}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
ll += 1
- 중복숫자 처리
사과게임의 숫자배열은 10행 17열로 인식한 숫자의 갯수가 170개가 되어야 합니다.
하지만 print(df)로 확인해보니 214개가 저장되었습니다.
matplotlib 라이브러리로 인식된 위치를 살펴봅시다.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(17, 10))
# 'left'는 x좌표, 'top은 y좌표
plt.scatter(df['left'], df['top'])
그림을 보니 10행, 17열은 맞습니다만 같은 위치에 점 2개가 있습니다.
confidence 값이 엄청 높지는 않아 1픽셀 정도 차이나도 '1.png'로 인식될 수 있어 비슷한 위치에 좌표가 잡힙니다.
confidence값이 너무 높으면 아예 누락이 되므로 confidence를 적당히 낮게 잡은 뒤 중복을 제거하는 방식이 안정적입니다.
중복숫자를 처리하는 여러 방법이 있습니다.
저는 k-means clustering 방법을 쓰겠습니다.
이 방법에서는 거리가 가까운 점을 하나의 클러스터로 묶는 작업을 반복수행합니다.
처음에는 214개였으니 한 번 작업을 시행하면 가장 가까운 2개 점은 사실상 하나의 점으로 간주하게 되어 213개의 점이 됩니다(정확한 설명 아님).
그럼 언제까지 반복하느냐? 제가 정해준 숫자까지 반복합니다.
전 이미 숫자는 170개여야 한다는 것을 아니까 170개가 될 때까지 이를 반복하면 됩니다.
그럼 214개의 점이 170개의 클러스터로 분류됩니다.
같은 클러스터에 속하는 점들은 서로 근처에 찍혀있고, 이는 중복이니 제거하거나, 평균을 내어 170개의 점으로 만들어주면 됩니다.
"""
윈도우 11에서 아래의 환경변수 설정을 해줘야
클러스터링시 오류가 안남
KMeans보다 먼저 선언해야함, 늦게 선언해도 오류남
"""
import os
os.environ["LOKY_MAX_CPU_COUNT"] = "4"
os.environ["OMP_NUM_THREADS"] = "2"
"""
n_clusters=170 >>> 170개가 될 때까지 반복하겠다.
"""
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()
- numpy로 변환
1. x좌표(left), y좌표(top) 정리
170개로 만든 df를 바로 numpy로 저장했으면 좋겠으나 그렇게 간단하지 않습니다.
이상적으로는 x좌표가 17개, y좌표가 10개로 고정된 숫자면 x좌표, y좌표가 작은 순부터 numpy에 저장한 뒤 reshape으로 10x17 배열로 만들면 되겠습니다.
하지만 위에서 scatter plot을 보면 같은 행이어도 y좌표가 조금씩 다릅니다.
머릿 속에 여러가지 방법이 떠오르지만 아까 클러스터링 방법을 썼으니 클러스터링 방법을 써봅시다.
"""
left는 x좌표로 17개의 클러스터가 될 때까지
클러스터링하면 17개 열의 x좌표 위치가 반환됨
top의 경우 y좌표 10개, 10개의 행에 대한 y좌표 위치 반환
"""
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_
"""
같은행이면 y좌표가 같아야 하고,
같은열이면 x좌표가 같아야함
평균값으로 넣어줌
"""
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를 사용하여 값 업데이트
이제 다시 scatter.plot()을 그려봅시다.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(17, 10))
ax = fig.add_subplot()
ax.scatter(df['left'], df['top'])
xloc = df['left'].unique()
for x in xloc:
ax.axvline(x=x, color='black')
yloc = df['top'].unique()
for y in yloc:
ax.axhline(y=y, color='black')
처음의 scatter plot에서는 점이 중복되어 있고 삐뚤빼뚤했지만 이젠 10행 17열로 예쁘게 정렬되었습니다.
df를 'top', 'left' 순서로 오름 차순으로 정리한 뒤 reshape을 하여 numpy 배열로 저장합시다.
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)
3. 결과 비교
왼쪽은 사과게임 화면캡처, 오른쪽은 print(num2d)의 결과입니다.
숫자의 배치가 완전히 똑같습니다.
이번 포스트에서는 화면의 숫자를 인식해서 numpy 배열로 저장했습니다.
다음에는 numpy 배열을 이용해서 특정 영역의 합이 10이 되면 마우스 드래그로 사과를 없애는 기능을 구현하겠습니다.
'프로젝트 > 사과게임 매크로 만들기' 카테고리의 다른 글
[python][사과게임 매크로 만들기] 3. CLI 기반 사과게임 매크로 코드 작성 (0) | 2025.04.11 |
---|---|
[python][사과게임 매크로 만들기] 2. 사과게임 매크로 코드 작성, 실행 (0) | 2025.04.05 |