- import cv2
- import numpy as np
- import pyautogui
- import pygetwindow as gw
- import pytesseract
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- import random
- import time
- import ctypes
- import ctypes
- from ctypes import wintypes
- pytesseract.pytesseract.tesseract_cmd = r"C:Program FilesTesseract-OCRtesseract.exe"
- PROCESS_VM_READ = 0x0010
- PROCESS_QUERY_INFORMATION = 0x0400
- def read_memory(process_id, address):
- # Uzyskaj uchwyt do procesu z odpowiednimi uprawnieniami
- process_handle = ctypes.windll.kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, process_id)
- if not process_handle:
- raise Exception("Nie można otworzyć procesu: {}".format(process_id))
- # Przygotuj buffor, do którego zostanie wczytana wartość
- value = ctypes.c_int()
- bytes_read = wintypes.SIZE()
- # Przeczytaj pamięć
- ctypes.windll.kernel32.ReadProcessMemory(process_handle, ctypes.c_void_p(address), ctypes.byref(value), ctypes.sizeof(value), ctypes.byref(bytes_read))
- # Zamknij uchwyt do procesu
- ctypes.windll.kernel32.CloseHandle(process_handle)
- return value.value
- class Net(nn.Module):
- def __init__(self):
- super(Net, self).__init__()
- self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
- self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
- self.conv2_drop = nn.Dropout2d()
- self.fc1 = nn.Linear(6480, 50)
- self.fc2 = nn.Linear(50, 5) # 5 akcji: w, a, s, d, space
- def forward(self, x):
- x = F.relu(F.max_pool2d(self.conv1(x), 2))
- x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
- x = x.view(-1, 6480)
- x = F.relu(self.fc1(x))
- x = self.fc2(x)
- return F.log_softmax(x, dim=1)
- def get_game_image(game_window):
- screenshot = pyautogui.screenshot(region=(game_window.left, game_window.top, game_window.width, game_window.height))
- screenshot = np.array(screenshot)
- screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
- return cv2.resize(screenshot, (84, 84))
- def get_score_region(screenshot):
- # Assuming the score is located at the top right corner of the screen
- # We can try to give some padding to make sure we are only capturing the score
- score_height = 40
- score_width = 150
- top_padding = 10
- right_padding = 10
- y = top_padding
- x = screenshot.shape[1] - score_width - right_padding
- score_region = screenshot[y:y + score_height, x:x + score_width]
- return score_region
- def get_score(screenshot):
- score_region = get_score_region(screenshot)
- if score_region.size == 0:
- print("Score region is empty!")
- return 0
- # We already have a grayscale image so we don't need to convert it again
- thresholded = cv2.threshold(score_region, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
- score_text = pytesseract.image_to_string(thresholded, config='--psm 6')
- score_digits = ''.join(filter(str.isdigit, score_text))
- return int(score_digits) if score_digits else 0
- def perform_action(action_index, game_window):
- actions = ["w", "a", "s", "d", "space"]
- action = actions[action_index]
- print(f"Performing action: {action}")
- if action == "space":
- x = random.randint(game_window.left, game_window.left + game_window.width)
- y = random.randint(game_window.top, game_window.top + game_window.height)
- pyautogui.moveTo(x, y)
- #pyautogui.click()
- pyautogui.keyDown(action)
- time.sleep (0.1)
- pyautogui.keyUp(action)
- else:
- pyautogui.keyDown(action)
- time.sleep (0.1)
- pyautogui.keyUp(action)
- return action
- # Przykładowe użycie - ZMIEN process_id NA WŁAŚCIWY PROCES ORAZ address NA WŁAŚCIWY ADRES
- process_id = 2616 # ID procesu gry
- hp_address = 0x0437E9DC # Adres pamięci HP
- score_address = 0x040351A4 # Adres pamięci HP
- def train(model, device, game_window, optimizer, previous_score, previous_hp):
- model.train()
- current_score = previous_score
- for i in range(10000): # liczba epok treningowych
- image = get_game_image(game_window)
- image_tensor = (
- torch.from_numpy(image).float().unsqueeze(0).unsqueeze(0).to(device)
- )
- optimizer.zero_grad()
- output = model(image_tensor)
- action_index = output.max(1)[1].view(1, 1).item()
- action = perform_action(action_index, game_window)
- time.sleep(0.1) # Odczekaj, aby gra mogła zaktualizować stan
- new_image = get_game_image(game_window)
- current_hp = read_memory(process_id, hp_address)
- curret_score = read_memory(process_id, score_address)
- print(f"Aktualne HP: {current_hp}")
- # Oblicz nagrodę i stratę
- # 100 130 = -30
- hp_diff = current_hp - previous_hp
- score_diff = current_score - previous_score;
- reward = 0;
- if hp_diff >= 0:
- reward+=1;
- else:
- reward-=1;
- if score_diff > 0:
- reward +=1;
- #reward = hp_diff + score_diff;
- loss = -torch.tensor([reward], dtype=torch.float,device = device) * output[0][action_index]
- #loss = -reward * output[0][action_index]
- loss.backward()
- optimizer.step()
- previous_hp = current_hp
- previous_score = current_score
- float_number = float(loss.item())
- print(f"loss: {float_number}")
- print(f"loss: {float_number}")
- print(f"reward: {reward}")
- def main():
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- model = Net().to(device)
- optimizer = optim.Adam(model.parameters(), lr=0.001)
- game_window = gw.getWindowsWithTitle("Yet Another Zombie Defense 2")[0]
- game_window.activate()
- process_id = game_window._hWnd
- initial_image = get_game_image(game_window)
- #initial_score = get_score(initial_image)
- train(model, device, game_window, optimizer, 0, 150)
- if __name__ == "__main__":
- main()