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
from ctypes import wintypes
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
PROCESS_VM_READ = 0x0010
PROCESS_QUERY_INFORMATION = 0x0400
def read_memory(process_id, address):
process_handle = ctypes.windll.kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, process_id)
if not process_handle:
raise Exception("Cannot open process: {}".format(process_id))
value = ctypes.c_int()
bytes_read = wintypes.SIZE()
ctypes.windll.kernel32.ReadProcessMemory(process_handle, ctypes.c_void_p(address), ctypes.byref(value), ctypes.sizeof(value), ctypes.byref(bytes_read))
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) # Actions: 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 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.keyDown(action)
time.sleep(0.1)
pyautogui.keyUp(action)
else:
pyautogui.keyDown(action)
time.sleep(0.1)
pyautogui.keyUp(action)
return action
def train(model, device, game_window, optimizer, previous_score, previous_hp):
model.train()
current_score = previous_score
current_hp = previous_hp
for i in range(10000):
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)
new_hp = read_memory(process_id, hp_address)
new_score = read_memory(process_id, score_address)
print(f"Previous HP: {previous_hp}, Current HP: {new_hp}")
print(f"Previous Score: {previous_score}, Current Score: {new_score}")
hp_diff = new_hp - previous_hp
score_diff = new_score - previous_score
reward = hp_diff + score_diff
print(f"HP Diff: {hp_diff}, Score Diff: {score_diff}, Reward: {reward}")
loss = -torch.tensor([reward], dtype=torch.float, device=device) * output[0][action_index]
loss.backward()
optimizer.step()
previous_hp = new_hp
previous_score = new_score
print(f"Loss: {loss.item()}")
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()
initial_image = get_game_image(game_window)
initial_hp = read_memory(process_id, hp_address)
initial_score = read_memory(process_id, score_address)
train(model, device, game_window, optimizer, initial_score, initial_hp)
if __name__ == "__main__":
main()