import cv2 import time import datetime import numpy as np import io from PIL import Image from io import BytesIO from ppadb.client import Client as AdbClient from PIL import Image client = AdbClient(host="127.0.0.1", port=5037) device = client.device("192.168.178.32:5555") # template positions close_sub_fights = cv2.imread('templates/close_sub_fights.jpg') close_fight = cv2.imread('templates/close_fight.jpg') fight_button = cv2.imread('templates/i.jpg') end_of_log = cv2.imread('templates/end_of_log.jpg') end_of_log2 = cv2.imread('templates/end_of_log2.jpg') # cursor positions fight_scroll_top = "2200 626" fight_scroll_bottom = "2200 1500" titan_fight = "1400 860" damage_taken = "450 850" close_details = "2175 450" close_titan_fight = "2650 570" defense_log = "350 870" def non_max_suppression(boxes, overlapThresh): if len(boxes) == 0: return [] # Convert to float boxes = np.array(boxes, dtype="float") # Initialize the list of picked indexes pick = [] # Grab the coordinates of the bounding boxes x1 = boxes[:,0] y1 = boxes[:,1] x2 = boxes[:,2] y2 = boxes[:,3] # Compute the area of the bounding boxes and sort by bottom-right y-coordinate area = (x2 - x1 + 1) * (y2 - y1 + 1) idxs = np.argsort(y2) # Keep looping while some indexes still remain in the indexes list while len(idxs) > 0: # Grab the last index in the indexes list and add the index value to the list of picked indexes last = len(idxs) - 1 i = idxs[last] pick.append(i) # Find the largest (x, y) coordinates for the start of the bounding box and the smallest (x, y) # coordinates for the end of the bounding box xx1 = np.maximum(x1[i], x1[idxs[:last]]) yy1 = np.maximum(y1[i], y1[idxs[:last]]) xx2 = np.minimum(x2[i], x2[idxs[:last]]) yy2 = np.minimum(y2[i], y2[idxs[:last]]) # Compute the width and height of the bounding box w = np.maximum(0, xx2 - xx1 + 1) h = np.maximum(0, yy2 - yy1 + 1) # Compute the ratio of overlap overlap = (w * h) / area[idxs[:last]] # Delete all indexes from the index list that have overlap greater than the threshold idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0]))) # Return only the bounding boxes that were picked return boxes[pick].astype("int") def screen_has_changed(prev_screenshot, threshold=0.01): # Take a new screenshot current_screenshot = device.screencap() # Convert to NumPy arrays prev_img = np.frombuffer(prev_screenshot, dtype=np.uint8) current_img = np.frombuffer(current_screenshot, dtype=np.uint8) # Load images prev_img = cv2.imdecode(prev_img, cv2.IMREAD_COLOR) current_img = cv2.imdecode(current_img, cv2.IMREAD_COLOR) # Calculate absolute difference diff = cv2.absdiff(prev_img, current_img) non_zero_count = np.count_nonzero(diff) # print(f"diff: {non_zero_count} > {threshold * diff.size} = {non_zero_count > threshold * diff.size}") # Check if the difference is greater than the threshold return non_zero_count > threshold * diff.size def save_screenshot(): # Take a screenshot result = device.screencap() timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") image = Image.open(io.BytesIO(result)) jpeg_filename = f"/mnt/t/nextcloud/InstantUpload/Herowars/{timestamp}.jpg" image = image.convert('RGB') # Convert to RGB mode for JPEG with open(jpeg_filename, "wb") as fp: image.save(fp, format='JPEG', quality=85) # Adjust quality as needed time.sleep(0.5) def wait_for_screen_change(): # Usage example prev_screenshot = device.screencap() while not screen_has_changed(prev_screenshot): time.sleep(0.1) # Polling interval def tap(location): device.shell(f"input tap {location}") time.sleep(1) def swipe(start, end): device.shell(f"input swipe {start} {end} 1000") time.sleep(0.5) def tap_button(template): button = find_templates(template) if len(button) == 0: return tap(f"{button[0][0]} {button[0][1]}") def is_end_of_log(): templates = find_templates(end_of_log) if (len(templates) == 0): templates = find_templates(end_of_log2) result = len(templates) > 0 if result: print("reached end of guild war log!") return result def find_templates(template_image): screenshot = device.screencap() target_image = Image.open(BytesIO(screenshot)) # Convert the image to a NumPy array and then to BGR format (which OpenCV uses) target_image = np.array(target_image) target_image = cv2.cvtColor(target_image, cv2.COLOR_RGB2BGR) w, h = template_image.shape[:-1] # Template matching result = cv2.matchTemplate(target_image, template_image, cv2.TM_CCOEFF_NORMED) # Define a threshold threshold = 0.9 # Adjust this threshold based on your requirements # Finding all locations where match exceeds threshold locations = np.where(result >= threshold) locations = list(zip(*locations[::-1])) # Create list of rectangles rectangles = [(*loc, loc[0] + w, loc[1] + h) for loc in locations] # Apply non-maximum suppression to remove overlaps rectangles = non_max_suppression(rectangles, 0.3) # Initialize an empty list to store coordinates coordinates = [] for (startX, startY, endX, endY) in rectangles: # Calculate the center coordinates centerX = round(startX + (endX - startX) / 2) centerY = round(startY + (endY - startY) / 2) # Append the coordinate pair to the list coordinates.append((centerX, centerY)) # Sort the coordinates by y value in ascending order return sorted(coordinates, key=lambda x: x[1]) def find_max_y_pair(coordinates): # find the coordinate pair with the maximum y value result = max(coordinates, key=lambda x: x[1]) return f"{result[0]} {result[1]}" def take_fight_screenshots(): save_screenshot() # # if you desperately need submits # tap(damage_taken) # save_screenshot() tap_button(close_fight) time.sleep(1) def process_war_log(): buttons = find_templates(fight_button) if len(buttons) == 0: swipe(fight_scroll_bottom, fight_scroll_top) return # process all found buttons for pair in buttons: tap(f"{pair[0]} {pair[1]}") sub_buttons = find_templates(fight_button) if (len(sub_buttons) == 0): take_fight_screenshots() else: for pair2 in sub_buttons: tap(f"{pair2[0]} {pair2[1]}") take_fight_screenshots() tap_button(close_sub_fights) swipe(find_max_y_pair(buttons), fight_scroll_top) # start while not is_end_of_log(): process_war_log(); # possible duplicates here, but necessary to be sure to get the last fights process_war_log();